Obsah
1. Vykreslování spritů a animací na ZX Spectru
2. Sprity na osmibitových mikropočítačích
3. Vykreslení statického spritu
4. Úplný zdrojový kód dnešního prvního demonstračního příkladu
6. Zápis definice spritu v assembleru
7. Vykreslení většího spritu na obrazovku
8. Úplný zdrojový kód dnešního druhého demonstračního příkladu
9. Načtení definice spritu z „include“souboru
10. Úplný zdrojový kód dnešního třetího demonstračního příkladu
11. Načtení spritu z binárního souboru v době překladu
12. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu
13. Je mezi oběma způsoby načtení spritu v době překladu nějaký rozdíl?
14. Jemný posun spritu po jednotlivých pixelech
15. Hrubá výpočetní síla vs. předpočítání dat
16. Pomocný nástroj pro předpočítání jemného posunu spritu po jednotlivých pixelech
17. Zobrazení spritu s jemným posunem po obrazovce
18. Příloha: upravený soubor Makefile pro překlad demonstračních příkladů
19. Repositář s demonstračními příklady
1. Vykreslování spritů a animací na ZX Spectru
V dnešní části seriálu o tvorbě her i dalších aplikací určených pro slavné ZX Spectrum se začneme zabývat velmi zajímavou, ale o to složitější oblastí. Jedná se o vykreslování takzvaných spritů a taktéž o tvorbu animací na ZX Spectru. Může se jednat o animaci postavičky ve hře či v extrémním případě o postupnou změnu celé herní scény. Sprity a animace (resp. zkombinováno do animovaných spritů) jsou tedy nedílnou součástí většiny (akčních) her a liší se podle toho, jak vlastně vypadá herní scéna (2D střílečka, skákačka, isometrické zobrazení, 3D zobrazení atd.). Na ZX Spectru je vše poměrně komplikované (a z pohledu programátora tedy i zajímavé), a to zejména z těchto důvodů:
- Na původním ZX Spectru 48k není k dispozici zadní buffer (back buffer) určený pro vykreslování, takže se většinou vše přímo vykresluje na obrazovku, což může tvořit viditelné artefakty.
- Kvůli specifické struktuře obrazové paměti jsou výpočty adres pixelů složitější, než by to bylo nutné při lineárním adresování.
- Nejsou podporovány hardwarové sprity. Tím pádem nejsou podporovány ani detekce kolizí atd. (ale to již opouštíme oblast animací).
- A konečně: opět kvůli specifické struktuře obrazové paměti při dotyku a překryvu objektů dochází k porušení barvových atributů (attribute clash), čemuž se nevyhnula prakticky žádná hra na ZX Spectru (až na specifické případy, kdy se například postavička pohybuje tak, aby nepřekročila velikost atributu a i pozadí je dobře zvolené).
I přes tato omezení nalezneme na ZX Spectru mnoho her, v nichž jsou animace (typicky se jedná o animace herních postaviček) řešeny velmi uspokojivým způsobem.
2. Sprity na osmibitových mikropočítačích
Na některých osmibitových domácích počítačích a taktéž osmibitových herních konzolích se ve hrách, ale i v dalších aplikacích zaměřených na počítačovou grafiku, často používala možnost zrychleného vykreslování jednoduchých rastrových obrázků nazývaných sprity. Generování obrazů spritů na televizi či obrazovce monitoru přitom probíhalo nezávisle na vykreslování ostatní scény (v tomto případě nazývané pozadí – background, popř. hrací plocha – playground), přičemž bylo možné definovat prioritu vykreslování, tj. zda bude ve výsledném obrázku upřednostněn pixel z pozadí nebo ze spritu. Grafické subsystémy těchto počítačů, které podporovaly práci se sprity, také většinou obsahovaly takzvané kolizní registry, do kterých se ukládaly příznaky překryvu pozadí se sprity (kolize spritu s barvou pozadí) nebo mezi několika sprity navzájem (kolize spritů). Tím se při práci s pohyblivými obrazy předmětů nemusely provádět zdlouhavé testy mezi všemi zobrazovanými pixely, postačilo pouze přečíst stav z kolizních registrů.
Obrázek 1: Screenshot ze hry Starquake ve verzi pro osmibitové počítače Atari. Tyto počítače podporovaly vykreslování čtyř jednobarevných spritů (každý sprite samozřejmě mohl mít jinou barvu), jejichž rozlišení bylo 8×128, resp. 8×256 pixelů a dále čtyř takzvaných střel, což byly sprity zúžené na 2×128 nebo 2×256 pixelů (všechny střely se navíc mohly spojit do pátého spritu). Hrací plocha hry Starquake je vytvořena v monochromatickém textově-grafickém režimu, ovšem hráč je vykreslen pomocí spritu, tj. může mít barvu odlišnou od okolní scény a může se pohybovat v horizontálním i vertikálním směru bez nutnosti přesunů dat v obrazové paměti.
U většiny osmibitových počítačů, které podporovaly vykreslování spritů, byly video paměti určené pro uložení pozadí (hrací plochy) a paměti pro uložení rastrových dat spritů od sebe odděleny a teprve až v průběhu vykreslování na obrazovku se provedla kombinace pixelů pozadí a pixelů uložených ve spritech – viz druhý obrázek. Sprity byly poprvé použity v dnes již zapomenutém počítači Elektor TV Games Computer z roku 1979. Později byly podporovány například na osmibitových počítačích Atari, na počítači Commodore C64 (viz další kapitoly) a v pozdější době i na herních konzolích, a to včetně NESu.
Obrázek 2: Tok dat mezi operační pamětí, mikroprocesorem a čipy ANTIC i GTIA při generování video signálu na osmibitových počítačích Atari.
A jak je tomu na ZX Spectru? Hardwarové sprity podporovány nejsou, takže je vše nutné vykreslovat (a neustále překreslovat) v obrazové paměti. Navíc jsme zde omezeni možnostmi atributové paměti (známé barvové bloky o velikosti 8×8 pixelů), což znamená, že z programátorského pohledu je vše mnohem zajímavější.
3. Vykreslení statického spritu
Vraťme se nyní k problematice vykreslování spritů na ZX Spectru. Začneme jednoduchým příkladem, který již vlastně prakticky celý známe; tento příklad budeme postupně upravovat a „vyšperkovávat“. Nejprve se tedy pokusme o vykreslení statického spritu o velikosti 8×8 pixelů. Pro ilustraci jsem použil sprite z Atari, který jsem chtěl využít v nerealizované hře :-). Tvar spritu je do značné míry inspirován slavnou hrou Space Invaders:
██ ████ ██████ ██ ██ ██ ████████ █ █ █ ██ █ █ █ █ █
Bitmapu se spritem převedeme do binární podoby, přičemž černé pixely budou mít hodnotu 1 a průhledné pixely hodnotu 0:
00011000 00111100 01111110 11011011 11111111 00100100 01011010 10100101
Bitmapu se spritem můžeme ve zdrojovém kódu zapsat třeba takto (převod na decimální hodnoty):
SPRITE_ADR db 24, 60, 126, 219, 255, 36, 90, 165
Ovšem nic nám nebrání zůstat u binárních hodnot:
SPRITE_ADR db %00011000, %00111100, %01111110, %11011011, %11111111, %00100100, %01011010, %10100101
Popř. můžeme ponechat původní bitmapu viditelnou i přímo v kódu:
SPRITE_ADR db %00011000 db %00111100 db %01111110 db %11011011 db %11111111 db %00100100 db %01011010 db %10100101
A jak se takový sprite může vykreslit? Vzhledem k tomu, že má velikost 8×8 pixelů a pokud se omezíme na umístění spritu na souřadnice, které jsou násobkem osmi (což nám později zjednoduší práci s barvovými atributy), můžeme použít nám již známý kód pro vykreslení znaku. Ovšem namísto čtení bitmapy (masky) znaku z ROM budeme číst data od adresy SPRITE_ADR.
Připomeňme si – vypočtení adresy na obrazovce, přičemž je nutné dbát na specifickou strukturu obrazové paměti ZX Spectra:
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
Samotné vykreslení všech 64 pixelů se spritem je již triviální:
draw_sprite: ld hl, SPRITE_ADR ; adresa, od níž začíná maska spritu ld b, 8 ; počitadlo zapsaných bajtů loop: ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc l ; posun na další bajt masky (nemusíme řešit přetečení do vyššího bajtu) inc d ; posun na definici dalšího obrazového řádku djnz loop ; vnitřní smyčka: blok s osmi zápisy ret ; návrat z podprogramu
A takto bude vypadat obrazovka po vykreslení dvou spritů na různá místa:
Obrázek 3: Dvojice spritů vykreslená na obrazovku ZX Spectra.
Obrázek 4: Zvětšený sprite zobrazený na obrazovce ZX Spectra.
4. Úplný zdrojový kód dnešního prvního demonstračního příkladu
Úplný zdrojový kód dnešního prvního demonstračního příkladu naleznete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/120–8×8-sprite.asm:
SCREEN_ADR equ $4000 ENTRY_POINT equ $8000 org ENTRY_POINT start: ld b, 15 ; x-ová souřadnice ld c, 12 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy spritu call draw_sprite ld b, 31 ; x-ová souřadnice ld c, 23 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy call draw_sprite finish: jr finish ; žá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_sprite: ld hl, SPRITE_ADR ; adresa, od níž začíná maska spritu ld b, 8 ; počitadlo zapsaných bajtů loop: ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc l ; posun na další bajt masky (nemusíme řešit přetečení do vyššího bajtu) inc d ; posun na definici dalšího obrazového řádku djnz loop ; vnitřní smyčka: blok s osmi zápisy ret ; návrat z podprogramu SPRITE_ADR db 24, 60, 126, 219, 255, 36, 90, 165 end ENTRY_POINT
Překlad tohoto velmi jednoduchého demonstračního příkladu do strojového kódu je zcela přímočarý, což je ostatně patrné i z následujícího výpisu:
SCREEN_ADR EQU 4000 ENTRY_POINT EQU 8000 ORG 8000 8000: label start 8000:060F LD B, 0F 8002:0E0C LD C, 0C 8004:CD1680 CALL 8016 8007:CD2580 CALL 8025 800A:061F LD B, 1F 800C:0E17 LD C, 17 800E:CD1680 CALL 8016 8011:CD2580 CALL 8025 8014: label finish 8014:18FE JR 8014 8016: label calc_sprite_address 8016:79 LD A, C 8017:E607 AND 07 8019:0F RRCA 801A:0F RRCA 801B:0F RRCA 801C:B0 OR B 801D:5F LD E, A 801E:79 LD A, C 801F:E618 AND 18 8021:F640 OR 40 8023:57 LD D, A 8024:C9 RET 8025: label draw_sprite 8025:213180 LD HL, 8031 8028:0608 LD B, 08 802A: label loop 802A:7E LD A, (HL) 802B:12 LD (DE), A 802C:2C INC L 802D:14 INC D 802E:10FA DJNZ 802A 8030:C9 RET 8031: label SPRITE_ADR 8031:183C7EDB DEFB of 8 bytes 8035:FF245AA5 8039: END 8000 Emiting TAP basic loader Emiting TAP from 8000 to 8038
5. Definice většího spritu
Nyní se pokusme o vykreslení většího spritu. Vybereme si sprite z další známé hry, tentokrát ze Sabre Wulfa pocházejícího z roku 1984:
Obrázek 5: Postavička Sabremana v původním prostředí hry Sabre Wulf.
Postavička je zhruba definována v rastru 24×24 pixelů:
Obrázek 6: Vyříznutá postavička, která byla získána z předchozího screenshotu.
Bitmapa resp. maska spritu vypadá následovně:
█████ █ ██ ███ █ █ ███ █ ███ █ █ ███ ██ █ █ █ ██ █ █ ██ █ █ ███ ██ ██ █ ██ ███████ ███ ██████████ ██ ███████████ ██ █ ████████ ███ ███ ██████ ███ ██ ██████ ██ ██████ ████████ ██ ██ █ ██ ███ ███ ████ ████
Převedeno do binárního kódu získáme následující kód s bitmapou:
000000000000000000000000 000000000000000000000000 000000011111000000010000 000000110011100000010000 000001011101011100010000 000001011100110000010000 000001010011000000010000 000001001100100000010000 000001110011011000010000 000011001111111000111000 000111111111100000000000 000000000000000000110000 000000111111111110110000 000001011111111011100000 000011101111110111000000 000110001111110000000000 000110000000000000000000 000000011111100000000000 000000111111110000000000 000000011011000000000000 000000100000110000000000 000001110000111000000000 000111100000011110000000 000000000000000000000000
6. Zápis definice spritu v assembleru
V assembleru jsme direktivou db (define byte) omezeni na bitová pole o šířce osmi bitů. Šlo by sice použít i další direktivy („širší“ bitová pole), ovšem pro sprite o šířce 24 bitů (3 bajty) je pravděpodobně nejjednodušší zůstat u direktivy db a masku spritu zapsat do zdrojového kódu následovně:
SPRITE_ADR db %00000000, %00000000, %00000000 db %00000000, %00000000, %00000000 db %00000001, %11110000, %00010000 db %00000011, %00111000, %00010000 db %00000101, %11010111, %00010000 db %00000101, %11001100, %00010000 db %00000101, %00110000, %00010000 db %00000100, %11001000, %00010000 db %00000111, %00110110, %00010000 db %00001100, %11111110, %00111000 db %00011111, %11111000, %00000000 db %00000000, %00000000, %00110000 db %00000011, %11111111, %10110000 db %00000101, %11111110, %11100000 db %00001110, %11111101, %11000000 db %00011000, %11111100, %00000000 db %00011000, %00000000, %00000000 db %00000001, %11111000, %00000000 db %00000011, %11111100, %00000000 db %00000001, %10110000, %00000000 db %00000010, %00001100, %00000000 db %00000111, %00001110, %00000000 db %00011110, %00000111, %10000000 db %00000000, %00000000, %00000000
7. Vykreslení většího spritu na obrazovku
Mnohé sprity mají větší velikost než pouhých 8×8 pixelů, takže musíme mít k dispozici mechanismy pro práci s většími sprity. Pro jednoduchost se omezíme pouze na sprity, jejichž horizontální rozměr je celočíselným násobkem osmi, což znamená, že řádek s maskou spritu může být uložen v celých bajtech. V dalším textu si ukážeme vykreslení spritu o velikosti 24×24 pixelů, konkrétně se bude jednat o Sabremana, s nímž jsme se setkali v předchozích dvou kapitolách.
Obrázek 7: Na některých platformách se vykreslování spritů stará specializovaný čip. To však není případ ZX Spectra.
Rutinu pro zobrazení spritu rozšíříme tak, že bude volat další subrutinu určenou pro vykreslení osmi řádků spritu. Tato subrutina bude volána celkem třikrát, protože potřebujeme vykreslit 3×8=24 řádků:
draw_sprite: ld hl, SPRITE_ADR ; adresa, od níž začíná maska spritu push de call draw_8_lines ; vykreslit prvních 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 push de call draw_8_lines ; vykreslit druhých 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 call draw_8_lines ; vykreslit třetích 8 řádků spritu ret ; návrat z podprogramu
A vlastní subrutina pro vykreslení vykreslí vždy tři bajty, tedy 24 pixelů:
draw_8_lines: ld b, 8 ; počitadlo zapsaných řádků loop: ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc d ; posun na definici dalšího obrazového řádku dec e ; korekce - posun zpět pod první osmici pixelů dec e ; dtto djnz loop ; vnitřní smyčka: blok s 3x osmi zápisy ret ; návrat z podprogramu
Výsledek bude vypadat následovně:
Obrázek 8: Sprity o velikosti 24×24 pixelů vykreslené na obrazovku ZX Spectra.
8. Úplný zdrojový kód dnešního druhého demonstračního příkladu
Úplný zdrojový kód dnešního druhého demonstračního příkladu naleznete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/121–24×24-sprite.asm. Vypadá následovně:
SCREEN_ADR equ $4000 ENTRY_POINT equ $8000 org ENTRY_POINT start: ld b, 15 ; x-ová souřadnice ld c, 3 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy spritu call draw_sprite ld b, 29 ; x-ová souřadnice ld c, 21 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy call draw_sprite finish: jr finish ; žá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 add_e MACRO n ; zvýšení hodnoty regitru E ld a, e add a, n ld e, a endm draw_sprite: ld hl, SPRITE_ADR ; adresa, od níž začíná maska spritu push de call draw_8_lines ; vykreslit prvních 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 push de call draw_8_lines ; vykreslit druhých 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 call draw_8_lines ; vykreslit třetích 8 řádků spritu ret ; návrat z podprogramu draw_8_lines: ld b, 8 ; počitadlo zapsaných řádků loop: ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc d ; posun na definici dalšího obrazového řádku dec e ; korekce - posun zpět pod první osmici pixelů dec e ; dtto djnz loop ; vnitřní smyčka: blok s 3x osmi zápisy ret ; návrat z podprogramu SPRITE_ADR db %00000000, %00000000, %00000000 db %00000000, %00000000, %00000000 db %00000001, %11110000, %00010000 db %00000011, %00111000, %00010000 db %00000101, %11010111, %00010000 db %00000101, %11001100, %00010000 db %00000101, %00110000, %00010000 db %00000100, %11001000, %00010000 db %00000111, %00110110, %00010000 db %00001100, %11111110, %00111000 db %00011111, %11111000, %00000000 db %00000000, %00000000, %00110000 db %00000011, %11111111, %10110000 db %00000101, %11111110, %11100000 db %00001110, %11111101, %11000000 db %00011000, %11111100, %00000000 db %00011000, %00000000, %00000000 db %00000001, %11111000, %00000000 db %00000011, %11111100, %00000000 db %00000001, %10110000, %00000000 db %00000010, %00001100, %00000000 db %00000111, %00001110, %00000000 db %00011110, %00000111, %10000000 db %00000000, %00000000, %00000000 end ENTRY_POINT
Zajímavé bude zjistit, jak se vlastně přeložila sekvence direktiv db do výsledného binárního kódu:
SCREEN_ADR EQU 4000 ENTRY_POINT EQU 8000 ORG 8000 8000: label start 8000:060F LD B, 0F 8002:0E03 LD C, 03 8004:CD1680 CALL 8016 8007:CD2580 CALL 8025 800A:061D LD B, 1D 800C:0E15 LD C, 15 800E:CD1680 CALL 8016 8011:CD2580 CALL 8025 8014: label finish 8014:18FE JR 8014 8016: label calc_sprite_address 8016:79 LD A, C 8017:E607 AND 07 8019:0F RRCA 801A:0F RRCA 801B:0F RRCA 801C:B0 OR B 801D:5F LD E, A 801E:79 LD A, C 801F:E618 AND 18 8021:F640 OR 40 8023:57 LD D, A 8024:C9 RET Defining MACRO add_e Params: n 8025: label draw_sprite 8025:215180 LD HL, 8051 8028:D5 PUSH DE 8029:CD3E80 CALL 803E 802C:D1 POP DE Expanding MACRO add_e n= 0020 LD A , E 802D:7B LD A, E ADD A , n 802E:C620 ADD A, 20 LD E , A 8030:5F LD E, A ENDM ENDM End of MACRO add_e 8031:D5 PUSH DE 8032:CD3E80 CALL 803E 8035:D1 POP DE Expanding MACRO add_e n= 0020 LD A , E 8036:7B LD A, E ADD A , n 8037:C620 ADD A, 20 LD E , A 8039:5F LD E, A ENDM ENDM End of MACRO add_e 803A:CD3E80 CALL 803E 803D:C9 RET 803E: label draw_8_lines 803E:0608 LD B, 08 8040: label loop 8040:7E LD A, (HL) 8041:12 LD (DE), A 8042:23 INC HL 8043:1C INC E 8044:7E LD A, (HL) 8045:12 LD (DE), A 8046:23 INC HL 8047:1C INC E 8048:7E LD A, (HL) 8049:12 LD (DE), A 804A:23 INC HL 804B:14 INC D 804C:1D DEC E 804D:1D DEC E 804E:10F0 DJNZ 8040 8050:C9 RET 8051: label SPRITE_ADR 8051:000000 DEFB of 3 bytes 8054:000000 DEFB of 3 bytes 8057:01F010 DEFB of 3 bytes 805A:033810 DEFB of 3 bytes 805D:05D710 DEFB of 3 bytes 8060:05CC10 DEFB of 3 bytes 8063:053010 DEFB of 3 bytes 8066:04C810 DEFB of 3 bytes 8069:073610 DEFB of 3 bytes 806C:0CFE38 DEFB of 3 bytes 806F:1FF800 DEFB of 3 bytes 8072:000030 DEFB of 3 bytes 8075:03FFB0 DEFB of 3 bytes 8078:05FEE0 DEFB of 3 bytes 807B:0EFDC0 DEFB of 3 bytes 807E:18FC00 DEFB of 3 bytes 8081:180000 DEFB of 3 bytes 8084:01F800 DEFB of 3 bytes 8087:03FC00 DEFB of 3 bytes 808A:01B000 DEFB of 3 bytes 808D:020C00 DEFB of 3 bytes 8090:070E00 DEFB of 3 bytes 8093:1E0780 DEFB of 3 bytes 8096:000000 DEFB of 3 bytes 8099: END 8000 Emiting TAP basic loader Emiting TAP from 8000 to 8098
9. Načtení definice spritu z „include“souboru
Assembler Pasmo umožňuje do (hlavního) překládaného souboru vložit (include) obsah jiného souboru, který bude interpretován stejným způsobem – tedy tak, jakoby se jednalo o zdrojový kód zapsaný v assembleru. To nám mj. umožňuje oddělit data s definicí spritu (nebo klidně i data s obsahem celé obrazové paměti, melodie, definice scén ve hře atd.) do zvláštního souboru, popř. do zvláštních souborů vložit knihovní funkce nebo často používaná makra (nemusíme se tedy pokoušet o klasický přístup s linkingem – to nemá u mikroprocesoru omezeného na 64kB RAM zvláštní význam).
Podívejme se nyní, jak bude vypadat obsah souboru nazvaného sprite.asm, který bude obsahovat masku se spritem definovanou s využitím direktivy db či defb neboli define byte (viz též https://pasmo.speccy.org/pasmodoc.html#dirdb):
db %00000000, %00000000, %00000000 db %00000000, %00000000, %00000000 db %00000001, %11110000, %00010000 db %00000011, %00111000, %00010000 db %00000101, %11010111, %00010000 db %00000101, %11001100, %00010000 db %00000101, %00110000, %00010000 db %00000100, %11001000, %00010000 db %00000111, %00110110, %00010000 db %00001100, %11111110, %00111000 db %00011111, %11111000, %00000000 db %00000000, %00000000, %00110000 db %00000011, %11111111, %10110000 db %00000101, %11111110, %11100000 db %00001110, %11111101, %11000000 db %00011000, %11111100, %00000000 db %00011000, %00000000, %00000000 db %00000001, %11111000, %00000000 db %00000011, %11111100, %00000000 db %00000001, %10110000, %00000000 db %00000010, %00001100, %00000000 db %00000111, %00001110, %00000000 db %00011110, %00000111, %10000000 db %00000000, %00000000, %00000000
V překládaném „hlavním“ souboru nám postačí použít direktivu include určenou pro vložení spritu do překládaného souboru (viz též https://pasmo.speccy.org/pasmodoc.html#dirinclude). Samozřejmě použijeme i návěští (label):
SPRITE_ADR include "sprite.asm"
Na obrazovce nepoznáme žádný rozdíl:
Obrázek 9: Dvojice spritů, z nichž každý má rozměry 24×24 pixelů.
10. Úplný zdrojový kód dnešního třetího demonstračního příkladu
Úplný zdrojový kód dnešního třetího demonstračního příkladu naleznete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/122–24×24-sprite.asm. Kód vypadá následovně:
SCREEN_ADR equ $4000 ENTRY_POINT equ $8000 org ENTRY_POINT start: ld b, 15 ; x-ová souřadnice ld c, 3 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy spritu call draw_sprite ld b, 29 ; x-ová souřadnice ld c, 21 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy call draw_sprite finish: jr finish ; žá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 add_e MACRO n ; zvýšení hodnoty regitru E ld a, e add a, n ld e, a endm draw_sprite: ld hl, SPRITE_ADR ; adresa, od níž začíná maska spritu push de call draw_8_lines ; vykreslit prvních 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 push de call draw_8_lines ; vykreslit druhých 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 call draw_8_lines ; vykreslit třetích 8 řádků spritu ret ; návrat z podprogramu draw_8_lines: ld b, 8 ; počitadlo zapsaných řádků loop: ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc d ; posun na definici dalšího obrazového řádku dec e ; korekce - posun zpět pod první osmici pixelů dec e ; dtto djnz loop ; vnitřní smyčka: blok s 3x osmi zápisy ret ; návrat z podprogramu SPRITE_ADR include "sprite.asm" end ENTRY_POINT
Podívejme se nyní, jak se direktiva include projevila na výsledném binárním kódu vygenerovaném assemblerem:
SCREEN_ADR EQU 4000 ENTRY_POINT EQU 8000 ORG 8000 8000: label start 8000:060F LD B, 0F 8002:0E03 LD C, 03 8004:CD1680 CALL 8016 8007:CD2580 CALL 8025 800A:061D LD B, 1D 800C:0E15 LD C, 15 800E:CD1680 CALL 8016 8011:CD2580 CALL 8025 8014: label finish 8014:18FE JR 8014 8016: label calc_sprite_address 8016:79 LD A, C 8017:E607 AND 07 8019:0F RRCA 801A:0F RRCA 801B:0F RRCA 801C:B0 OR B 801D:5F LD E, A 801E:79 LD A, C 801F:E618 AND 18 8021:F640 OR 40 8023:57 LD D, A 8024:C9 RET Defining MACRO add_e Params: n 8025: label draw_sprite 8025:215180 LD HL, 8051 8028:D5 PUSH DE 8029:CD3E80 CALL 803E 802C:D1 POP DE Expanding MACRO add_e n= 0020 LD A , E 802D:7B LD A, E ADD A , n 802E:C620 ADD A, 20 LD E , A 8030:5F LD E, A ENDM ENDM End of MACRO add_e 8031:D5 PUSH DE 8032:CD3E80 CALL 803E 8035:D1 POP DE Expanding MACRO add_e n= 0020 LD A , E 8036:7B LD A, E ADD A , n 8037:C620 ADD A, 20 LD E , A 8039:5F LD E, A ENDM ENDM End of MACRO add_e 803A:CD3E80 CALL 803E 803D:C9 RET 803E: label draw_8_lines 803E:0608 LD B, 08 8040: label loop 8040:7E LD A, (HL) 8041:12 LD (DE), A 8042:23 INC HL 8043:1C INC E 8044:7E LD A, (HL) 8045:12 LD (DE), A 8046:23 INC HL 8047:1C INC E 8048:7E LD A, (HL) 8049:12 LD (DE), A 804A:23 INC HL 804B:14 INC D 804C:1D DEC E 804D:1D DEC E 804E:10F0 DJNZ 8040 8050:C9 RET 8051: label SPRITE_ADR INCLUDE sprite.asm 8051:000000 DEFB of 3 bytes 8054:000000 DEFB of 3 bytes 8057:01F010 DEFB of 3 bytes 805A:033810 DEFB of 3 bytes 805D:05D710 DEFB of 3 bytes 8060:05CC10 DEFB of 3 bytes 8063:053010 DEFB of 3 bytes 8066:04C810 DEFB of 3 bytes 8069:073610 DEFB of 3 bytes 806C:0CFE38 DEFB of 3 bytes 806F:1FF800 DEFB of 3 bytes 8072:000030 DEFB of 3 bytes 8075:03FFB0 DEFB of 3 bytes 8078:05FEE0 DEFB of 3 bytes 807B:0EFDC0 DEFB of 3 bytes 807E:18FC00 DEFB of 3 bytes 8081:180000 DEFB of 3 bytes 8084:01F800 DEFB of 3 bytes 8087:03FC00 DEFB of 3 bytes 808A:01B000 DEFB of 3 bytes 808D:020C00 DEFB of 3 bytes 8090:070E00 DEFB of 3 bytes 8093:1E0780 DEFB of 3 bytes 8096:000000 DEFB of 3 bytes End of INCLUDE 8099: END 8000 Emiting TAP basic loader Emiting TAP from 8000 to 8098
11. Načtení spritu z binárního souboru v době překladu
V deváté kapitole jsme si ukázali, jak je možné direktivou include do překládaného zdrojového souboru vložit v době překladu obsah jiného souboru, který v našem případě obsahuje definici spritu. Ovšem binární data ve skutečnosti nemusíme rozepisovat do sekvence direktiv db či defb, protože můžeme assembleru říci, že na aktuální pozici ve výsledném obrázku magnetické pásky má vložit obsah zvoleného souboru (tedy zkopírovat ho bit po bitu). Pro tento účel se používá direktiva incbin a zapisuje se takto:
SPRITE_ADR incbin "sprite.bin"
Vkládaný binární soubor má délku přesně 72 bajtů a obsahuje pochopitelně masku spritu 24×24 pixelů (tedy 3×24 bajtů). Tento soubor nalezneme na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/sprite.bin.
Na obrazovce bychom opět neměli poznat žádný rozdíl, protože výsledek je prakticky shodný (až na horní řádek) s předchozími dvěma screenshoty:
Obrázek 10: Dvojice spritů, z nichž každý má rozměry 24×24 pixelů.
12. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu
Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu naleznete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/123–24×24-sprite.asm. Kód vypadá následovně:
SCREEN_ADR equ $4000 ENTRY_POINT equ $8000 org ENTRY_POINT start: ld b, 15 ; x-ová souřadnice ld c, 3 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy spritu call draw_sprite ld b, 29 ; x-ová souřadnice ld c, 21 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy call draw_sprite finish: jr finish ; žá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 add_e MACRO n ; zvýšení hodnoty regitru E ld a, e add a, n ld e, a endm draw_sprite: ld hl, SPRITE_ADR ; adresa, od níž začíná maska spritu push de call draw_8_lines ; vykreslit prvních 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 push de call draw_8_lines ; vykreslit druhých 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 call draw_8_lines ; vykreslit třetích 8 řádků spritu ret ; návrat z podprogramu draw_8_lines: ld b, 8 ; počitadlo zapsaných řádků loop: ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc d ; posun na definici dalšího obrazového řádku dec e ; korekce - posun zpět pod první osmici pixelů dec e ; dtto djnz loop ; vnitřní smyčka: blok s 3x osmi zápisy ret ; návrat z podprogramu SPRITE_ADR incbin "sprite.bin" end ENTRY_POINT
SCREEN_ADR EQU 4000 ENTRY_POINT EQU 8000 ORG 8000 8000: label start 8000:060F LD B, 0F 8002:0E03 LD C, 03 8004:CD1680 CALL 8016 8007:CD2580 CALL 8025 800A:061D LD B, 1D 800C:0E15 LD C, 15 800E:CD1680 CALL 8016 8011:CD2580 CALL 8025 8014: label finish 8014:18FE JR 8014 8016: label calc_sprite_address 8016:79 LD A, C 8017:E607 AND 07 8019:0F RRCA 801A:0F RRCA 801B:0F RRCA 801C:B0 OR B 801D:5F LD E, A 801E:79 LD A, C 801F:E618 AND 18 8021:F640 OR 40 8023:57 LD D, A 8024:C9 RET Defining MACRO add_e Params: n 8025: label draw_sprite 8025:215180 LD HL, 8051 8028:D5 PUSH DE 8029:CD3E80 CALL 803E 802C:D1 POP DE Expanding MACRO add_e n= 0020 LD A , E 802D:7B LD A, E ADD A , n 802E:C620 ADD A, 20 LD E , A 8030:5F LD E, A ENDM ENDM End of MACRO add_e 8031:D5 PUSH DE 8032:CD3E80 CALL 803E 8035:D1 POP DE Expanding MACRO add_e n= 0020 LD A , E 8036:7B LD A, E ADD A , n 8037:C620 ADD A, 20 LD E , A 8039:5F LD E, A ENDM ENDM End of MACRO add_e 803A:CD3E80 CALL 803E 803D:C9 RET 803E: label draw_8_lines 803E:0608 LD B, 08 8040: label loop 8040:7E LD A, (HL) 8041:12 LD (DE), A 8042:23 INC HL 8043:1C INC E 8044:7E LD A, (HL) 8045:12 LD (DE), A 8046:23 INC HL 8047:1C INC E 8048:7E LD A, (HL) 8049:12 LD (DE), A 804A:23 INC HL 804B:14 INC D 804C:1D DEC E 804D:1D DEC E 804E:10F0 DJNZ 8040 8050:C9 RET 8051: label SPRITE_ADR INCBIN sprite.bin 8099: END 8000 Emiting TAP basic loader Emiting TAP from 8000 to 8098
13. Je mezi oběma způsoby načtení spritu v době překladu nějaký rozdíl?
Pokud se pozorně podíváme na výpisy prováděné assemblerem pro demonstrační příklady ukázané v kapitolách 10 a 12, snadno zjistíme, že délky výsledných souborů s obrazem magnetické pásky jsou naprosto stejné, i když způsob vložení binárního obrazu spritu je odlišný (vložení textového zdrojového kódu s direktivami DB versus vložení binární masky spritu). Pro porovnání se postačuje podívat na posledních několik řádků získaných z obou výpisů:
End of INCLUDE 8099: END 8000 Emiting TAP basic loader Emiting TAP from 8000 to 8098
a:
8051: label SPRITE_ADR INCBIN sprite.bin 8099: END 8000 Emiting TAP basic loader Emiting TAP from 8000 to 8098
Podívejme se nyní na to, jak se liší binární obrazy magnetických pásek, které se do ZX Spectra (či do jeho emulátoru) načítají BASICovským příkazem LOAD. Oba binární soubory s koncovkou .tap si můžeme převést na hexadecimální výpis, který se bude snáze porovnávat. Použijeme k tomu například standardní nástroj od.
Nejprve provedeme překlad obou příkladů, což je triviální:
$ make 122.tap $ make 123.tap
Následně provedeme převod obrazů pásek do hexadecimálního tvaru:
$ od -t x1 122.tap 0000000 13 00 00 00 6c 6f 61 64 65 72 20 20 20 20 47 00 0000020 0a 00 47 00 1b 49 00 ff 00 0a 0d 00 fd 33 32 37 0000040 36 37 0e 00 00 ff 7f 00 0d 00 14 17 00 f4 32 33 0000060 36 31 30 0e 00 00 3a 5c 00 2c 32 35 35 0e 00 00 0000100 ff 00 00 0d 00 1e 05 00 ef 22 22 af 0d 00 28 0e 0000120 00 f9 c0 33 32 37 36 38 0e 00 00 00 80 00 0d 08 0000140 13 00 00 03 31 32 32 2e 74 61 70 20 20 20 99 00 0000160 00 80 00 80 c0 9b 00 ff 06 0f 0e 03 cd 16 80 cd 0000200 25 80 06 1d 0e 15 cd 16 80 cd 25 80 18 fe 79 e6 0000220 07 0f 0f 0f b0 5f 79 e6 18 f6 40 57 c9 21 51 80 0000240 d5 cd 3e 80 d1 7b c6 20 5f d5 cd 3e 80 d1 7b c6 0000260 20 5f cd 3e 80 c9 06 08 7e 12 23 1c 7e 12 23 1c 0000300 7e 12 23 14 1d 1d 10 f0 c9 00 00 00 00 00 00 01 0000320 f0 10 03 38 10 05 d7 10 05 cc 10 05 30 10 04 c8 0000340 10 07 36 10 0c fe 38 1f f8 00 00 00 30 03 ff b0 0000360 05 fe e0 0e fd c0 18 fc 00 18 00 00 01 f8 00 03 0000400 fc 00 01 b0 00 02 0c 00 07 0e 00 1e 07 80 00 00 0000420 00 59 0000422
a:
$ od -t x1 123.tap 0000000 13 00 00 00 6c 6f 61 64 65 72 20 20 20 20 47 00 0000020 0a 00 47 00 1b 49 00 ff 00 0a 0d 00 fd 33 32 37 0000040 36 37 0e 00 00 ff 7f 00 0d 00 14 17 00 f4 32 33 0000060 36 31 30 0e 00 00 3a 5c 00 2c 32 35 35 0e 00 00 0000100 ff 00 00 0d 00 1e 05 00 ef 22 22 af 0d 00 28 0e 0000120 00 f9 c0 33 32 37 36 38 0e 00 00 00 80 00 0d 08 0000140 13 00 00 03 31 32 33 2e 74 61 70 20 20 20 99 00 0000160 00 80 00 80 c1 9b 00 ff 06 0f 0e 03 cd 16 80 cd 0000200 25 80 06 1d 0e 15 cd 16 80 cd 25 80 18 fe 79 e6 0000220 07 0f 0f 0f b0 5f 79 e6 18 f6 40 57 c9 21 51 80 0000240 d5 cd 3e 80 d1 7b c6 20 5f d5 cd 3e 80 d1 7b c6 0000260 20 5f cd 3e 80 c9 06 08 7e 12 23 1c 7e 12 23 1c 0000300 7e 12 23 14 1d 1d 10 f0 c9 00 00 00 00 00 00 01 0000320 f0 10 03 38 10 05 d7 10 05 cc 10 05 30 10 04 c8 0000340 10 07 36 10 0c fe 38 1f f8 00 00 00 30 03 ff b0 0000360 05 fe e0 0e fd c0 18 fc 00 18 00 00 01 f8 00 03 0000400 fc 00 01 b0 00 02 0c 00 07 0e 00 1e 07 80 00 00 0000420 00 59 0000422
Oba takto vzniklé soubory lze snadno porovnat:
$ od -t x1 122.tap >122.hex $ od -t x1 123.tap >123.hex
následováno příkazem:
$ diff -y 122.hex 123.hex
Výsledné soubory se liší v pouhých dvou bajtech:
0000000 13 00 00 00 6c 6f 61 64 65 72 20 20 20 20 47 00 0000000 13 00 00 00 6c 6f 61 64 65 72 20 20 20 20 47 00 0000020 0a 00 47 00 1b 49 00 ff 00 0a 0d 00 fd 33 32 37 0000020 0a 00 47 00 1b 49 00 ff 00 0a 0d 00 fd 33 32 37 0000040 36 37 0e 00 00 ff 7f 00 0d 00 14 17 00 f4 32 33 0000040 36 37 0e 00 00 ff 7f 00 0d 00 14 17 00 f4 32 33 0000060 36 31 30 0e 00 00 3a 5c 00 2c 32 35 35 0e 00 00 0000060 36 31 30 0e 00 00 3a 5c 00 2c 32 35 35 0e 00 00 0000100 ff 00 00 0d 00 1e 05 00 ef 22 22 af 0d 00 28 0e 0000100 ff 00 00 0d 00 1e 05 00 ef 22 22 af 0d 00 28 0e 0000120 00 f9 c0 33 32 37 36 38 0e 00 00 00 80 00 0d 08 0000120 00 f9 c0 33 32 37 36 38 0e 00 00 00 80 00 0d 08 0000140 13 00 00 03 31 32*32*2e 74 61 70 20 20 20 99 00 | 0000140 13 00 00 03 31 32*33*2e 74 61 70 20 20 20 99 00 0000160 00 80 00 80*c0*9b 00 ff 06 0f 0e 03 cd 16 80 cd | 0000160 00 80 00 80*c1*9b 00 ff 06 0f 0e 03 cd 16 80 cd 0000200 25 80 06 1d 0e 15 cd 16 80 cd 25 80 18 fe 79 e6 0000200 25 80 06 1d 0e 15 cd 16 80 cd 25 80 18 fe 79 e6 0000220 07 0f 0f 0f b0 5f 79 e6 18 f6 40 57 c9 21 51 80 0000220 07 0f 0f 0f b0 5f 79 e6 18 f6 40 57 c9 21 51 80 0000240 d5 cd 3e 80 d1 7b c6 20 5f d5 cd 3e 80 d1 7b c6 0000240 d5 cd 3e 80 d1 7b c6 20 5f d5 cd 3e 80 d1 7b c6 0000260 20 5f cd 3e 80 c9 06 08 7e 12 23 1c 7e 12 23 1c 0000260 20 5f cd 3e 80 c9 06 08 7e 12 23 1c 7e 12 23 1c 0000300 7e 12 23 14 1d 1d 10 f0 c9 00 00 00 00 00 00 01 0000300 7e 12 23 14 1d 1d 10 f0 c9 00 00 00 00 00 00 01 0000320 f0 10 03 38 10 05 d7 10 05 cc 10 05 30 10 04 c8 0000320 f0 10 03 38 10 05 d7 10 05 cc 10 05 30 10 04 c8 0000340 10 07 36 10 0c fe 38 1f f8 00 00 00 30 03 ff b0 0000340 10 07 36 10 0c fe 38 1f f8 00 00 00 30 03 ff b0 0000360 05 fe e0 0e fd c0 18 fc 00 18 00 00 01 f8 00 03 0000360 05 fe e0 0e fd c0 18 fc 00 18 00 00 01 f8 00 03 0000400 fc 00 01 b0 00 02 0c 00 07 0e 00 1e 07 80 00 00 0000400 fc 00 01 b0 00 02 0c 00 07 0e 00 1e 07 80 00 00 0000420 00 59 0000420 00 59 0000422 0000422
První rozdílný bajt souvisí s odlišným jménem bloku v hlavičce: „122.tap“ vs. „123.tap“, druhý bajt obsahuje kontrolní součet bloku a taktéž se tedy musí lišit o jedničku. Jinak jsou oba soubory totožné a v tomto ohledu není žádný rozdíl, jakým způsobem data se sprity, obrázky atd. do výsledného binárního souboru vkládáme.
14. Jemný posun spritu po jednotlivých pixelech
Ve druhé části dnešního článku si ukážeme jeden ze způsobů realizace jemného posunu spritu po jednotlivých pixelech. Začneme vertikálním posunem, protože ten je – i přes „prapodivnosti“ organizace paměti ZX Spectra – snadnější, protože nám do subrutiny pro výpočet počáteční adresy postačí vložit odlišnou hodnotu y-ové souřadnice. Ovšem jak realizovat jemný horizontální posun? Ten vyžaduje bitové posuny všech bajtů spritu, ovšem všechny „vysunuté“ bity se musí vsunout do navazujícího bajtu. Ukažme si to na našem příkladu.
Původní sprite vypadal následovně:
000000000000000000000000 000000000000000000000000 000000011111000000010000 000000110011100000010000 000001011101011100010000 000001011100110000010000 000001010011000000010000 000001001100100000010000 000001110011011000010000 000011001111111000111000 000111111111100000000000 000000000000000000110000 000000111111111110110000 000001011111111011100000 000011101111110111000000 000110001111110000000000 000110000000000000000000 000000011111100000000000 000000111111110000000000 000000011011000000000000 000000100000110000000000 000001110000111000000000 000111100000011110000000 000000000000000000000000
Jeho jemný posun doprava o jeden pixel vede k odlišné masce:
000000000000000000000000 000000000000000000000000 000000001111100000001000 000000011001110000001000 000000101110101110001000 000000101110011000001000 000000101001100000001000 000000100110010000001000 000000111001101100001000 000001100111111100011100 000011111111110000000000 000000000000000000011000 000000011111111111011000 000000101111111101110000 000001110111111011100000 000011000111111000000000 000011000000000000000000 000000001111110000000000 000000011111111000000000 000000001101100000000000 000000010000011000000000 000000111000011100000000 000011110000001111000000 000000000000000000000000
Na úrovni jednotlivých bajtů je pak rozdíl výraznější:
00000000 00000000 00000000 00000000 00000000 00000000 00000001 11110000 00010000 00000011 00111000 00010000 00000101 11010111 00010000 00000101 11001100 00010000 00000101 00110000 00010000 00000100 11001000 00010000 00000111 00110110 00010000 00001100 11111110 00111000 00011111 11111000 00000000 00000000 00000000 00110000 00000011 11111111 10110000 00000101 11111110 11100000 00001110 11111101 11000000 00011000 11111100 00000000 00011000 00000000 00000000 00000001 11111000 00000000 00000011 11111100 00000000 00000001 10110000 00000000 00000010 00001100 00000000 00000111 00001110 00000000 00011110 00000111 10000000 00000000 00000000 00000000
vs.:
00000000 00000000 00000000 00000000 00000000 00000000 00000000 11111000 00001000 00000001 10011100 00001000 00000010 11101011 10001000 00000010 11100110 00001000 00000010 10011000 00001000 00000010 01100100 00001000 00000011 10011011 00001000 00000110 01111111 00011100 00001111 11111100 00000000 00000000 00000000 00011000 00000001 11111111 11011000 00000010 11111111 01110000 00000111 01111110 11100000 00001100 01111110 00000000 00001100 00000000 00000000 00000000 11111100 00000000 00000001 11111110 00000000 00000000 11011000 00000000 00000001 00000110 00000000 00000011 10000111 00000000 00001111 00000011 11000000 00000000 00000000 00000000
Ve skutečnosti je však nutné celý sprite rozšířit o minimálně sedm sloupců, tedy o sedm bitů na každém řádku, aby se vysouvané „jedničkové“ bity neztratily. V praxi se samozřejmě provádí rozšíření o osm bitů a výsledek po posunu tedy bude vypadat následovně:
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 11111000 00001000 00000000 00000001 10011100 00001000 00000000 00000010 11101011 10001000 00000000 00000010 11100110 00001000 00000000 00000010 10011000 00001000 00000000 00000010 01100100 00001000 00000000 00000011 10011011 00001000 00000000 00000110 01111111 00011100 00000000 00001111 11111100 00000000 00000000 00000000 00000000 00011000 00000000 00000001 11111111 11011000 00000000 00000010 11111111 01110000 00000000 00000111 01111110 11100000 00000000 00001100 01111110 00000000 00000000 00001100 00000000 00000000 00000000 00000000 11111100 00000000 00000000 00000001 11111110 00000000 00000000 00000000 11011000 00000000 00000000 00000001 00000110 00000000 00000000 00000011 10000111 00000000 00000000 00001111 00000011 11000000 00000000 00000000 00000000 00000000 00000000
15. Hrubá výpočetní síla vs. předpočítání dat
Jemný posun spritu, tedy výpočet všech osmi možných pozic spritu (další pozice již vyžadují posun o celý bajt a jsou tedy triviální), můžeme realizovat mnoha různými způsoby. Mezi tyto způsoby patří i tato trojice technik:
- Předpočítáním všech možných pozic spritu (tedy všech masek) už v době překladu (a tedy na silném PC). Nevýhodou tohoto přístupu je fakt, že se zvětšuje velikost výsledného obrazu magnetické pásky, kterou je nutné nahrát do paměti RAM. A samozřejmě i celková volná kapacita RAM je na ZX Spectru (zejména na původním modelu s 48kB RAM) dosti podstatným způsobem omezena. Výhodou je, že tento způsob je z pohledu programátora nejjednodušší.
- Předpočítáním všech možných pozic spritu po spuštění programu (inicializační část hry). Velikost obrazu magnetické pásky se zmenší, což vede k rychlejšímu nahrání celé hry či jiné aplikace do RAM. Ovšem nároky na RAM jsou stejné, jako v předchozím případě. Realizace je nepatrně složitější, neboť ji budeme muset naprogramovat v assembleru mikroprocesoru Zilog Z80.
- Počítáním masky spritu ve všech požadovaných horizontálních pozicích v době běhu hry, tedy při jeho vlastním vykreslování. Obsazení RAM je nejmenší, ovšem samotná vykreslovací rutina musí být naprogramována tak, aby byly bitové posuny prováděny co nejrychleji.
16. Pomocný nástroj pro předpočítání jemného posunu spritu po jednotlivých pixelech
Pro výpočet všech osmi možných horizontálních pozic spritu definovaného bitovou maskou byl vytvořen následující pomocný program napsaný v céčku, jehož úplný zdrojový kód naleznete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/tools/scroll_sprite.c. Po překladu programu příkazem:
$ gcc -std=c99 -pedantic -Wall scroll_sprite.c -o scroll_sprite
se program používá následovně:
$ ./scroll_sprite [bajtů_na_řádku] [výška_v_pixelech] [input.bin] [output]
kde se za [input.bin] napíše jméno vstupního souboru s maskou spritu. V našem konkrétním případě použijeme tento příkaz:
$ ./scroll_sprite 3 24 sprite.bin sprites
Výsledkem bude osm souborů sprites_X.bin a osm souborů sprites_X.asm (kde se za X doplňuje hodnota 0 až 7 s informací o horizontálním posunu). První osmice souborů obsahuje binární masky spritů (velikost 96 bajtů) a druhá osmice masku zapsanou direktivou DB.
Zdrojový kód tohoto nástroje vypadá následovně:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define OK 0 #define ERROR 1 #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 #define MAX_FILENAME_LENGTH 200 int parse_int_parameter(char *input) { int output; char *end; errno = 0; output = strtol(input, &end, 10); /* Check for various possible errors */ if (errno != 0) { perror("strtol"); exit(EXIT_FAILURE); } /* Check if at least one digit was found */ if (input == end) { printf("Invalid input value: %s\n", input); exit(EXIT_FAILURE); } return output; } unsigned char **allocate_sprite(int bytes_per_line, int height) { int i; /* allocate row pointers */ unsigned char **sprite = (unsigned char **) calloc(sizeof(unsigned char *), height); /* allocate rows */ for (i = 0; i < height; i++) { sprite[i] = (unsigned char *) calloc(sizeof(unsigned char), bytes_per_line + 1); } return sprite; } /* Read binary file with sprite data: a true bitmap. */ int read_input_sprite_file(int bytes_per_line, int height, char *input_file, unsigned char **sprite) { FILE *fin; int i, j; fin = fopen(input_file, "rb"); for (j = 0; j < height; j++) { for (i = 0; i < bytes_per_line; i++) { int byte = fgetc(fin); if (byte == EOF) { perror("fgetc"); fclose(fin); return ERROR; } sprite[j][i] = byte; } /* last byte on row */ sprite[j][bytes_per_line] = 0x00; } fclose(fin); return OK; } /* Write binary file with sprite data: a true bitmap. */ int write_output_sprite_file(int bytes_per_line, int height, char *output_file, unsigned char **sprite, int shift) { FILE *fout; int i, j; char filename[MAX_FILENAME_LENGTH]; snprintf(filename, MAX_FILENAME_LENGTH, "%s_%d.bin", output_file, shift); fout = fopen(filename, "wb"); for (j = 0; j < height; j++) { for (i = 0; i < bytes_per_line; i++) { int written = fputc(sprite[j][i], fout); if (written == EOF) { perror("fputc"); fclose(fout); return ERROR; } } } fclose(fout); return OK; } int write_assembly_include_file(int bytes_per_line, int height, char *output_file, unsigned char **sprite, int shift) { FILE *fout; int i, j; char filename[MAX_FILENAME_LENGTH]; snprintf(filename, MAX_FILENAME_LENGTH, "%s_%d.asm", output_file, shift); fout = fopen(filename, "wt"); fprintf(fout, "SPRITE_SHIFT_%d:\n", shift); for (j = 0; j < height; j++) { fprintf(fout, "\tdb"); for (i = 0; i < bytes_per_line; i++) { fprintf(fout, " 0x%02x", sprite[j][i]); if (i != bytes_per_line-1) { fputs(", ", fout); } } fputc('\n', fout); } fclose(fout); return OK; } /* Shift sprite right by one pixel. */ void shift_sprite(int bytes_per_line, int height, unsigned char **sprite) { int i, j; for (j = 0; j < height; j++) { for (i = bytes_per_line-1; i >= 0; i--) { unsigned char byte = sprite[j][i]; byte >>= 1; if (i != 0) { /* check lowest bit of previous byte */ if ((sprite[j][i-1] & 0x01) == 0x01) { byte |= 0x80; } } sprite[j][i] = byte; } } } void perform_conversion(int bytes_per_line, int height, char *input_file, char *output_file) { unsigned char **sprite = allocate_sprite(bytes_per_line, height); int shift; if (read_input_sprite_file(bytes_per_line, height, input_file, sprite) != OK) { exit(EXIT_FAILURE); } for (shift = 0; shift < 8; shift++) { if (write_output_sprite_file(bytes_per_line + 1, height, output_file, sprite, shift) != OK) { exit(EXIT_FAILURE); } if (write_assembly_include_file(bytes_per_line + 1, height, output_file, sprite, shift) != OK) { exit(EXIT_FAILURE); } shift_sprite(bytes_per_line + 1, height, sprite); } } int main(int argc, char *argv[]) { int bytes_per_line; int height; /* check CLI arguments */ if (argc != 5) { printf("Usage: %s [bytes_per_line] [height] [input.bin] [output]", argv[0]); exit(EXIT_FAILURE); } bytes_per_line = parse_int_parameter(argv[1]); height = parse_int_parameter(argv[2]); printf("Bytes per line: %d\n", bytes_per_line); printf("Height: %d\n", height); perform_conversion(bytes_per_line, height, argv[3], argv[4]); return EXIT_SUCCESS; }
17. Zobrazení spritu s jemným posunem po obrazovce
Jednotlivé binárně reprezentované masky spritů o velikosti 32×24 pixelů, které byly vygenerovány nástrojem z předchozí kapitoly, připojíme k obrazu magnetické pásky vytvářené assemblerem tímto jednoduchým způsobem:
SPRITE_ADR incbin "sprites.bin0" incbin "sprites.bin1" incbin "sprites.bin2" incbin "sprites.bin3" incbin "sprites.bin4" incbin "sprites.bin5" incbin "sprites.bin6" incbin "sprites.bin7"
Povšimněte si, že sice nemáme návěští pro každou jednotlivou masku, to ovšem není zapotřebí, protože adresu začátku každé masky dokážeme snadno vypočítat z jeho indexu:
SPRITE_ADR+(index*24*4)
Tento výpočet použijeme v makru draw_shifted_sprite, které nejdříve vypočte adresu masky spritu a posléze zavolá podprogram pro vykreslení spritu na obrazovku:
draw_shifted_sprite MACRO index ld hl, SPRITE_ADR+(index*24*4) ; adresa, od níž začíná maska spritu call draw_sprite endm
Samotný podprogram pro vykreslení spritu byl upraven tak, aby vykreslil sprite o velikosti 32×24 pixelů a nikoli pouze 24×24 pixelů:
draw_sprite: push de call draw_8_lines ; vykreslit prvních 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 push de call draw_8_lines ; vykreslit druhých 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 call draw_8_lines ; vykreslit třetích 8 řádků spritu ret ; návrat z podprogramu draw_8_lines: ld b, 8 ; počitadlo zapsaných řádků loop: ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc d ; posun na definici dalšího obrazového řádku dec e ; korekce - posun zpět pod první osmici pixelů dec e ; dtto dec e ; dtto djnz loop ; vnitřní smyčka: blok s 4x osmi zápisy ret ; návrat z podprogramu
Výsledek zobrazený na obrazovce ukazuje korektní výsledek:
Obrázek 11: Šestice spritů, z nichž každý je posunut o různý počet pixelů doprava.
A takto vypadá výsledný program:
SCREEN_ADR equ $4000 ENTRY_POINT equ $8000 org ENTRY_POINT draw_shifted_sprite MACRO index ld hl, SPRITE_ADR+(index*24*4) ; adresa, od níž začíná maska spritu call draw_sprite endm start: ld b, 0 ; x-ová souřadnice ld c, 2 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy spritu draw_shifted_sprite 0 ld b, 0 ; x-ová souřadnice ld c, 5 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy spritu draw_shifted_sprite 1 ld b, 0 ; x-ová souřadnice ld c, 9 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy draw_shifted_sprite 2 ld b, 0 ; x-ová souřadnice ld c, 12 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy draw_shifted_sprite 3 ld b, 0 ; x-ová souřadnice ld c, 16 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy draw_shifted_sprite 4 ld b, 0 ; x-ová souřadnice ld c, 19 ; y-ová souřadnice call calc_sprite_address ; výpočet adresy draw_shifted_sprite 5 finish: jr finish ; žá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 add_e MACRO n ; zvýšení hodnoty regitru E ld a, e add a, n ld e, a endm draw_sprite: push de call draw_8_lines ; vykreslit prvních 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 push de call draw_8_lines ; vykreslit druhých 8 řádků spritu pop de add_e 32 ; zvýšit E o hodnotu 32 call draw_8_lines ; vykreslit třetích 8 řádků spritu ret ; návrat z podprogramu draw_8_lines: ld b, 8 ; počitadlo zapsaných řádků loop: ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc e ld a,(hl) ; načtení jednoho bajtu z masky ld (de),a ; zápis hodnoty na adresu (DE) inc hl ; posun na další bajt masky inc d ; posun na definici dalšího obrazového řádku dec e ; korekce - posun zpět pod první osmici pixelů dec e ; dtto dec e ; dtto djnz loop ; vnitřní smyčka: blok s 4x osmi zápisy ret ; návrat z podprogramu SPRITE_ADR incbin "sprites.bin0" incbin "sprites.bin1" incbin "sprites.bin2" incbin "sprites.bin3" incbin "sprites.bin4" incbin "sprites.bin5" incbin "sprites.bin6" incbin "sprites.bin7" end ENTRY_POINT
Způsob překladu do strojového kódu:
SCREEN_ADR EQU 4000 ENTRY_POINT EQU 8000 ORG 8000 Defining MACRO draw_shifted_sprite Params: index 8000: label start 8000:0600 LD B, 00 8002:0E02 LD C, 02 8004:CD5080 CALL 8050 Expanding MACRO draw_shifted_sprite index= 0000 LD HL , SPRITE_ADR + ( index * 0018 * 0004 ) 8007:218D80 LD HL, 808D CALL draw_sprite 800A:CD5F80 CALL 805F ENDM ENDM End of MACRO draw_shifted_sprite 800D:0600 LD B, 00 800F:0E05 LD C, 05 8011:CD5080 CALL 8050 Expanding MACRO draw_shifted_sprite index= 0001 LD HL , SPRITE_ADR + ( index * 0018 * 0004 ) 8014:21ED80 LD HL, 80ED CALL draw_sprite 8017:CD5F80 CALL 805F ENDM ENDM End of MACRO draw_shifted_sprite 801A:0600 LD B, 00 801C:0E09 LD C, 09 801E:CD5080 CALL 8050 Expanding MACRO draw_shifted_sprite index= 0002 LD HL , SPRITE_ADR + ( index * 0018 * 0004 ) 8021:214D81 LD HL, 814D CALL draw_sprite 8024:CD5F80 CALL 805F ENDM ENDM End of MACRO draw_shifted_sprite 8027:0600 LD B, 00 8029:0E0C LD C, 0C 802B:CD5080 CALL 8050 Expanding MACRO draw_shifted_sprite index= 0003 LD HL , SPRITE_ADR + ( index * 0018 * 0004 ) 802E:21AD81 LD HL, 81AD CALL draw_sprite 8031:CD5F80 CALL 805F ENDM ENDM End of MACRO draw_shifted_sprite 8034:0600 LD B, 00 8036:0E10 LD C, 10 8038:CD5080 CALL 8050 Expanding MACRO draw_shifted_sprite index= 0004 LD HL , SPRITE_ADR + ( index * 0018 * 0004 ) 803B:210D82 LD HL, 820D CALL draw_sprite 803E:CD5F80 CALL 805F ENDM ENDM End of MACRO draw_shifted_sprite 8041:0600 LD B, 00 8043:0E13 LD C, 13 8045:CD5080 CALL 8050 Expanding MACRO draw_shifted_sprite index= 0005 LD HL , SPRITE_ADR + ( index * 0018 * 0004 ) 8048:216D82 LD HL, 826D CALL draw_sprite 804B:CD5F80 CALL 805F ENDM ENDM End of MACRO draw_shifted_sprite 804E: label finish 804E:18FE JR 804E 8050: label calc_sprite_address 8050:79 LD A, C 8051:E607 AND 07 8053:0F RRCA 8054:0F RRCA 8055:0F RRCA 8056:B0 OR B 8057:5F LD E, A 8058:79 LD A, C 8059:E618 AND 18 805B:F640 OR 40 805D:57 LD D, A 805E:C9 RET Defining MACRO add_e Params: n 805F: label draw_sprite 805F:D5 PUSH DE 8060:CD7580 CALL 8075 8063:D1 POP DE Expanding MACRO add_e n= 0020 LD A , E 8064:7B LD A, E ADD A , n 8065:C620 ADD A, 20 LD E , A 8067:5F LD E, A ENDM ENDM End of MACRO add_e 8068:D5 PUSH DE 8069:CD7580 CALL 8075 806C:D1 POP DE Expanding MACRO add_e n= 0020 LD A , E 806D:7B LD A, E ADD A , n 806E:C620 ADD A, 20 LD E , A 8070:5F LD E, A ENDM ENDM End of MACRO add_e 8071:CD7580 CALL 8075 8074:C9 RET 8075: label draw_8_lines 8075:0608 LD B, 08 8077: label loop 8077:7E LD A, (HL) 8078:12 LD (DE), A 8079:23 INC HL 807A:1C INC E 807B:7E LD A, (HL) 807C:12 LD (DE), A 807D:23 INC HL 807E:1C INC E 807F:7E LD A, (HL) 8080:12 LD (DE), A 8081:23 INC HL 8082:1C INC E 8083:7E LD A, (HL) 8084:12 LD (DE), A 8085:23 INC HL 8086:14 INC D 8087:1D DEC E 8088:1D DEC E 8089:1D DEC E 808A:10EB DJNZ 8077 808C:C9 RET 808D: label SPRITE_ADR INCBIN sprites.bin0 INCBIN sprites.bin1 INCBIN sprites.bin2 INCBIN sprites.bin3 INCBIN sprites.bin4 INCBIN sprites.bin5 INCBIN sprites.bin6 INCBIN sprites.bin7 838D: END 8000 Emiting TAP basic loader Emiting TAP from 8000 to 838C
18. Příloha: upravený soubor Makefile pro překlad demonstračních příkladů
Výše uvedené demonstrační příklady i příklady, které již byly popsány v předchozích šestnácti článcích [1] [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], je možné přeložit s využitím souboru Makefile, jehož aktuální verze vypadá následovně (pro překlad a slinkování je, jak je již v tomto seriálu zvykem, použit assembler Pasmo):
ASSEMBLER := pasmo all: 01.tap 02.tap 03.tap 04.tap 05.tap 06.tap 07.tap 08.tap 09.tap 10.tap \ 11.tap 12.tap 13.tap 14.tap 15.tap 16.tap 17.tap 18.tap 19.tap 20.tap \ 21.tap 22.tap 23.tap 24.tap 25.tap 26.tap 27.tap 28.tap 29.tap 30.tap \ 31.tap 32.tap 33.tap 34.tap 35.tap 36.tap 37.tap 38.tap 39.tap 40.tap \ 41.tap 42.tap 43.tap 44.tap 45.tap 46.tap 47.tap 48.tap 49.tap 50.tap \ 51.tap 52.tap 53.tap 54.tap 55.tap 56.tap 57.tap 58.tap 59.tap 60.tap \ 61.tap 62.tap 63.tap 64.tap 65.tap 66.tap 67.tap 68.tap 69.tap 70.tap \ 71.tap 72.tap 73.tap 74.tap 75.tap 76.tap 77.tap 78.tap 79.tap 80.tap \ 81.tap 82.tap 83.tap 84.tap 85.tap 86.tap 87.tap 88.tap 80.tap 90.tap \ 91.tap 92.tap 93.tap 94.tap 95.tap 96.tap 97.tap 98.tap 99.tap 100.tap \ 101.tap 102.tap 103.tap 104.tap 105.tap 106.tap 107.tap 108.tap 109.tap \ 110.tap 111.tap 112.tap 113.tap 114.tap 115.tap 116.tap 117.tap 118.tap \ 119.tap 120.tap 121.tap 122.tap 123.tap 124.tap 125.tap clean: rm -f *.tap .PHONY: all clean 01.tap: 01-color-attribute.asm $(ASSEMBLER) -v -d --tap $< $@ > 01-color-attribute.lst 02.tap: 02-blinking-attribute.asm $(ASSEMBLER) -v -d --tap $< $@ > 02-blinking-attribute.lst 03.tap: 03-symbolic-names.asm $(ASSEMBLER) -v -d --tap $< $@ > 03-symbolic-names.lst 04.tap: 04-operators.asm $(ASSEMBLER) -v -d --tap $< $@ > 04-operators.lst 05.tap: 05-better-symbols.asm $(ASSEMBLER) -v -d --tap $< $@ > 05-better-symbols.lst 06.tap: 06-tapbas-v1.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 06-tapbas-v1.lst 07.tap: 07-tapbas-v2.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 07-tapbas-v2.lst 08.tap: 08-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 08-loop.lst 09.tap: 09-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 09-loop.lst 10.tap: 10-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 10-loop.lst 11.tap: 11-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 11-loop.lst 12.tap: 12-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 12-loop.lst 13.tap: 13-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 13-loop.lst 14.tap: 14-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 14-loop.lst 15.tap: 15-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 15-loop.lst 16.tap: 16-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 16-loop.lst 17.tap: 17-loop.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 17-loop.lst 18.tap: 18-cls.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 18-cls.lst 19.tap: 19-print-char-call.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 19-print-char-call.lst 20.tap: 20-print-char-rst.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 20-print-char-rst.lst 21.tap: 21-print-char.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 21-print-char.lst 22.tap: 22-print-all-chars.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 22-print-all-chars.lst 23.tap: 23-print-all-chars.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 23-print-all-chars.lst 24.tap: 24-change-color.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 24-change-color.lst 25.tap: 25-change-flash.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 25-change-flash.lst 26.tap: 26-print-at.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 26-print-at.lst 27.tap: 27-print-string.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 27-print-string.lst 28.tap: 28-print-string.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 28-print-string.lst 29.tap: 29-print-colorized-string.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 29-print-colorized-string.lst 30.tap: 30-print-string-ROM.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 30-print-string-ROM.lst 31.tap: 31-attributes.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 31-attributes.lst 32.tap: 32-fill-in-vram.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 32-fill-in-vram.lst 33.tap: 33-fill-in-vram-no-ret.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 33-fill-in-vram-no-ret.lst 34.tap: 34-fill-in-vram-pattern.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 34-fill-in-vram-pattern.lst 35.tap: 35-slow-fill-in-vram.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 35-slow-fill-in-vram.lst 36.tap: 36-slow-fill-in-vram-no-ret.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 36-slow-fill-in-vram-no-ret.lst 37.tap: 37-fill-block.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 37-fill-block.lst 38.tap: 38-fill-block-with-pattern.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 38-fill-block-with-pattern.lst 39.tap: 39-fill-block-optimized.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 39-fill-block-optimized.lst 40.tap: 40-draw-char.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 40-draw-char.lst 41.tap: 41-draw-any-char.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 41-draw-any-char.lst 42.tap: 42-block-anywhere.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 42-block-anywhere.lst 43.tap: 43-block-anywhere-rrca.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 43-block-anywhere-rrca.lst 44.tap: 44-better-draw-char.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 44-better-draw-char.lst 45.tap: 45-even-better-draw-char.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 45-even-better-draw-char.lst 46.tap: 46-draw-char-at.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 46-draw-char-at.lst 47.tap: 47-draw-char-at-unrolled.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 47-draw-char-at-unrolled.lst 48.tap: 48-incorrect-print-string.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 48-incorrect-print-string.lst 49.tap: 49-correct-print-string.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 49-correct-print-string.lst 50.tap: 50-ascii-table.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 50-ascii-table.lst 51.tap: 51-plot-block.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 51-plot-block.lst 52.tap: 52-plot-pixel.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 52-plot-pixel.lst 53.tap: 53-plot-pixel.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 53-plot-pixel.lst 54.tap: 54-plot-pixel-on-background.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 54-plot-pixel-on-background.lst 55.tap: 55-plot-pixel-on-background.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 55-plot-pixel-on-background.lst 56.tap: 56-inverse-ascii-table.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 56-inverse-ascii-table.lst 57.tap: 57-plot-pixel-on-inverse-background.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 57-plot-pixel-on-inverse-background.lst 58.tap: 58-plot-inverse-pixel-on-inverse-background.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 58-plot-inverse-pixel-on-inverse-background.lst 59.tap: 59-configurable-ascii-table.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 59-configurable-ascii-table.lst 60.tap: 60-plot-over.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 60-plot-over.lst 61.tap: 61-print-number-A.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 61-print-number-A.lst 62.tap: 62-print-number-B.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 62-print-number-B.lst 63.tap: 63-print-number-C.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 63-print-number-C.lst 64.tap: 64-print-number-D.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 64-print-number-D.lst 65.tap: 65-more-numbers-A.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 65-more-numbers-A.lst 66.tap: 66-more-numbers-B.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 66-more-numbers-B.lst 67.tap: 67-print-flags-1.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 67-print-flags-1.lst 68.tap: 68-print-flags-2.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 68-print-flags-2.lst 69.tap: 69-print-flags-3.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 69-print-flags-3.lst 70.tap: 70-print-flags-4.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 70-print-flags-4.lst 71.tap: 71-print-flags-5.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 71-print-flags-5.lst 72.tap: 72-print-flags-6.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 72-print-flags-6.lst 73.tap: 73-print-flags-7.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 73-print-flags-7.lst 74.tap: 74-print-hex-number.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 74-print-hex-number.lst 75.tap: 75-print-hex-number.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 75-print-hex-number.lst 76.tap: 76-print-hex-numbers.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 76-print-hex-numbers.lst 77.tap: 77-add-hex-numbers.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 77-add-hex-numbers.lst 78.tap: 78-add-bcd-numbers.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 78-add-bcd-numbers.lst 79.tap: 79-print-hex-digit-jmp.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 79-print-hex-digit-jmp.lst 80.tap: 80-print-hex-digit-overflow.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 80-print-hex-digit-overflow.lst 81.tap: 81-print-hex-digit-daa.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 81-print-hex-digit-daa.lst 82.tap: 82-print-hex-numbers-daa.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 82-print-hex-numbers-daa.lst 83.tap: 83-print-fp-numbers.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 83-print-fp-numbers.lst 84.tap: 84-print-ascii-table.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 84-print-ascii-table.lst 85.tap: 85-copy-ascii-table.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 85-copy-ascii-table.lst 86.tap: 86-copy-ascii-table-B.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 86-copy-ascii-table-B.lst 87.tap: 87-copy-ascii-table-C.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 87-copy-ascii-table-C.lst 88.tap: 88-copy-ascii-table-D.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 88-copy-ascii-table-D.lst 89.tap: 89-copy-ascii-table-E.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 89-copy-ascii-table-E.lst 90.tap: 90-copy-ascii-table-F.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 90-copy-ascii-table-F.lst 91.tap: 91-copy-ascii-table-G.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 91-copy-ascii-table-G.lst 92.tap: 92-copy-ascii-table-H.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 92-copy-ascii-table-H.lst 93.tap: 93-copy-ascii-table-I.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 93-copy-ascii-table-I.lst 94.tap: 94-color-attribute.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 94-color-attribute.lst 95.tap: 95-keypress.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 95-keypress.lst 96.tap: 96-keypress-row.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 96-keypress-row.lst 97.tap: 97-keypress-all-rows.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 97-keypress-all-rows.lst 98.tap: 98-game-character.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 98-game-character.lst 99.tap: 99-game-character-2.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 99-game-character-2.lst 100.tap: 100-cursor-joystick.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 100-cursor-joystick.lst 101.tap: 101-sinclair-joystick.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 101-sinclair-joystick.lst 102.tap: 102-kempston-joystick.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 102-kempston-joystick.lst 103.tap: 103-beep.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 103-beep.lst 104.tap: 104-music-scale.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 104-music-scale.lst 105.tap: 105-direct-speaker.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 105-direct-speaker.lst 106.tap: 106-direct-speaker-di.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 106-direct-speaker-di.lst 107.tap: 107-direct-speaker-border.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 107-direct-speaker-border.lst 108.tap: 108-direct-speaker-border-di.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 108-direct-speaker-border-di.lst 109.tap: 109-ay-note-a.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 109-ay-note-a.lst 110.tap: 110-ay-note-a.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 110-ay-note-a.lst 111.tap: 111-ay-two-notes.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 111-ay-two-notes.lst 112.tap: 112-ay-noise.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 112-ay-noise.lst 113.tap: 113-ay-low-frequency.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 113-ay-low-frequency.lst 114.tap: 114-ay-high-frequency.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 114-ay-high-frequency.lst 115.tap: 115-ay-low-noise.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 115-ay-low-noise.lst 116.tap: 116-ay-envelope.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 116-ay-envelope.lst 117.tap: 117-ay-envelope.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 117-ay-envelope.lst 118.tap: 118-ay-envelope.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 118-ay-envelope.lst 119.tap: 119-noise-envelope.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 119-noise-envelope.lst 120.tap: 120-8x8-sprite.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 120-8x8-sprite.lst 121.tap: 121-24x24-sprite.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 121-24x24-sprite.lst 122.tap: 122-24x24-sprite.asm sprite.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 122-24x24-sprite.lst 123.tap: 123-24x24-sprite.asm sprite.bin $(ASSEMBLER) -v -d --tapbas $< $@ > 123-24x24-sprite.lst 124.tap: 124-shifted-sprite.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 124-shifted-sprite.lst 125.tap: 125-shifted-sprite.asm $(ASSEMBLER) -v -d --tapbas $< $@ > 125-shifted-sprite.lst
19. Repositář s demonstračními příklady
V tabulce zobrazené pod tímto odstavcem jsou uvedeny odkazy na všechny prozatím popsané demonstrační příklady určené pro překlad a spuštění na osmibitovém domácím mikropočítači ZX Spectrum (libovolný model či jeho klon), které jsou psány v assembleru mikroprocesoru Zilog Z80. Pro překlad těchto demonstračních příkladů je možné použít například assembler Pasmo (viz též úvodní článek):
# | Soubor | Stručný popis | Adresa |
---|---|---|---|
1 | 01-color-attribute.asm | modifikace jednoho barvového atributu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/01-color-attribute.asm |
2 | 02-blinking-attribute.asm | barvový atribut s nastavením bitů pro blikání a vyšší intenzitu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/02-blinking-attribute.asm |
3 | 03-symbolic-names.asm | symbolická jména v assembleru | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/03-symbolic-names.asm |
4 | 04-operators.asm | operátory a operace se symbolickými hodnotami | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/04-operators.asm |
5 | 05-better-symbols.asm | tradičnější symbolická jména | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/05-better-symbols.asm |
6 | 06-tapbas-v1.asm | vygenerování BASICovského loaderu (neúplný příklad) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/06-tapbas-v1.asm |
7 | 07-tapbas-v2.asm | vygenerování BASICovského loaderu (úplný příklad) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/07-tapbas-v2.asm |
8 | 08-loop.asm | jednoduchá počítaná programová smyčka: naivní varianta | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/08-loop.asm |
9 | 09-loop.asm | programová smyčka: zkrácení kódu pro vynulování použitých pracovních registrů | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/09-loop.asm |
10 | 10-loop.asm | programová smyčka: optimalizace skoku na konci smyčky (instrukce DJNZ) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/10-loop.asm |
11 | 11-loop.asm | programová smyčka: optimalizace využití pracovních registrů | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/11-loop.asm |
12 | 12-loop.asm | programová smyčka: použití pracovního registru IX | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/12-loop.asm |
13 | 13-loop.asm | programová smyčka: použití pracovního registru IY | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/13-loop.asm |
14 | 14-loop.asm | programová smyčka se šestnáctibitovým počitadlem, základní varianta | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/14-loop.asm |
15 | 15-loop.asm | programová smyčka se šestnáctibitovým počitadlem, vylepšená varianta | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/15-loop.asm |
16 | 16-loop.asm | použití relativního skoku a nikoli skoku absolutního | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/16-loop.asm |
17 | 17-loop.asm | programová smyčka: inc l namísto inc hl | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/17-loop.asm |
18 | 18-cls.asm | smazání obrazovky a otevření kanálu číslo 2 (screen) přes funkci v ROM | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/18-cls.asm |
19 | 19-print-char-call.asm | smazání obrazovky a výpis jednoho znaku na obrazovku přes funkci v ROM (použití instrukce CALL) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/19-print-char-call.asm |
20 | 20-print-char-rst.asm | smazání obrazovky a výpis jednoho znaku na obrazovku přes funkci v ROM (použití instrukce RST) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/20-print-char-rst.asm |
21 | 21-print-char.asm | pouze výpis jednoho znaku na obrazovku bez jejího smazání | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/21-print-char.asm |
22 | 22-print-all-chars.asm | výpis znakové sady znak po znaku (nekorektní verze příkladu) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/22-print-all-chars.asm |
23 | 23-print-all-chars.asm | výpis znakové sady znak po znaku (korektní verze příkladu) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/23-print-all-chars.asm |
24 | 24-change-color.asm | změna barvových atributů (popředí a pozadí) vypisovaných znaků | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/24-change-color.asm |
25 | 25-change-flash.asm | povolení či zákaz blikání vypisovaných znaků | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/25-change-flash.asm |
26 | 26-print-at.asm | výpis znaku či znaků na určené místo na obrazovce | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/26-print-at.asm |
27 | 27-print-string.asm | výpis celého řetězce explicitně zapsanou programovou smyčkou (základní varianta) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/27-print-string.asm |
28 | 28-print-string.asm | výpis celého řetězce explicitně zapsanou programovou smyčkou (vylepšená varianta) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/28-print-string.asm |
29 | 29-print-colorized-string.asm | výpis řetězce, který obsahuje i řídicí znaky pro změnu barvy atd. | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/29-print-colorized-string.asm |
30 | 30-print-string-ROM.asm | výpis řetězce s využitím služby/subrutiny uložené v ROM ZX Spectra | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/30-print-string-ROM.asm |
31 | 31-attributes.asm | modifikace atributů pro tisk řetězce subrutinou uloženou v ROM | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/31-attributes.asm |
32 | 32-fill-in-vram.asm | vyplnění celé bitmapy barvou popředí, návrat do systému | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/32-fill-in-vram.asm |
33 | 33-fill-in-vram-no-ret.asm | vyplnění celé bitmapy barvou popředí, bez návratu do systému | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/33-fill-in-vram-no-ret.asm |
34 | 34-fill-in-vram-pattern.asm | vyplnění celé bitmapy zvoleným vzorkem | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/34-fill-in-vram-pattern.asm |
35 | 35-slow-fill-in-vram.asm | pomalé vyplnění celé bitmapy, vizualizace struktury bitmapy | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/35-slow-fill-in-vram.asm |
36 | 36-slow-fill-in-vram-no-ret.asm | pomalé vyplnění celé bitmapy, vizualizace struktury bitmapy, bez návratu do systému | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/36-slow-fill-in-vram-no-ret.asm |
37 | 37-fill-block.asm | vykreslení bloku 8×8 pixelů | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/37-fill-block.asm |
38 | 38-fill-block-with-pattern.asm | vykreslení bloku 8×8 pixelů zvoleným vzorkem | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/38-fill-block-with-pattern.asm |
39 | 39-fill-block-optimized.asm | optimalizace předchozího příkladu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/39-fill-block-optimized.asm |
40 | 40-draw-char.asm | vykreslení znaku do levého horního rohu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/40-draw-char.asm |
41 | 41-draw-any-char.asm | podprogram pro vykreslení libovolně zvoleného znaku do levého horního rohu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/41-draw-any-char.asm |
42 | 42-block-anywhere.asm | podprogramy pro vykreslení bloku 8×8 pixelů kamkoli na obrazovku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/42-block-anywhere.asm |
43 | 43-block-anywhere-rrca.asm | podprogramy pro vykreslení bloku 8×8 pixelů kamkoli na obrazovku, vylepšená varianta | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/43-block-anywhere-rrca.asm |
44 | 44-better-draw-char.asm | vykreslení znaku v masce 8×8 pixelů, vylepšená varianta | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/44-better-draw-char.asm |
45 | 45-even-better-draw-char.asm | posun offsetu pro vykreslení dalšího znaku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/45-even-better-draw-char.asm |
46 | 46-draw-char-at.asm | vykreslení znaku v masce 8×8 pixelů kamkoli na obrazovku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/46-draw-char-at.asm |
47 | 47-draw-char-at-unrolled.asm | vykreslení znaku v masce 8×8 pixelů kamkoli na obrazovku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/47-draw-char-at-unrolled.asm |
48 | 48-incorrect-print-string.asm | tisk řetězce, nekorektní varianta | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/48-incorrect-print-string.asm |
49 | 49-correct-print-string.asm | tisk řetězce, korektní varianta | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/49-correct-print-string.asm |
50 | 50-ascii-table.asm | tisk několika bloků ASCII tabulky | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/50-ascii-table.asm |
51 | 51-plot-block.asm | vykreslení pixelu verze 1: zápis celého bajtu na pozici pixelu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/51-plot-block.asm |
52 | 52-plot-pixel.asm | vykreslení pixelu verze 2: korektní vykreslení jednoho pixelu, ovšem překreslení celého bajtu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/52-plot-pixel.asm |
53 | 53-plot-pixel.asm | vykreslení pixelu verze 3: vylepšená verze předchozího demonstračního příkladu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/53-plot-pixel.asm |
54 | 54-plot-pixel-on-background.asm | vykreslení pixelu vůči pozadí (nekorektní varianta) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/54-plot-pixel-on-background.asm |
55 | 55-plot-pixel-on-background.asm | vykreslení pixelu vůči pozadí (korektní varianta) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/55-plot-pixel-on-background.asm |
56 | 56-inverse-ascii-table.asm | vykreslení ASCII tabulky inverzní barvou (inkoust vs. papír) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/56-inverse-ascii-table.asm |
57 | 57-plot-pixel-on-inverse-background.asm | vykreslení pixelů barvou papíru proti inverzní ASCII tabulce | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/57-plot-pixel-on-inverse-background.asm |
58 | 58-plot-inverse-pixel-on-inverse-background.asm | vykreslení pixelů inverzní barvou proti inverzní ASCII tabulce | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm58-plot-inverse-pixel-on-inverse-background.asm/ |
59 | 59-configurable-ascii-table.asm | vykreslení ASCII tabulky buď přímo inkoustem nebo inverzně | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/59-configurable-ascii-table.asm |
60 | 60-plot-over.asm | přibližná implementace příkazu PLOT OVER | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/60-plot-over.asm |
61 | 61-print-number-A.asm | ukázka použití podprogramu pro tisk celého čísla | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/61-print-number-A.asm |
62 | 62-print-number-B.asm | pokus o vytištění záporných čísel | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/62-print-number-B.asm |
63 | 63-print-number-C.asm | tisk maximální podporované hodnoty 9999 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/63-print-number-C.asm |
64 | 64-print-number-D.asm | tisk vyšší než podporované hodnoty 10000 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/64-print-number-D.asm |
65 | 65-more-numbers-A.asm | vytištění číselné řady | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/65-more-numbers-A.asm |
66 | 66-more-numbers-B.asm | kombinace tisku celočíselných hodnot s dalšími subrutinami | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/66-more-numbers-B.asm |
67 | 67-print-flags-1.asm | příznakové bity po provedení celočíselné operace 1+2 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/67-print-flags-1.asm |
68 | 68-print-flags-2.asm | příznakové bity po provedení celočíselné operace 0+0 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/68-print-flags-2.asm |
69 | 69-print-flags-3.asm | příznakové bity po provedení operace 255+1 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/69-print-flags-3.asm |
70 | 70-print-flags-4.asm | příznakové bity po provedení operace 254+1 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/70-print-flags-4.asm |
71 | 71-print-flags-5.asm | příznakové bity po provedení operace 255+255 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/71-print-flags-5.asm |
72 | 72-print-flags-6.asm | výsledek operace 100+100, nastavení příznakových bitů | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/72-print-flags-6.asm |
73 | 73-print-flags-7.asm | výsledek operace 128+128, nastavení příznakových bitů | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/73-print-flags-7.asm |
74 | 74-print-hex-number.asm | tisk hexadecimálního čísla v rozsahu 0×00 až 0×ff (neoptimalizovaná varianta) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/74-print-hex-number.asm |
75 | 75-print-hex-number.asm | tisk hexadecimálního čísla v rozsahu 0×00 až 0×ff (optimalizovaná varianta) | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/75-print-hex-number.asm |
76 | 76-print-hex-numbers.asm | tisk několika hexadecimálních hodnot | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/76-print-hex-numbers.asm |
77 | 77-add-hex-numbers.asm | součet dvou osmibitových hexadecimálních hodnot s tiskem všech výsledků | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/77-add-hex-numbers.asm |
78 | 78-add-bcd-numbers.asm | součet dvou osmibitových BCD hodnot s tiskem všech výsledků | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/78-add-bcd-numbers.asm |
79 | 79-print-hex-digit-jmp.asm | tisk jedné hexadecimální cifry s využitím podmíněného skoku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/79-print-hex-digit-jmp.asm |
80 | 80-print-hex-digit-overflow.asm | otestování, jaký znak je vytištěn pro hodnoty větší než 15 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/80-print-hex-digit-overflow.asm |
81 | 81-print-hex-digit-daa.asm | tisk jedné hexadecimální cifry s využitím instrukce DAA | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/81-print-hex-digit-daa.asm |
82 | 82-print-hex-numbers-daa.asm | tisk série hexadecimálních hodnot s využitím instrukce DAA | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/82-print-hex-numbers-daa.asm |
83 | 83-print-fp-numbers.asm | tisk numerických hodnot reprezentovaných v systému plovoucí řádové tečky | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/83-print-fp-numbers.asm |
84 | 84-print-ascii-table.asm | tisk jednoho bloku s ASCII tabulkou | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/84-print-ascii-table.asm |
85 | 85-copy-ascii-table.asm | kopie bloku bajt po bajtu založená na naivní programové smyčce | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/85-copy-ascii-table.asm |
86 | 86-copy-ascii-table-B.asm | kopie bloku s využitím instrukce LDIR | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/86-copy-ascii-table-B.asm |
87 | 87-copy-ascii-table-C.asm | kopie bloku bajt po bajtu založená na programové smyčce a instrukci LDI | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/87-copy-ascii-table-C.asm |
88 | 88-copy-ascii-table-D.asm | rozbalení programové smyčky s instrukcí LDI | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/88-copy-ascii-table-D.asm |
89 | 89-copy-ascii-table-E.asm | korektní smyčka pro všechny možné velikosti bloků | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/89-copy-ascii-table-E.asm |
90 | 90-copy-ascii-table-F.asm | kostra programu, který pro kopii bloků (16 bajtů) využívá zásobník | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/90-copy-ascii-table-F.asm |
91 | 91-copy-ascii-table-G.asm | definice makra a několikeré použití (aplikace) tohoto makra | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/91-copy-ascii-table-G.asm |
92 | 92-copy-ascii-table-H.asm | opakování makra založené na REPT | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/92-copy-ascii-table-H.asm |
93 | 93-copy-ascii-table-I.asm | vícenásobná kopie části obrazovky | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/93-copy-ascii-table-I.asm |
94 | 94-color-attribute.asm | modifikace jednoho barvového atributu na obrazovce ZX Spectra | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/94-color-attribute.asm |
95 | 95-keypress.asm | detekce stisku jedné klávesy s vizualizací stisku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/95-keypress.asm |
96 | 96-keypress-row.asm | detekce stisku kláves v jednom fyzickém řádku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/96-keypress-row.asm |
97 | 97-keypress-all-rows.asm | detekce stisku všech kláves klávesnice ZX Spectra 48k | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/97-keypress-all-rows.asm |
98 | 98-game-character.asm | zajištění pohybu hráče v herní scéně s využitím klávesnice | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/98-game-character.asm |
99 | 99-game-character-2.asm | vylepšení předchozího demonstračního příkladu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/99-game-character-2.asm |
100 | 100-cursor-joystick.asm | zajištění pohybu hráče v herní scéně kurzorovým joystickem | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/100-cursor-joystick.asm |
101 | 101-sinclair-joystick.asm | zajištění pohybu hráče v herní scéně joystickem připojeným přes Interface 2 | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/101-sinclair-joystick.asm |
102 | 102-kempston-joystick.asm | zajištění pohybu hráče v herní scéně joystickem připojeným přes rozhraní Kempston | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/102-kempston-joystick.asm |
103 | 103-beep.asm | využití subrutiny v ROM s implementací příkazu BEEP | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/103-beep.asm |
104 | 104-music-scale.asm | přehrání celé stupnice | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/104-music-scale.asm |
105 | 105-direct-speaker.asm | přímé programové ovládání beeperu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/105-direct-speaker.asm |
106 | 106-direct-speaker-di.asm | zakázání přerušení při přehrávání | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/106-direct-speaker-di.asm |
107 | 107-direct-speaker-border.asm | vizualizace frekvence přehrávání změnou okraje v reálném čase | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/107-direct-speaker-border.asm |
108 | 108-direct-speaker-border-di.asm | vizualizace frekvence přehrávání změnou okraje v reálném čase | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/108-direct-speaker-border-di.asm |
109 | 109-ay-note-a.asm | přehrání komorního A na čipu AY-3–8912 na ZX Spectru | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/109-ay-note-a.asm |
110 | 110-ay-note-a.asm | přehrání komorního A na čipu AY-3–8912 na ZX Spectru | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/110-ay-note-a.asm |
111 | 111-ay-two-notes.asm | souběžné přehrání dvojice not | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/111-ay-two-notes.asm |
112 | 112-ay-noise.asm | vygenerování šumu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/112-ay-noise.asm |
113 | 113-ay-low-frequency.asm | čistý tón s nízkou frekvencí | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/113-ay-low-frequency.asm |
114 | 114-ay-high-frequency.asm | čistý tón s vysokou frekvencí | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/114-ay-high-frequency.asm |
115 | 115-ay-low-noise.asm | vygenerování šumu s nízkou frekvencí | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/115-ay-low-noise.asm |
116 | 116-ay-envelope.asm | použití obálky založené na trojúhelníkovém signálu | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/116-ay-envelope.asm |
117 | 117-ay-envelope.asm | použití obálky založené na trojúhelníkovém signálu s vysokou frekvencí | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/117-ay-envelope.asm |
118 | 118-ay-envelope.asm | použití obálky založené na trojúhelníkovém signálu s nízkou frekvencí | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/118-ay-envelope.asm |
119 | 119-noise-envelope.asm | použití obálky společně se šumem | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/119-noise-envelope.asm |
120 | 120–8×8-sprite.asm | vykreslení spritu o velikosti 8×8 pixelů na obrazovku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/120–8×8-sprite.asm |
121 | 121–24×24-sprite.asm | vykreslení spritu o velikosti 24×24 pixelů na obrazovku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/121–24×24-sprite.asm |
122 | 122–24×24-sprite.asm | načtení spritu o velikosti 24×24 pixelů z include souboru a jeho vykreslení na obrazovku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/122–24×24-sprite.asm |
123 | 123–24×24-sprite.asm | načtení spritu o velikosti 24×24 pixelů z binárního souboru a jeho vykreslení na obrazovku | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/123–24×24-sprite.asm |
124 | 124-shifted-sprite.asm | načtení spritů posunutých o 0 až 7 pixelů z binárních souborů | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/124-shifted-sprite.asm |
125 | 125-shifted-sprite.asm | načtení spritů posunutých o 0 až 7 pixelů z include souborů | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/125-shifted-sprite.asm |
126 | Makefile | Makefile pro překlad a slinkování všech demonstračních příkladů do podoby obrazu magnetické pásky | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/Makefile |
20. Odkazy na Internetu
- z80 standalone assembler
https://www.asm80.com/onepage/asmz80.html - The ZX BASIC Compiler
https://www.boriel.com/pages/the-zx-basic-compiler.html - Z80 Assembly programming for the ZX Spectrum
https://www.chibiakumas.com/z80/ZXSpectrum.php - 8-BIT SMACKDOWN! 65C02 vs. Z80: slithy VLOGS #6
https://www.youtube.com/watch?v=P1paVoFEvyc - Instrukce mikroprocesoru Z80
https://clrhome.org/table/ - Z80 instructions: adresní režimy atd.
https://jnz.dk/z80/instructions.html - Z80 Instruction Groups
https://jnz.dk/z80/instgroups.html - Elena, New programming language for the ZX Spectrum Next
https://vintageisthenewold.com/elena-new-programming-language-for-the-zx-spectrum-next/ - Sinclair BASIC
https://worldofspectrum.net/legacy-info/sinclair-basic/ - Grafika na osmibitových počítačích firmy Sinclair
https://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair/ - Grafika na osmibitových počítačích firmy Sinclair II
https://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/ - HiSoft BASIC
https://worldofspectrum.net/infoseekid.cgi?id=0008249 - YS MegaBasic
https://worldofspectrum.net/infoseekid.cgi?id=0008997 - Beta Basic
https://worldofspectrum.net/infoseekid.cgi?id=0007956 - BASIC+
https://worldofspectrum.net/infoseekid.php?id=0014277 - Spectrum ROM Memory Map
https://skoolkit.ca/disassemblies/rom/maps/all.html - Goto subroutine
https://skoolkit.ca/disassemblies/rom/asm/7783.html - Spectrum Next: The Evolution of the Speccy
https://www.specnext.com/about/ - Sedmdesátiny assemblerů: lidsky čitelný strojový kód
https://www.root.cz/clanky/sedmdesatiny-assembleru-lidsky-citelny-strojovy-kod/ - Programovací jazyk BASIC na osmibitových mikropočítačích
https://www.root.cz/clanky/programovaci-jazyk-basic-na-osmibitovych-mikropocitacich/ - Programovací jazyk BASIC na osmibitových mikropočítačích (2)
https://www.root.cz/clanky/programovaci-jazyk-basic-na-osmibitovych-mikropocitacich-2/#k06 - Programovací jazyk BASIC na osmibitových mikropočítačích (3)
https://www.root.cz/clanky/programovaci-jazyk-basic-na-osmibitovych-mikropocitacich-3/ - Sinclair BASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/Sinclair_BASIC - Assembly Language: Still Relevant Today
http://wilsonminesco.com/AssyDefense/ - Programovani v assembleru na OS Linux
http://www.cs.vsb.cz/grygarek/asm/asmlinux.html - Why Assembly Language Programming? (Why Learning Assembly Language Is Still a Good Idea)
https://wdc65×x.com/markets/education/why-assembly-language-programming/ - Low Fat Computing
http://www.ultratechnology.com/lowfat.htm - Assembly Language
https://www.cleverism.com/skills-and-tools/assembly-language/ - Why do we need assembly language?
https://cs.stackexchange.com/questions/13287/why-do-we-need-assembly-language - Assembly language (Wikipedia)
https://en.wikipedia.org/wiki/Assembly_language#Historical_perspective - Assembly languages
https://curlie.org/Computers/Programming/Languages/Assembly/ - vasm
http://sun.hasenbraten.de/vasm/ - B-ELITE
https://jsj.itch.io/b-elite - ZX-Spectrum Child
http://www.dotkam.com/2008/11/19/zx-spectrum-child/ - Speccy.cz
http://www.speccy.cz/ - Planet Sinclair
http://www.nvg.ntnu.no/sinclair/ - World of Spectrum
http://www.worldofspectrum.org/ - The system variables
https://worldofspectrum.org/ZXBasicManual/zxmanchap25.html - ZX Spectrum manual: chapter #17 Graphics
https://worldofspectrum.org/ZXBasicManual/zxmanchap17.html - Why does Sinclair BASIC have two formats for storing numbers in the same structure?
https://retrocomputing.stackexchange.com/questions/8834/why-does-sinclair-basic-have-two-formats-for-storing-numbers-in-the-same-structu - Plovoucí řádová čárka na ZX Spectru
https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/#k05 - Norma IEEE 754 a příbuzní: formáty plovoucí řádové tečky
https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/#k05 - 1A1B: THE ‚REPORT AND LINE NUMBER PRINTING‘ SUBROUTINE
https://skoolkid.github.io/rom/asm/1A1B.html - 2DE3: THE ‚PRINT A FLOATING-POINT NUMBER‘ SUBROUTINE
https://skoolkid.github.io/rom/asm/2DE3.html - 5C63: STKBOT – Address of bottom of calculator stack
https://skoolkid.github.io/rom/asm/5C63.html - 5C65: STKEND – Address of start of spare space
https://skoolkid.github.io/rom/asm/5C65.html - Why does Sinclair BASIC have two formats for storing numbers in the same structure?
https://retrocomputing.stackexchange.com/questions/8834/why-does-sinclair-basic-have-two-formats-for-storing-numbers-in-the-same-structu - Chapter 24: The memory
https://worldofspectrum.org/ZXBasicManual/zxmanchap24.html - Survey of Floating-Point Formats
https://mrob.com/pub/math/floatformats.html - Convert an 8bit number to hex in z80 assembler
https://stackoverflow.com/questions/22838444/convert-an-8bit-number-to-hex-in-z80-assembler - 80 MICROPROCESSOR Instruction Set Summary
http://www.textfiles.com/programming/CARDS/z80 - Extended Binary Coded Decimal Interchange Code
http://en.wikipedia.org/wiki/EBCDIC - ASCII/EBCDIC Conversion Table
http://docs.hp.com/en/32212–90008/apcs01.html - EBCDIC
http://www.hansenb.pdx.edu/DMKB/dict/tutorials/ebcdic.php - EBCDIC tables
http://home.mnet-online.de/wzwz.de/temp/ebcdic/cc_en.htm - The Mainframe Blog
http://mainframe.typepad.com/blog/2006/11/my_personal_mai.html - Binary-coded decimal
https://en.wikipedia.org/wiki/Binary-coded_decimal - BCD
https://cs.wikipedia.org/wiki/BCD - Z80 heaven: Floating Point
http://z80-heaven.wikidot.com/floating-point - Z80, the 8-bit Number Cruncher
http://www.andreadrian.de/oldcpu/Z80_number_cruncher.html - Floating-point library for Z80
https://github.com/DW0RKiN/Floating-point-Library-for-Z80 - z80float
https://github.com/Zeda/z80float - Fixed point arithmetic
https://www.root.cz/clanky/fixed-point-arithmetic/ - ZX Spectrum BASIC Programming – 2nd Edition
https://archive.org/details/zx-spectrum-basic-programming/page/n167/mode/2up - ZX Spectrum BASIC Programming – 2nd Edition
https://archive.org/details/zx-spectrum-basic-programming/page/n169/mode/2up - How fast is memcpy on the Z80?
https://retrocomputing.stackexchange.com/questions/4744/how-fast-is-memcpy-on-the-z80 - How do Z80 Block Transfer instructions work?
https://retrocomputing.stackexchange.com/questions/5416/how-do-z80-block-transfer-instructions-work - Retro Programming Made Simple: Keyboard
http://www.breakintoprogram.co.uk/hardware/computers/zx-spectrum/keyboard - How ZX Spectrum avoided key ghosting
https://retrocomputing.stackexchange.com/questions/16235/how-zx-spectrum-avoided-key-ghosting - ZX Spectrum Keyboard Visualized
http://www.kameli.net/marq/?p=2055 - Sinclair ZX Spectrum Joysticks Explained
https://www.retroisle.com/general/spectrum_joysticks.php - When A Single Bit Was Enough, Into The Sound Of The ZX Spectrum
https://hackaday.com/2022/01/20/when-a-single-bit-was-enough-into-the-sound-of-the-zx-spectrum/ - 03B5: THE ‚BEEPER‘ SUBROUTINE
https://skoolkid.github.io/rom/asm/03B5.html - How To Write ZX Spectrum Games – Chapter 3
https://chuntey.wordpress.com/2013/02/28/how-to-write-zx-spectrum-games-chapter-3/ - Understanding computer sound
https://www.youtube.com/playlist?list=PL0qES-IQZC8w4vqeQhxHxKgxYYqs3CEOx - Understanding Computer Sound. 5. ZX Spectrum
https://www.youtube.com/watch?v=N5ACJd2LvbY - Dark Fusion (Gremlin Graphics, 1988)
https://www.youtube.com/watch?v=ADL3mdRMzoA - Arkanoid Spectrum Title Music
https://www.youtube.com/watch?v=TymO0Lj7Vp8 - Tim Follin – „Chronos“ (ZX Spectrum) [Oscilloscope Visualization]
https://www.youtube.com/watch?v=yJy45MHrPjc - [60 FPS] Wally Beben – „Sanxion Loader“ [ZX Spectrum (Beeper)] (Oscilloscope View)
https://www.youtube.com/watch?v=JwMxOfQVl7A - Understanding Computer Sound
http://forgottencomputer.com/retro/sound/ - AY-3–8912
https://sinclair.wiki.zxnet.co.uk/wiki/AY-3–8912 - AY-3–8912
https://github.com/topics/ay-3–8912 - Z80 Assembly programming for the ZX Spectrum
https://www.chibiakumas.com/z80/ZXSpectrum.php?noui=1 - AY-3–8910
http://vgmpf.com/Wiki/index.php/AY-3–8910 - AY-3–8910/8912 Programmable sound generator data manual
https://f.rdw.se/AY-3–8910-datasheet.pdf - Sprite (computer graphics)
https://en.wikipedia.org/wiki/Sprite_(computer_graphics) - Piskel is a free online editor for animated sprites & pixel art
https://www.piskelapp.com/ - TAP format
https://sinclair.wiki.zxnet.co.uk/wiki/TAP_format