Názory k článku Vykreslování spritů a animací na ZX Spectru (2. část)

  • Článek je starý, nové názory již nelze přidávat.
  • 15. 8. 2023 9:41

    atarist

    na to reseni attribute clash jsem tedy zvedavy, protoze to ve vsech pripadech udelat IMHO nejde. Mozna pro peclive pripraveny pixel art.

  • 15. 8. 2023 14:24

    atarist

    me jako ataristovi uz to zacina pripadat docela hardcore (ale to bylo uz povidani o priznacich - proste je Z80 slozitej, IMHO zbytecne) ale pekny pekny,.

    mimochodem ty atributy - to je asi duvod, proc je valna cast Speccy her oproti jednotnemu pozadi ze?

    15. 8. 2023, 14:24 editováno autorem komentáře

  • 15. 8. 2023 14:22

    dj-bobr

    Tahle série článků mě prostě baví, už jen proto, že mi připomíná moje asm pokusy na Spectru v době mého útlého mládí.
    Jinak attribute clash měla vtipně řešená hra Three Weeks in Paradise - zmáčknutím trojky jste přepínali, zda má postavička převzít atributy pozadí, nebo zda má postavička být žlutá a přebarvovat pozadí.

  • 24. 8. 2023 17:39

    atarist

    Hele klidne a v klidu (to neni nic osobniho). Na Atarku jsou bitmapove rezimy ciste "linearni", coz je super a kazdej to hned chape a i zacatecnik muze zacit zkouset assembler a dostat neco rozumneho (na druhou stranu sirka 160 nebo 320 pixelu je opruz oproti 256, o tom zadna - i to ale Antic umi)..

    Speccy s atributama, to by jeste taky nebyl problem (ostatne komous to ma taky trosku podobne), ale to rozdeleni obrazovky na tretiny a jeste interlace po osmi radcich proste vsechny vykreslovaci algoritmy dost zabije.

    Technicky chapu, proc to chteli takto (atributy maji vysku osm scanlines), ale zase takovej problem by nebyl (IMHO!) to udelat linearne z pohledu CPU - poprehazovat par adresovych radku :)

    [sprity a tak po Speccym nechci, to je proste jinej stroj, elegantni svym minimalismem a je to tak dobre]

    Potom Speccy dost trpi tim, ze nemelo od zacatku joystickovej port. Jako je to vlastne uplna kravina, ale takto to "umoznilo" firmam si udelat vlastni nekompatibilni reseni a od te doby se to tahne. Kazda hra musi mit vyber joysticku, podpora pro dva joysticky docela vazne atd.

  • 25. 8. 2023 12:45

    Korporátní lopata

    Co se týče organizace video paměti, to je daný dvěma věcmi. O přístup do nižších 16K RAM se dělí ULA a Z80. Aby nebyl procesor moc zpomalován, načítá se vždycky jeden pár bajtů, osmice pixelů a attributy pomocí page modu. Místo aby ULA dala na sběrnici adresu řady, poslala RAS, pak dala na sběrnici adresu sloupku, poslala CAS, což je základní postup jak dostat jeden bajt z paměti, pošle na sběrnici adresu řady, pošle RAS a pak dvakrát za sebou pošle adresu sloupce a CAS. Je to o něco rychlejší, ale ty dva bajty musí být z hlediska organizace paměti ve stejné řadě. Šlo by to udělat lineárně pomocí standardního čtení paměti, ale to by chtělo rychlejší a tedy dražší paměti.
    Druhej problém je, že ULA není klasickej ASIC jako POKEY, ANTIC, VIC, SID ale polotovar, je tam nějaká matrice X*Y předpřipravených buněk a v každé je tranzistor a pár odporů, přes to pak dodá jen jedna vrstva spojů a upeče se čip. Výhoda je, že vývoj čipu je mnohem rychlejší ale je to o něco méně flexibilní a musí se ta logika vejít do standartních velikostí dodávaných výrobcem. Takže se hodně šetřilo na počtu logických prvků, aby se to vůbec vešlo do té matrice. Pro představu, zákaznický čip, podle katalogu Synerteku, trvalo vyvinout asi 9 až 12 měsíců, od začátku do nájezdu na produkční kvantity. ULA Spectra byla vyvinuta asi za třetinu až polovinu času.
    S tím portem pro joystick souhlasím, ale takových bot má Spectrum víc, třeba microdrive místo standardního pro disku.

    25. 8. 2023, 12:46 editováno autorem komentáře

  • 25. 8. 2023 13:04

    Korporátní lopata

    Jinak celá Sinclairova filozofie byla, od kalkulaček po počítače, že vzal něco, co si mohla dovolit střední třída, vymyslel cenovku akceptovatelnou pro nižší třídu a pak se teprve koukal, jak to udělat levně.
    Třebas ty kalkulačky od Sinclairu jsou taky dost zajímavé bazmeky, plné cost-cut řešení.

  • 25. 8. 2023 15:34

    TencosledujeLupu

    Tak MD je poplatné Clivově strategii. Cheap but not by firstlook. Postavte vedle gumáka takovou 5 1/4 mechaniku a z druhé strany MD mechaniku :-)

  • 15. 8. 2023 16:59

    _dw

    Pred vice jak 6 lety jsem napsal demo EOB pro ZX 48kb a tam jsem resil atributy zvlast komplikovanym zpusobem, protoze je to uplne jiny zanr her (krokovy dungeon) a nemusim prekreslovat obrazovku x-krat za vterinu. Scena se vykresluje "malirskou" metodou. Postupne se vykresluji objekty od nevzdalenejsiho.

    Kazdy sprite obsahuje hlavicku o sirce a vyssce v jednotkach znaku. Sprite se orezava, aby se nekreslilo mimo. Sprite jde i zrcadlit a usetrit si tak misto protoze temer polovina spritu sten je identickych.

    ;typedef struct
    ;{252
    ;    unsigned char  Offset na PIXel data;
    ;    unsigned char  Pocet_sloupcu_spritu;
    ;    unsigned char  Pocet_radku_spritu;
    ;} ZXHeader;

    Kazdy znak se vykresluje samostatne a obsahuje vlastni atribut za nim je 0 nebo 8 nebo 16 bajtu dat.

    Pokud atribut ma specialni hodnotu tak se to vykresluje odlisne.

    Napriklad pokud je atribut 0x40 (jen BRIGHTNESS nastaveno na 1) tak je znak celopruhledny a nejsou tak ani ulozeny jeho data krome toho atributu.

    Pokud ma atribut PAPER nastaven na nulu tak ma ten znak pruhledny PAPER, ale obsahuje nejaka data v INK. PAPER se prebira z puvodniho znaku co tam uz je nakreslen.

    Pokud ma atribut nastaven FLASH (ten je smazan z vysledku) tak to znamena ze ma znak v sobe i data s maskou (1 v masce znaci pruhlednost = zachova puvodni hodnotu (pouzije se AND)). Nova data se nakresli pak pres OR.

    Posledni dva body jdou kombinovat.

    Vykreslovaci rutina je tady:
    https://github.com/DW0RKiN/3D-Dungeon/blob/master/Source/sprite2buffer.asm

    C zdrojak pro prevod bmp spritu (se specialni barvou na pruhlednost) na data co se ukladaji do asm jsem bohuzel ztratil.

    Vysledek muze vypadat nejak takto: https://raw.githubusercontent.com/DW0RKiN/3D-Dungeon/master/screen.png

  • 15. 8. 2023 17:23

    atarist

    ty sprite se tedy posouvaji po "znacich" nebo tam je fine scrolling? (nebo jak se tomu rika na Spectru, proste posun po pixelech)

  • 15. 8. 2023 17:47

    _dw

    Cela scena se prekresluje. Je to "3D" pohled. Bud se tam neco zmeni, pohyb nepratel a nebo se pohnes ty (posun na dalsi ctverec nebo otoceni o 90 stupnu). Sprite maji tak vicemene pevnou pozici kam se kresli (nebo spis 2 pevne pozice). Zmeny muzou byt napriklad u vzdalenejsich sten, ze jsou jinde od stredni vertikaly pohledu. Nebo nejakych drobnosti jako pak, nebo dekoraci.

    Spust si radsi demo a pochopis: https://github.com/DW0RKiN/3D-Dungeon/blob/master/dungeon_v7.z80

    Kempston joystick:

    up move
    down move
    left move
    right move

    up+left turning
    up+right turning

    down+left inventory
    down+right inventory

    fire action

    fire+up first hand
    fire+down second hand
    fire+left another person
    fire+right another person

    15. 8. 2023, 17:47 editováno autorem komentáře

  • 15. 8. 2023 19:53

    _dw

    Ted me doslo, ze jsem otazku spatne pochopil a asi spravna odpoved je, ze ty sprity lze umistit (presouvat) po znacich (nasobcich 8 pixelu).

    Ve skutecnosti je to trochu slozitejsi, protoze se muze kreslit jak do screenu tak do bufferu, a jeste to je bud uvnitr okna s orezanim a nebo vne.

    Scena se kresli v bufferu a pak se to kopiruje na obrazovku tak aby se to nestretlo s "paprskem".

  • 15. 8. 2023 20:16

    atarist

    na to jsem se ptal, ale potom jsem si uvedomil, ze to asi neni v krokovacim dungeonu az tak potreba. Prisery jsou (predpokladam) vzdycky dve vedle sebe a kdyztak se posunou o celych osm pixelu stetjne jako v tom dungeon masterovi (EOB zase tak dobre neznam, ale DM jo). V DM to byly ukroky o cely ctverec a nebo kdyz jedna z priser zdechla, tak se zbytek posunul o pul ctverce do stredu.

    akorat tedy nechapu, jak jsi to cely i s animacema priser a back bufferem narval do 48kB :)

  • 18. 8. 2023 6:17

    _dw

    Abych nebyl uplne mimo tema tak jsem udelal relativne rychlou rutinu pro vykresleni spritu s maskou a ohlidanim orezani vpravo a dole.
    Orezani shora jde provest pouhou spravne zvolenenou hodnotou v DE (protoze calc_sprite_address rutina neumi spocitat zaporne souradnice).
    Orezani zleva je vetsi problem ale staci proste zmensit velikost spritu a nastavit zacatek spritu na spravnou posunutou hodnotu. Pokud je to vlevo tak ze se nic nezobrazi tak je to problem, musel by se trosku zmenit kod a rutina umet osetrit i zapornou velikost sirky spritu.

    Sprity mohou mit v podstate neomezenou velikost (255*255)
    Sprity jsou ulozeny a vykreslovany postupne zhora dolu po sloupcich. Sloupce jsou vykresleny zleva doprava.

    Na atributy jsem se vykaslal, ale taky by to slo provest.

    SCREEN_ADR      equ $4000
    ENTRY_POINT     equ $8000
    
            org ENTRY_POINT
    
    start:
    
            ld   hl, alien8                 ; adresa zdrojového bloku
            ld   de, 0x4000                 ; adresa cílového bloku
            ld   bc, 32*(192+24)            ; velikost přenášených dat
            ldir                            ; provést blokový přenos
    
            ld bc, 0x0001                   ; x-ová souřadnice a y-ová souřadnice
            call calc_sprite_address        ; výpočet adresy spritu
            call draw_my_sprite
    
            ld bc, 0x0505                   ; x-ová souřadnice a y-ová souřadnice
            call calc_sprite_address        ; výpočet adresy spritu
            call draw_my_sprite
    
    
            ld bc, 0x1E06                   ; x-ová souřadnice a y-ová souřadnice
            call calc_sprite_address        ; výpočet adresy spritu
            call draw_my_sprite
    
    
            ld bc, 0x100E                   ; x-ová souřadnice a y-ová souřadnice
            call calc_sprite_address        ; výpočet adresy spritu
            call draw_my_sprite
    
            ld bc, 0x1F16                   ; x-ová souřadnice a y-ová souřadnice
            call calc_sprite_address        ; výpočet adresy
            call draw_my_sprite
    
    
            ld bc, 0*256+14                 ; 3:10          x-ová souřadnice*256+y-ová souřadnice
            call calc_sprite_address        ; 3:17          výpočet adresy
            ld hl, SPRITE_ADR+1*(3*16)      ; 3:10
            ld bc, 0x0203                   ; 3:10          šířka a výška spritu
            call draw_sprite                ; 3:17
    
            ld de, 0x38F8                   ; 3:10          adresa
            call draw_my_sprite             ; 3:17
    
            ld de, 0x38C8                   ; 3:10          adresa
            call draw_my_sprite             ; 3:17
    
    
    
            jr $                            ; žádný návrat do systému
    
    
    calc_sprite_address:
            ; parametry:
            ; B - x-ová souřadnice (ve znacích, ne pixelech)
            ; C - y-ová souřadnice (ve znacích, ne pixelech)
            ;
            ; návratové hodnoty:
            ; DE - adresa pro zápis bloku
            ;
            ; vzor adresy:
            ; 0 1 0 Y4 Y3 0 0 0 | Y2 Y1 Y0 X4 X3 X2 X1 X0
            ld  a, c
            and %00000111                   ; pouze spodní tři bity y-ové souřadnice (řádky 0..7)
            rrca
            rrca
            rrca                            ; nyní jsou čísla řádků v horních třech bitech
            or  b                           ; připočítat x-ovou souřadnici
            ld  e, a                        ; máme spodní bajt adresy
                                            ; Y2 Y1 Y0 X4 X3 X2 X1 X0
    
            ld  a, c                        ; y-ová souřadnice
            and %00011000                   ; dva bity s indexem "bloku" 0..3 (dolní tři bity už máme zpracovány)
            or  %01000000                   ; "posun" do obrazové paměti (na 0x4000)
            ld  d, a                        ; máme horní bajt adresy
                                            ; 0 1 0 Y5 Y4 0 0 0
            ret                             ; návrat z podprogramu
    
    
    draw_my_sprite:
            ld hl, SPRITE_ADR               ; 3:10
            ld bc, 3*256+3                  ; 3:10          výška spritu v řádcích
    
    ; input:
    ; hl = adresa dat spritu
    ; de = adresa nulteho pixelradku znaku
    ; bc = šířka a výška spritu ve znacích
    draw_sprite:
            ld  a, c                        ; 1:4
            ld  (draw_sprite_c), a          ; 3:13
    draw_sprite_same_high:
            di                              ; 1:4
            ld (draw_sprite_exit+1), sp     ; 4:20
            ld sp, hl                       ; 1:6           adresa, od níž začíná data a maska spritu
    
            jr draw_sprite_init             ; 2:12
    draw_sprite_column:                     ;               další sloupec
            dec b                           ; 1:4
            jr  z, draw_sprite_exit         ; 2:7/12
    
    draw_sprite_de equ $+1
            ld de, 0x0000                   ; 3:10          předchozí začátek sloupce (šlo by použít i ix nebo stínový registr)
            inc e                           ; 1:4           další sloupec
            ld  a, 0x1F                     ; 2:7
            and e                           ; 1:4
            jr  z, draw_sprite_exit         ; 2:7/12
    
    draw_sprite_c equ $+1
            ld  c, 0x00                     ; 2:7           výška spritu ve znacích
    draw_sprite_init:
            ld (draw_sprite_de), de         ; 4:20
    
    ; input:
    ; sp = adresa dat spritu
    ; de = adresa nulteho pixelradku znaku
    ; b = šířka spritu ve znacích
    ; c = výška spritu ve znacích
    draw_sprite_third:
            ld   a, d                       ; 1:4
            cp  0x58                        ; 2:7
            sbc  a, a                       ; 1:4
            and  d                          ; 1:4
            ld   d, a                       ; 1:4
    draw_sprite_row:
            pop hl                          ; 1:10          nultý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          první pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          druhý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          třetí pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          čtvrtý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          patý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          šestý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          sedmý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            dec c                           ; 1:4
            jr  z, draw_sprite_column       ; 2:7/12       další řádek?
    
            ld  a, e                        ; 1:4
            add a, 0x20                     ; 2:7
            ld  e, a                        ; 1:4
            jr  c, draw_sprite_third        ; 2:7/12       další třetina?
    
            ld  a, d                        ; 1:4
            sub 0x08                        ; 2:7
            ld  d, a                        ; 1:4
            jr  draw_sprite_row             ; 2:12
    
    draw_sprite_exit:
            ld  sp, 0x0000                  ; 3:10
            ei                              ; 1:4
            ret                             ; 1:10         návrat z podprogramu
    
    
    SPRITE_ADR
    ; column 0
            db %00000000, %11111111   ; 1
            db %00000000, %11111110
            db %00000001, %11111100
            db %00000011, %11111000
            db %00000101, %11110000
            db %00000101, %11110000
            db %00000101, %11110000
            db %00000100, %11110000
    
            db %00000111, %11110000   ; 2
            db %00001100, %11100000
            db %00011111, %11000000
            db %00000000, %11100000
            db %00000011, %11111000
            db %00000101, %11110000
            db %00001110, %11100000
            db %00011000, %11000000
    
            db %00011000, %11000000   ; 3
            db %00000001, %11100100
            db %00000011, %11111000
            db %00000001, %11111100
            db %00000010, %11111000
            db %00000111, %11100000
            db %00011110, %11000000
            db %00000000, %11100001
    ; column 1
            db %00000000, %11111111   ; 1
            db %00000000, %00001111
            db %11110000, %00000111
            db %00111000, %00000000
            db %11010111, %00000000
            db %11001100, %00000000
            db %00110000, %00000001
            db %11001000, %00000001
    
            db %00110110, %00000000   ; 2
            db %11111110, %00000000
            db %11111000, %00000000
            db %00000000, %00000000
            db %11111111, %00000000
            db %11111110, %00000000
            db %11111101, %00000000
            db %11111100, %00000000
    
            db %00000000, %00000011   ; 3
            db %11111000, %00000011
            db %11111100, %00000001
            db %10110000, %00000011
            db %00001100, %00000001
            db %00001110, %01100000
            db %00000111, %11110000
            db %00000000, %11111000
    ; column 2
            db %00000000, %11111111   ; 1
            db %00000000, %11101111
            db %00010000, %11000111
            db %00010000, %11000111
            db %00010000, %00000111
            db %00010000, %11000111
            db %00010000, %11000111
            db %00010000, %11000111
    
            db %00010000, %11000111   ; 2
            db %00111000, %00000011
            db %00000000, %00000111
            db %00110000, %00000111
            db %10110000, %00000111
            db %11100000, %00001111
            db %11000000, %00011111
            db %00000000, %00111111
    
            db %00000000, %11111111   ; 3
            db %00000000, %11111111
            db %00000000, %11111111
            db %00000000, %11111111
            db %00000000, %11111111
            db %00000000, %01111111
            db %10000000, %00111111
            db %00000000, %01111111
    
    alien8:
            incbin Alien8.scr
    
    end ENTRY_POINT
  • 22. 8. 2023 5:13

    _dw

    Upravil jsem tu rutinu pro vypocet adresy, aby zvladl i zaporne Y. Staci jen poprehazet trosku instrukce, takze ve vysledku to ma stejnou velikost i rychlost.

    calc_sprite_address:
            ; parametry:
            ; B - x-ová souřadnice (ve znacích, ne pixelech)
            ; C - y-ová souřadnice (ve znacích, ne pixelech)
            ;
            ; návratové hodnoty:
            ; DE - adresa pro zápis bloku
            ;
            ; vzor adresy:
            ; 0 1 0 Y4 Y3 0 0 0 | Y2 Y1 Y0 X4 X3 X2 X1 X0
            ld  a, c
            and %00000111                   ; pouze spodní tři bity y-ové souřadnice (řádky 0..7)
            rrca
            rrca
            rrca                            ; nyní jsou čísla řádků v horních třech bitech
            or  b                           ; připočítat x-ovou souřadnici
            ld  e, a                        ; máme spodní bajt adresy
                                            ; Y2 Y1 Y0 X4 X3 X2 X1 X0
            ld  a, c                        ; y-ová souřadnice
            add a, %01000000                ; + 0x4000
            and %11111000                   ; dva bity s indexem "bloku" 0..3 (dolní tři bity už máme zpracovány)
            ld  d, a                        ; máme horní bajt adresy
                                            ; 0 1 0 Y5 Y4 0 0 0
            ret                             ; návrat z podprogramu

    Aby to zvladlo i zaporne X jsem to uz musel upravit vic. Prvne jsem musel uvolnit jeden 16 bitovy registr takze jsem upravit tu rutinu vstupni registry byly zaroven vystupni registry. Aby to slo musel jsem prohodit co drzi X a co Y.

    calc_sprite_address:
            ; parametry:
            ; D - y-ová souřadnice (ve znacích, ne pixelech)
            ; E - x-ová souřadnice (ve znacích, ne pixelech)
            ;
            ; návratové hodnoty:
            ; DE - adresa pro zápis bloku
            ;
            ; vzor adresy:
            ; 0 1 0 Y4 Y3 0 0 0 | Y2 Y1 Y0 X4 X3 X2 X1 X0
            ld   a, d
            and  %00000111                  ; pouze spodní tři bity y-ové souřadnice (řádky 0..7)
            rrca
            rrca
            rrca                            ; nyní jsou čísla řádků v horních třech bitech
            or   e                          ; připočítat x-ovou souřadnici
            ld   e, a                       ; máme spodní bajt adresy
                                            ; Y2 Y1 Y0 X4 X3 X2 X1 X0
            ld   a, d                       ; y-ová souřadnice
            add  a, %01000000               ; + 0x4000
            and  %11111000                  ; dva bity s indexem "bloku" 0..3 (dolní tři bity už máme zpracovány)
            ld   d, a                       ; máme horní bajt adresy
                                            ; 0 1 0 Y5 Y4 0 0 0
            ret                             ; návrat z podprogramu

    Tim jsem si uvolnnil BC pro velikost spritu a HL pro jeho adresu a ta rutina mohlo zjistovat zda je X zaporne a pokud je tak misto toho X vynulovat a zmensit velikost sirky a dat spritu.

    calc_sprite_address:
            ; parametry:
            ; D - y-ová souřadnice (ve znacích, ne pixelech)
            ; E - x-ová souřadnice (ve znacích, ne pixelech)
            ;
            ; návratové hodnoty:
            ; DE - adresa pro zápis bloku
            ;
            ; vzor adresy:
            ; 0 1 0 Y4 Y3 0 0 0 | Y2 Y1 Y0 X4 X3 X2 X1 X0
    
            ; hl = adresa spritu
            ; bc = šířka a výška spritu ve znacích
    
    
            ld   a, e                       ; 1:4
            or   a                          ; 1:4
            jp   p, calc_sprite_address_pls ; 3:10
    
            push de                         ; 1:11
            ex   de, hl                     ; 1:4
            ld    h, 0x00                   ; 2:7
            ld    l, c                      ; 1:4
            add  hl, hl                     ; 1:11          2 * sprite vyska
            add  hl, hl                     ; 1:11          4 * sprite vyska
            add  hl, hl                     ; 1:11          8 * sprite vyska
            add  hl, hl                     ; 1:11          16* sprite vyska
            ex   de, hl                     ; 1:4
    
            add  hl, de                     ; 1:11          hl + a*sprite vyska*16
            dec   b                         ; 1:4
            jr    z, $+5                    ; 2:7/12        cely sprite je vlevo mimo screen
            inc   a                         ; 1:4
            jr   nz, $-5                    ; 2:7/12
            pop  de                         ; 1:10
            ld    e, a                      ; 1:4
            or    a                         ; 1:4
            ret  nz                         ; 1:5/11
    
    calc_sprite_address_pls:
            ld   a, d
            and  %00000111                  ; pouze spodní tři bity y-ové souřadnice (řádky 0..7)
            rrca
            rrca
            rrca                            ; nyní jsou čísla řádků v horních třech bitech
            or   e                          ; připočítat x-ovou souřadnici
            ld   e, a                       ; máme spodní bajt adresy
                                            ; Y2 Y1 Y0 X4 X3 X2 X1 X0
            ld   a, d                       ; y-ová souřadnice
            add  a, %01000000               ; + 0x4000
            and  %11111000                  ; dva bity s indexem "bloku" 0..3 (dolní tři bity už máme zpracovány)
            ld   d, a                       ; máme horní bajt adresy
                                            ; 0 1 0 Y5 Y4 0 0 0
            ret                             ; návrat z podprogramu

    Cely program co kresli teoreticky az 255 x 255 znaku spritu na souradnice XY -128 az +127:

    SCREEN_ADR      equ $4000
    ENTRY_POINT     equ $8000
    
            org ENTRY_POINT
    
    start:
    
    if 0
            ld   hl, alien8                 ; adresa zdrojového bloku
            ld   de, 0x4000                 ; adresa cílového bloku
            ld   bc, 32*(192+24)            ; velikost přenášených dat
            ldir                            ; provést blokový přenos
    endif
    
            ld   hl, 0x4000                 ; adresa cílového bloku
            ld  (hl), l                     ;
            ld   de, 0x4001                 ; adresa cílového bloku
            ld   bc, 32*192                 ; velikost přenášených dat
            ldir                            ; provést blokový přenos
    
            ld  (hl), %00111000             ;
            ld   bc, 32*24-1                ; velikost přenášených dat
            ldir                            ; provést blokový přenos
    
    
    check:
            ld   de, 256*1 +((0-3) & 255)   ; 3:10          y-ová souřadnice a x-ová souřadnice (uplne mimo screen)
            call draw_my_sprite             ; 3:17
    
            ld   de, 256*6 +((0-2) & 255)   ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 256*11+((0-1) & 255)   ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 256*16+0               ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 256*21+1               ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 0x0505                 ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 0x061E                 ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 0x0E10                 ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 0x161F                 ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 256*(0-2)+8            ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
            ld   de, 256*(0-1)+24           ; 3:10          y-ová souřadnice a x-ová souřadnice
            call draw_my_sprite             ; 3:17
    
    
            jr $                            ; žádný návrat do systému
    
    
    calc_sprite_address:
            ; parametry:
            ; D - y-ová souřadnice (ve znacích, ne pixelech)
            ; E - x-ová souřadnice (ve znacích, ne pixelech)
            ;
            ; návratové hodnoty:
            ; DE - adresa pro zápis bloku
            ;
            ; vzor adresy:
            ; 0 1 0 Y4 Y3 0 0 0 | Y2 Y1 Y0 X4 X3 X2 X1 X0
    
            ; hl = adresa spritu
            ; bc = šířka a výška spritu ve znacích
    
    
            ld   a, e                       ; 1:4
            or   a                          ; 1:4
            jp   p, calc_sprite_address_pls ; 3:10
    
            push de                         ; 1:11
            ex   de, hl                     ; 1:4
            ld    h, 0x00                   ; 2:7
            ld    l, c                      ; 1:4
            add  hl, hl                     ; 1:11          2 * sprite vyska
            add  hl, hl                     ; 1:11          4 * sprite vyska
            add  hl, hl                     ; 1:11          8 * sprite vyska
            add  hl, hl                     ; 1:11          16* sprite vyska
            ex   de, hl                     ; 1:4
    
            add  hl, de                     ; 1:11          hl + a*sprite vyska*16
            dec   b                         ; 1:4
            jr    z, $+5                    ; 2:7/12        cely sprite je vlevo mimo screen
            inc   a                         ; 1:4
            jr   nz, $-5                    ; 2:7/12
            pop  de                         ; 1:10
            ld    e, a                      ; 1:4
            or    a                         ; 1:4
            ret  nz                         ; 1:5/11
    
    calc_sprite_address_pls:
            ld   a, d
            and  %00000111                  ; pouze spodní tři bity y-ové souřadnice (řádky 0..7)
            rrca
            rrca
            rrca                            ; nyní jsou čísla řádků v horních třech bitech
            or   e                          ; připočítat x-ovou souřadnici
            ld   e, a                       ; máme spodní bajt adresy
                                            ; Y2 Y1 Y0 X4 X3 X2 X1 X0
            ld   a, d                       ; y-ová souřadnice
            add  a, %01000000               ; + 0x4000
            and  %11111000                  ; dva bity s indexem "bloku" 0..3 (dolní tři bity už máme zpracovány)
            ld   d, a                       ; máme horní bajt adresy
                                            ; 0 1 0 Y5 Y4 0 0 0
            ret                             ; návrat z podprogramu
    
    draw_my_sprite:
            ld   hl, SPRITE_ADR             ; 3:10
            ld   bc, 3*256+3                ; 3:10          výška spritu v řádcích
            call calc_sprite_address        ; 3:17          výpočet adresy
    
    
    ; input:
    ; hl = adresa dat spritu
    ; de = adresa nulteho pixelradku znaku
    ; bc = šířka a výška spritu ve znacích
    draw_sprite:
            ld  a, c                        ; 1:4
            ld  (draw_sprite_c), a          ; 3:13
    draw_sprite_same_high:
            di                              ; 1:4
            ld (draw_sprite_exit+1), sp     ; 4:20
            ld sp, hl                       ; 1:6           adresa, od níž začíná data a maska spritu
    
            jr draw_sprite_init             ; 2:12
    draw_sprite_column:                     ;               další sloupec
            dec b                           ; 1:4
            jr  z, draw_sprite_exit         ; 2:7/12
    
    draw_sprite_de equ $+1
            ld de, 0x0000                   ; 3:10          předchozí začátek sloupce (šlo by použít i ix nebo stínový registr)
            inc e                           ; 1:4           další sloupec
            ld  a, 0x1F                     ; 2:7
            and e                           ; 1:4
            jr  z, draw_sprite_exit         ; 2:7/12
    
    draw_sprite_c equ $+1
            ld  c, 0x00                     ; 2:7           výška spritu ve znacích
    draw_sprite_init:
            ld (draw_sprite_de), de         ; 4:20
    
    ; input:
    ; sp = adresa dat spritu
    ; de = adresa nulteho pixelradku znaku
    ; b = šířka spritu ve znacích
    ; c = výška spritu ve znacích
    draw_sprite_third:
            ld   a, d                       ; 1:4
            cp  0x58                        ; 2:7
            sbc  a, a                       ; 1:4
            and  d                          ; 1:4
            ld   d, a                       ; 1:4
    draw_sprite_row:
            pop hl                          ; 1:10          nultý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          první pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          druhý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          třetí pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          čtvrtý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          patý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          šestý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            pop hl                          ; 1:10          sedmý pixelřádek
            ld  a, (de)                     ; 1:7
            and h                           ; 1:4
            xor l                           ; 1:4
            ld (de), a                      ; 1:7
            inc d                           ; 1:4
    
            dec c                           ; 1:4
            jr  z, draw_sprite_column       ; 2:7/12       další řádek?
    
            ld  a, e                        ; 1:4
            add a, 0x20                     ; 2:7
            ld  e, a                        ; 1:4
            jr  c, draw_sprite_third        ; 2:7/12       další třetina?
    
            ld  a, d                        ; 1:4
            sub 0x08                        ; 2:7
            ld  d, a                        ; 1:4
            jr  draw_sprite_row             ; 2:12
    
    draw_sprite_exit:
            ld  sp, 0x0000                  ; 3:10
            ei                              ; 1:4
            ret                             ; 1:10         návrat z podprogramu
    
    
    SPRITE_ADR
    ; column 0
            db %00000000, %11111111   ; 1
            db %00000000, %11111110
            db %00000001, %11111100
            db %00000011, %11111000
            db %00000101, %11110000
            db %00000101, %11110000
            db %00000101, %11110000
            db %00000100, %11110000
    
            db %00000111, %11110000   ; 2
            db %00001100, %11100000
            db %00011111, %11000000
            db %00000000, %11100000
            db %00000011, %11111000
            db %00000101, %11110000
            db %00001110, %11100000
            db %00011000, %11000000
    
            db %00011000, %11000000   ; 3
            db %00000001, %11100100
            db %00000011, %11111000
            db %00000001, %11111100
            db %00000010, %11111000
            db %00000111, %11100000
            db %00011110, %11000000
            db %00000000, %11100001
    ; column 1
            db %00000000, %11111111   ; 1
            db %00000000, %00001111
            db %11110000, %00000111
            db %00111000, %00000000
            db %11010111, %00000000
            db %11001100, %00000000
            db %00110000, %00000001
            db %11001000, %00000001
    
            db %00110110, %00000000   ; 2
            db %11111110, %00000000
            db %11111000, %00000000
            db %00000000, %00000000
            db %11111111, %00000000
            db %11111110, %00000000
            db %11111101, %00000000
            db %11111100, %00000000
    
            db %00000000, %00000011   ; 3
            db %11111000, %00000011
            db %11111100, %00000001
            db %10110000, %00000011
            db %00001100, %00000001
            db %00001110, %01100000
            db %00000111, %11110000
            db %00000000, %11111000
    ; column 2
            db %00000000, %11111111   ; 1
            db %00000000, %11101111
            db %00010000, %11000111
            db %00010000, %11000111
            db %00010000, %00000111
            db %00010000, %11000111
            db %00010000, %11000111
            db %00010000, %11000111
    
            db %00010000, %11000111   ; 2
            db %00111000, %00000011
            db %00000000, %00000111
            db %00110000, %00000111
            db %10110000, %00000111
            db %11100000, %00001111
            db %11000000, %00011111
            db %00000000, %00111111
    
            db %00000000, %11111111   ; 3
            db %00000000, %11111111
            db %00000000, %11111111
            db %00000000, %11111111
            db %00000000, %11111111
            db %00000000, %01111111
            db %10000000, %00111111
            db %00000000, %01111111
    
    alien8:
            incbin Alien8.scr
    
    end ENTRY_POINT

    PS: Ten Alien8 screen ma bohuzel 20 radek nastaven jak paper tak ink na nulu. Takze to testuji na bile plose.

  • 19. 8. 2023 18:38

    Dushino

    Jsem odkojený Atari 800 XL. A v každé kapitole tohoto seriálu mi několikrát unikne z úst: "To je peklo!". Takže velký respekt pánům programátorům snad i dámám programátirkám na ZX Spectrum!

  • 21. 8. 2023 10:38

    Korporátní lopata

    To je fakt zvláštní, mně naopak přijde ZX neuvěřitelně jednoduché: Z80 + jeden port + organizace videopaměti, toť vše. Když to porovnám s Atari nebo C64, kde se člověk musí naučit ještě funkce podpůrných čipů.

  • 21. 8. 2023 21:35

    Dushino

    Je fakt, že pro zobrazení "hráče" je třeba nějaké to nastavení předem, ale pak už stačí jen zápis do jednoho registru a máme horizontální pohyb. Pro vertikální pohyb je třeba posunout data "hráče" v lineární paměti. A pro detekci kolize stačí vyčíst jeden registr. Za mě ta složitější inicializace stojí za to, protože v runtime to šetři spoustu výkonu CPU a tím i času. Viz seriál o Atari. Ono to není zas tak složité.

  • 22. 8. 2023 11:53

    Korporátní lopata

    Mno, pro jeden sprajt asi jo. Ale když chci opravdu hodně sprajtů, tak asi budu muset napsat nějaký multiplexer. Na spectru umím tohle a je to trivka, stačí mít sprajty setříděné podle osy y, což udělám jednoduchou variací na insert sort.
    https://drive.google.com/file/d/1Ez21Plh3ixntP6ByLRhN3YyRF4mfFoD_/view?usp=drive_link

  • 22. 8. 2023 13:58

    Dushino

    Atari hardwarově podporuje 4-5 spritů. (Pátý může vzniknout kombinací "střel" do jednoho "hráče".) To je ale to, co podporují podpůrné čipy. (Různými fintami se dá docílit většího počtu, ale to je už opravdu hardcore.)
    Stále je možno sprity řešit tak, jako na ZX Spectru, tj. zobrazením přímo ve videoRAM bez podpory HW. Pak ale, stejně jako na Spectru, musím řešit vykreslování, clipping na hranicích obrazovky a kolize. Třeba jako zde: http://atari.vjetnam.cz/index.php?frame=sres&sfield=atari_invaders&page=0
    Jsou to dva různé přístupy, oba jsou možné.

  • 22. 8. 2023 14:55

    Korporátní lopata

    Jasně, chápu. Já sem právě nedávno koukal na Segu Master System a dostalo mne že když portovali Lemmingy, tak to museli řešit softwarovými sprajty protože VDP prostě nedá víc než osm sprajtů na mikrořádku a lemmingů je řádově víc. To musela být otročina ten kód odladit.
    Ono se s tím clippováním na spectru dost podvádí, buď se to maskuje attributama nebo se kresli do bufru bez ořezů a na obrazovku se kopíruje už ořez.
    Můj oblibenej trik pro vertikální clipping je použití tabulky mikrořádků pro výpočet vertikální polohy a přechod na každý další druhý mikrořádek. A když už to člověk má, tak prvních a posledních 16 logických mikrořádku nereferuje adresy ve video paměti ale prostor v ROM, takže sprajtovací rutina to nemusí řešit.
    Kolize se počítá ručně, ale zas si člověk může hrát s velikostí kolizních boxů, docela to pomáhá s hratelností.
    Ve výsledku si nad těmahle problémama člověk láme hlavu, když to píše poprvé, podruhé, pak už to je buď copy paste nebo má v hlavě dobrou představu jak to napsat.