Hlavní navigace

Vykreslování spritů a animací na ZX Spectru

25. 7. 2023
Doba čtení: 67 minut

Sdílet

 Autor: Depositphotos
V prakticky každé hře se setkáme s pohybujícími se objekty. Vykreslování takových objektů na ZX Spectru je poměrně složité kvůli specifické organizaci grafické paměti a systému barvových atributů.

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

5. Definice většího spritu

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

20. Odkazy na Internetu

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ů:

  1. 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.
  2. 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í.
  3. Nejsou podporovány hardwarové sprity. Tím pádem nejsou podporovány ani detekce kolizí atd. (ale to již opouštíme oblast animací).
  4. 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.

Poznámka: navíc se seznámíme i s některými užitečnými vlastnostmi assembleru Pasmo, zejména s direktivami include a incbin.

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ů.

pc7101

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.

pc6904

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
Poznámka: samozřejmě již víme, že při požadavcích na co největší rychlost bude vhodnější kód rozbalit.

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
Poznámka: povšimněte si, že osm bajtů s definicí spritu bylo zapsáno do výsledného binárního souboru ihned za strojové instrukce. U osmibitových mikroprocesorů je tato kombinace dat s instrukcemi zcela běžná (už jen kvůli tomu, že neexistuje ochrana paměti).

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.

Mario

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.

Poznámka: ve skutečnosti většinou chceme, aby byl sprite „průhledný“ v těch místech, kde jsou bity jeho masky nulové. Tuto úpravu provedeme později.
Poznámka2: dokážete přijít na to, za jakých podmínek a zda vůbec NEbude vykreslovací rutina korektní?

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
Poznámka: opět platí, že se binární data pouze „přilepila“ za poslední strojovou instrukci.

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/pas­modoc.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/pas­modoc.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
Poznámka: můžeme vidět, že se do výsledného binárního souboru pouze na konec přidaly bajty s definicí tvaru spritu.

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.

Poznámka: tento způsob využijeme například pro vkládání větších obrázků, scén ve hře (bludišť) apod. do výsledného obrazu magnetické pásky.

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
Poznámka: nyní je „symbolický“ výstup nepatrně odlišný, protože obsah binárního souboru se ve výsledku neukazuje. Ovšem již rozdíly v adresách zobrazených v prvním sloupci (0×8099 versus 0×8051) ukazují, že se do výsledného binárního kódu s obrazem magnetické pásky vložilo 72 bajtů z binárního souboru sprite.bin.

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.

Poznámka: formát obrazu magnetické pásky je ve stručnosti popsán například zde.

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:

  1. 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šší.
  2. 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.
  3. 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.
Poznámka: v dnešním článku se prozatím zaměříme na první způsob, který je z pohledu programátora nejjednodušší, protože předpočet všech pozic spritu můžeme provést na PC, využít k realizaci posouvacího algoritmu vyšší programovací jazyk, používat ladicí nástroje atd. atd.

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:

