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
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í.
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.
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
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
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
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".
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 :)
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
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.
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é.
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
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é.
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.