CS24 tip temata

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

  1. z80 standalone assembler
    https://www.asm80.com/one­page/asmz80.html
  2. The ZX BASIC Compiler
    https://www.boriel.com/pages/the-zx-basic-compiler.html
  3. Z80 Assembly programming for the ZX Spectrum
    https://www.chibiakumas.com/z80/ZXSpec­trum.php
  4. 8-BIT SMACKDOWN! 65C02 vs. Z80: slithy VLOGS #6
    https://www.youtube.com/wat­ch?v=P1paVoFEvyc
  5. Instrukce mikroprocesoru Z80
    https://clrhome.org/table/
  6. Z80 instructions: adresní režimy atd.
    https://jnz.dk/z80/instructions.html
  7. Z80 Instruction Groups
    https://jnz.dk/z80/instgroups.html
  8. Elena, New programming language for the ZX Spectrum Next
    https://vintageisthenewold.com/elena-new-programming-language-for-the-zx-spectrum-next/
  9. Sinclair BASIC
    https://worldofspectrum.net/legacy-info/sinclair-basic/
  10. Grafika na osmibitových počítačích firmy Sinclair
    https://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair/
  11. Grafika na osmibitových počítačích firmy Sinclair II
    https://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/
  12. HiSoft BASIC
    https://worldofspectrum.net/in­foseekid.cgi?id=0008249
  13. YS MegaBasic
    https://worldofspectrum.net/in­foseekid.cgi?id=0008997
  14. Beta Basic
    https://worldofspectrum.net/in­foseekid.cgi?id=0007956
  15. BASIC+
    https://worldofspectrum.net/in­foseekid.php?id=0014277
  16. Spectrum ROM Memory Map
    https://skoolkit.ca/disas­semblies/rom/maps/all.html
  17. Goto subroutine
    https://skoolkit.ca/disas­semblies/rom/asm/7783.html
  18. Spectrum Next: The Evolution of the Speccy
    https://www.specnext.com/about/
  19. Sedmdesátiny assemblerů: lidsky čitelný strojový kód
    https://www.root.cz/clanky/sed­mdesatiny-assembleru-lidsky-citelny-strojovy-kod/
  20. Programovací jazyk BASIC na osmibitových mikropočítačích
    https://www.root.cz/clanky/pro­gramovaci-jazyk-basic-na-osmibitovych-mikropocitacich/
  21. Programovací jazyk BASIC na osmibitových mikropočítačích (2)
    https://www.root.cz/clanky/pro­gramovaci-jazyk-basic-na-osmibitovych-mikropocitacich-2/#k06
  22. Programovací jazyk BASIC na osmibitových mikropočítačích (3)
    https://www.root.cz/clanky/pro­gramovaci-jazyk-basic-na-osmibitovych-mikropocitacich-3/
  23. Sinclair BASIC (Wikipedia CZ)
    http://cs.wikipedia.org/wi­ki/Sinclair_BASIC
  24. Assembly Language: Still Relevant Today
    http://wilsonminesco.com/AssyDefense/
  25. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/gryga­rek/asm/asmlinux.html
  26. Why Assembly Language Programming? (Why Learning Assembly Language Is Still a Good Idea)
    https://wdc65×x.com/market­s/education/why-assembly-language-programming/
  27. Low Fat Computing
    http://www.ultratechnology­.com/lowfat.htm
  28. Assembly Language
    https://www.cleverism.com/skills-and-tools/assembly-language/
  29. Why do we need assembly language?
    https://cs.stackexchange.com/qu­estions/13287/why-do-we-need-assembly-language
  30. Assembly language (Wikipedia)
    https://en.wikipedia.org/wi­ki/Assembly_language#Histo­rical_perspective
  31. Assembly languages
    https://curlie.org/Computer­s/Programming/Languages/As­sembly/
  32. vasm
    http://sun.hasenbraten.de/vasm/
  33. B-ELITE
    https://jsj.itch.io/b-elite
  34. ZX-Spectrum Child
    http://www.dotkam.com/2008/11/19/zx-spectrum-child/
  35. Speccy.cz
    http://www.speccy.cz/
  36. Planet Sinclair
    http://www.nvg.ntnu.no/sinclair/
  37. World of Spectrum
    http://www.worldofspectrum.org/
  38. The system variables
    https://worldofspectrum.or­g/ZXBasicManual/zxmanchap25­.html
  39. ZX Spectrum manual: chapter #17 Graphics
    https://worldofspectrum.or­g/ZXBasicManual/zxmanchap17­.html
  40. Why does Sinclair BASIC have two formats for storing numbers in the same structure?
    https://retrocomputing.stac­kexchange.com/questions/8834/why-does-sinclair-basic-have-two-formats-for-storing-numbers-in-the-same-structu
  41. Plovoucí řádová čárka na ZX Spectru
    https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/#k05
  42. 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
  43. 1A1B: THE ‚REPORT AND LINE NUMBER PRINTING‘ SUBROUTINE
    https://skoolkid.github.i­o/rom/asm/1A1B.html
  44. 2DE3: THE ‚PRINT A FLOATING-POINT NUMBER‘ SUBROUTINE
    https://skoolkid.github.i­o/rom/asm/2DE3.html
  45. 5C63: STKBOT – Address of bottom of calculator stack
    https://skoolkid.github.i­o/rom/asm/5C63.html
  46. 5C65: STKEND – Address of start of spare space
    https://skoolkid.github.i­o/rom/asm/5C65.html
  47. Why does Sinclair BASIC have two formats for storing numbers in the same structure?
    https://retrocomputing.stac­kexchange.com/questions/8834/why-does-sinclair-basic-have-two-formats-for-storing-numbers-in-the-same-structu
  48. Chapter 24: The memory
    https://worldofspectrum.or­g/ZXBasicManual/zxmanchap24­.html
  49. Survey of Floating-Point Formats  
    https://mrob.com/pub/math/flo­atformats.html
  50. Convert an 8bit number to hex in z80 assembler
    https://stackoverflow.com/qu­estions/22838444/convert-an-8bit-number-to-hex-in-z80-assembler
  51. 80 MICROPROCESSOR Instruction Set Summary
    http://www.textfiles.com/pro­gramming/CARDS/z80
  52. Extended Binary Coded Decimal Interchange Code
    http://en.wikipedia.org/wiki/EBCDIC
  53. ASCII/EBCDIC Conversion Table
    http://docs.hp.com/en/32212–90008/apcs01.html
  54. EBCDIC
    http://www.hansenb.pdx.edu/DMKB/dic­t/tutorials/ebcdic.php
  55. EBCDIC tables
    http://home.mnet-online.de/wzwz.de/temp/eb­cdic/cc_en.htm
  56. The Mainframe Blog
    http://mainframe.typepad.com/blog/2006/11/my_per­sonal_mai.html
  57. Binary-coded decimal
    https://en.wikipedia.org/wiki/Binary-coded_decimal
  58. BCD
    https://cs.wikipedia.org/wiki/BCD
  59. Z80 heaven: Floating Point
    http://z80-heaven.wikidot.com/floating-point
  60. Z80, the 8-bit Number Cruncher
    http://www.andreadrian.de/ol­dcpu/Z80_number_cruncher.html
  61. Floating-point library for Z80
    https://github.com/DW0RKiN/Floating-point-Library-for-Z80
  62. z80float
    https://github.com/Zeda/z80float
  63. Fixed point arithmetic
    https://www.root.cz/clanky/fixed-point-arithmetic/
  64. ZX Spectrum BASIC Programming – 2nd Edition
    https://archive.org/details/zx-spectrum-basic-programming/page/n167/mode/2up
  65. ZX Spectrum BASIC Programming – 2nd Edition
    https://archive.org/details/zx-spectrum-basic-programming/page/n169/mode/2up
  66. How fast is memcpy on the Z80?
    https://retrocomputing.stac­kexchange.com/questions/4744/how-fast-is-memcpy-on-the-z80
  67. How do Z80 Block Transfer instructions work?
    https://retrocomputing.stac­kexchange.com/questions/5416/how-do-z80-block-transfer-instructions-work
  68. Retro Programming Made Simple: Keyboard
    http://www.breakintoprogram­.co.uk/hardware/computers/zx-spectrum/keyboard
  69. How ZX Spectrum avoided key ghosting
    https://retrocomputing.stac­kexchange.com/questions/16235/how-zx-spectrum-avoided-key-ghosting
  70. ZX Spectrum Keyboard Visualized
    http://www.kameli.net/marq/?p=2055
  71. Sinclair ZX Spectrum Joysticks Explained
    https://www.retroisle.com/ge­neral/spectrum_joysticks.php
  72. 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/
  73. 03B5: THE ‚BEEPER‘ SUBROUTINE
    https://skoolkid.github.i­o/rom/asm/03B5.html
  74. How To Write ZX Spectrum Games – Chapter 3
    https://chuntey.wordpress­.com/2013/02/28/how-to-write-zx-spectrum-games-chapter-3/
  75. Understanding computer sound
    https://www.youtube.com/pla­ylist?list=PL0qES-IQZC8w4vqeQhxHxKgxYYqs3CEOx
  76. Understanding Computer Sound. 5. ZX Spectrum
    https://www.youtube.com/wat­ch?v=N5ACJd2LvbY
  77. Dark Fusion (Gremlin Graphics, 1988)
    https://www.youtube.com/wat­ch?v=ADL3mdRMzoA
  78. Arkanoid Spectrum Title Music
    https://www.youtube.com/wat­ch?v=TymO0Lj7Vp8
  79. Tim Follin – „Chronos“ (ZX Spectrum) [Oscilloscope Visualization]
    https://www.youtube.com/wat­ch?v=yJy45MHrPjc
  80. [60 FPS] Wally Beben – „Sanxion Loader“ [ZX Spectrum (Beeper)] (Oscilloscope View)
    https://www.youtube.com/wat­ch?v=JwMxOfQVl7A
  81. Understanding Computer Sound
    http://forgottencomputer.com/re­tro/sound/
  82. AY-3–8912
    https://sinclair.wiki.zxnet­.co.uk/wiki/AY-3–8912
  83. AY-3–8912
    https://github.com/topics/ay-3–8912
  84. Z80 Assembly programming for the ZX Spectrum
    https://www.chibiakumas.com/z80/ZXSpec­trum.php?noui=1
  85. AY-3–8910
    http://vgmpf.com/Wiki/index.php/AY-3–8910
  86. AY-3–8910/8912 Programmable sound generator data manual
    https://f.rdw.se/AY-3–8910-datasheet.pdf
  87. Sprite (computer graphics)
    https://en.wikipedia.org/wi­ki/Sprite_(computer_graphic­s)
  88. Piskel is a free online editor for animated sprites & pixel art
    https://www.piskelapp.com/
  89. TAP format
    https://sinclair.wiki.zxnet­.co.uk/wiki/TAP_format

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.