Kopie datových bloků na ZX Spectru s využitím zásobníku

11. 5. 2023
Doba čtení: 65 minut

Sdílet

Autor: Depositphotos
Ukážeme si trik pro blokový přenos dat, který spočívá v tom, že se přenáší vždy dvojice bajtů, což sice samo o sobě rychlejší není, ale Z80 nemusí načítat operační kódy instrukcí při přenosu každého bajtu.

Obsah

1. Kopie datových bloků na ZX Spectru s využitím zásobníku

2. Kopie bloku o délce šestnácti bajtů přes zásobník

3. Doba přenosu šestnácti bajtů

4. Teoretická doba přenosu celého bloku 2048 bajtů

5. Úplný zdrojový kód dnešního prvního demonstračního příkladu

6. Makra v assemblerech

7. Generování strojového kódu s využitím makra

8. Opakované použití makra

9. Úplný zdrojový kód dnešního druhého demonstračního příkladu

10. Opakování libovolného bloku v assembleru

11. Realizace kopie osmi bloků, z nichž každý má velikost šestnácti bajtů

12. Úplný zdrojový kód dnešního třetího demonstračního příkladu

13. Kopie bloku pixelů na různá místa na obrazovce

14. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu

15. Shrnutí: blokové přesuny dat na mikroprocesoru Z80

16. Kam dál?

17. Čtení stisknutých kláves

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. Kopie datových bloků na ZX Spectru s využitím zásobníku

V předchozí části seriálu o vývoji programů pro legendární osmibitový domácí mikropočítač ZX Spectrum jsme si ukázali, jakým způsobem je možné realizovat subrutiny určené pro kopii paměťových bloků. Seznámili jsme se s ručně (a naivně) naprogramovanou smyčkou, s instrukcí LDIR a s rozbalením smyčky s využitím sekvence instrukcí LDI. Poslední zmíněná možnost je prozatím nejrychlejší, protože dokáže přenést jeden bajt (pro delší bloky) za přibližně 16,5 strojového cyklu. Mohlo by se zdát, že to je nejrychlejší forma přenosu bloků dat. Ovšem programátoři přišli ještě (minimálně) na jeden trik – využili zásobník, resp. přesněji řečeno instrukce určené pro ukládání dat na zásobník a pro vyjímání dat ze zásobníku. Princip triku spočívá v tom, že se přenáší vždy dvojice bajtů, což sice samo o sobě rychlejší není (stále se používá osmibitová sběrnice), ale mikroprocesor Z80 nemusí načítat operační kódy instrukcí při přenosu každého bajtu.

Navíc si dnes ukážeme další možnosti assembleru Pasmo, který dokáže pracovat s makry, expandovat makra a opakovat expanzi maker.

Obrázek 1: Takto vypadá obrazová paměť před provedením kopie prvních 2048 bajtů.

Obrázek 2: Výsledek získaný po zkopírování prvních 2048 bajtů do druhé oblasti obrazové paměti.

2. Kopie bloku o délce šestnácti bajtů přes zásobník

Popišme si tedy krok po kroku, jak je možné využít zásobník pro kopii bloků dat. Pro jednoduchost začneme největším blokem, který je možné (bez opakování kódu) tímto způsobem zkopírovat. Takový blok bude mít délku šestnácti bajtů.

Nejprve je nutné zakázat obsluhu přerušení, protože by se paměťová oblast, kterou prohlásíme za „zásobník“ mohla obslužnou rutinou přepsat. Pro zákaz přerušení slouží instrukce disable interrupt:

di

Dále do ukazatele vrcholu zásobníku SP vložíme adresu zdrojového bloku – tedy vlastně prohlásíme, že zásobník končí na této adrese:

ld   sp, source_address

Trik začíná – z našeho „zásobníku“ vyjmeme osm bajtů a postupně je uložíme do dvojice registrů AF, BC, DE a HL (kupodivu je možné použít i příznakový registr). Při každém vyjmutí dvojice bajtů se posune SP o správnou hodnotu, tj. zvýší se o dvojku (protože zásobník roste směrem dolů, jak je zvykem):

pop  af
pop  bc
pop  de
pop  hl

Tutéž operaci provedeme s druhou sadou registrů, tj. ze zásobníku získáme dalších osm bajtů (povšimněte si, že přepnutí sady registrů je nutné provést dvěma instrukcemi, protože EXX nepracuje s AF):

exx
ex   af, af'
pop  af
pop  bc
pop  de
pop  hl

Nyní máme naplněny 2×4 registrové páry. Přesuneme vrchol našeho „zásobníku“ na adresu cílového bloku, konkrétně na jeho konec (opět z toho důvodu, že zásobník roste směrem dolů a budeme tedy paměť zaplňovat od posledního bajtu):

ld   sp, destination_address+16

A začneme do „zásobníku“ ukládat jednotlivé registrové páry, a to přesně v opačném pořadí:

push hl
push de
push bc
push af

Přepneme se na originální sadu registrů a budeme pokračovat s další čtveřicí registrových párů:

exx
ex   af, af'
push hl
push de
push bc
push af

Na závěr je vhodné přesunout SP na původní hodnotu a povolit přerušení instrukcí enable interrupts:

ei

Celý blok instrukcí, které dokážou přesunout šestnáct bajtů, vypadá takto:

di
ld   sp, source_address
pop  af
pop  bc
pop  de
pop  hl
exx
ex   af, af'
pop  af
pop  bc
pop  de
pop  hl
ld   sp, destination_address+16
push hl
push de
push bc
push af
exx
ex   af, af'
push hl
push de
push bc
push af
ei
Poznámka: nejedná se o příliš elegantní kód, že?

Obrázek 3: Výsledek přenosu několika 16bajtových bloků (prostřední třetina obrazovky).

3. Doba přenosu šestnácti bajtů

Nyní je nutné si vypočítat, kolik strojových cyklů zabere přenos šestnácti bajtů přes zásobník. Připišme si tedy k jednotlivým instrukcím počet cyklů:

 10     ld   sp, source_address
 10     pop  af
 10     pop  bc
 10     pop  de
 10     pop  hl
  4     exx
  4     ex   af, af'
 10     pop  af
 10     pop  bc
 10     pop  de
 10     pop  hl
 10     ld   sp, destination_address+16
 11     push hl
 11     push de
 11     push bc
 11     push af
  4     exx
  4     ex   af, af'
 11     push hl
 11     push de
 11     push bc
 11     push af
----------------
204     cyklů

Přenos jediného bajtu tedy bude trvat přibližně 12,75 cyklů a pokud připočteme amortizované instrukce di a ei, můžeme počítat s cca 13 cykly na bajt!

4. Teoretická doba přenosu celého bloku 2048 bajtů

Pokud se bude předchozí blok opakovat celkem 128×, lze přenést celých 2048 bajtů, což byl náš původní požadavek z předchozího článku. Doba trvání přenosu jednoho bajtu zůstane na 12,75 cyklech, celkem bude zapotřebí 26112 cyklů. Ovšem velikost celého bloku s instrukcemi pro přenos se stane enormní, což si opět můžeme spočítat (tentokrát nebudeme počítat cykly, ale bajty):

3    ld   sp, source_address
1    pop  af
1    pop  bc
1    pop  de
1    pop  hl
1    exx
1    ex   af, af'
1    pop  af
1    pop  bc
1    pop  de
1    pop  hl
3    ld   sp, destination_address+16
1    push hl
1    push de
1    push bc
1    push af
1    exx
1    ex   af, af'
1    push hl
1    push de
1    push bc
1    push af
----------------
26   bajtů

Pro přenos 2048 bajtů dat tedy bude zapotřebí 26×128=3328 bajtů strojového kódu! Takový luxus si můžeme dovolit dnes, ovšem pro ZX Spectrum s 48 kB RAM je to většinou nemožné (výjimkou je kód generovaný za běhu, například v demech).

5. Ú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 založeného na použití zásobníku bude vypadat následovně:

; Example #90:
;    Print ASCII table on screen + copy it to second part of screen using stack.
 
SCREEN_ADR          equ $4000
SCREEN_BLOCK_SIZE   equ 32*64
SECOND_SCREEN_BLOCK equ SCREEN_ADR+SCREEN_BLOCK_SIZE
 
CHAR_ADR            equ $3c00
ENTRY_POINT         equ $8000
 
        org ENTRY_POINT
 
        ; Vstupní bod celého programu
start:
        call fill_in_screen           ; vyplnění obrazovky ASCII tabulkami
        di
 
        ld   sp, SCREEN_ADR+32*9
        pop  af
        pop  bc
        pop  de
        pop  hl
        exx
        ex   af, af'
        pop  af
        pop  bc
        pop  de
        pop  hl
        ld   sp, SECOND_SCREEN_BLOCK+16
        push hl
        push de
        push bc
        push af
        exx
        ex   af, af'
        push hl
        push de
        push bc
        push af
 
        ld   sp, SCREEN_ADR+32*(8+9)
        pop  af
        pop  bc
        pop  de
        pop  hl
        exx
        ex   af, af'
        pop  af
        pop  bc
        pop  de
        pop  hl
        ld   sp, SECOND_SCREEN_BLOCK+16+(32*8)
        push hl
        push de
        push bc
        push af
        exx
        ex   af, af'
        push hl
        push de
        push bc
        push af
 
        ld   sp, SCREEN_ADR+32*(8+8+9)
        pop  af
        pop  bc
        pop  de
        pop  hl
        exx
        ex   af, af'
        pop  af
        pop  bc
        pop  de
        pop  hl
        ld   sp, SECOND_SCREEN_BLOCK+16+(32*8*2)
        push hl
        push de
        push bc
        push af
        exx
        ex   af, af'
        push hl
        push de
        push bc
        push af
 
        ld sp, SCREEN_ADR+SECOND_SCREEN_BLOCK+2048
        ei
finito:
        jr finito                     ; ukončit program nekonečnou smyčkou
 
 
fill_in_screen:
        ; Vyplnění obrazovky snadno rozpoznatelným vzorkem - ASCII tabulkami
        ;
        ; vstupy:
        ; žádné
        ld de, SCREEN_ADR        ; adresa pro vykreslení prvního bloku znaků
        call draw_ascii_table    ; vykreslení 96 znaků
        ret                      ; návrat z podprogramu
 
 
draw_ascii_table:
        ; Vytištění ASCII tabulky
        ;       
        ; vstupy:
        ; DE - adresa v obrazové paměti pro vykreslení znaku
        ld a, ' '                ; kód vykreslovaného znaku
next_char:
        push af                  ; uschovat akumulátor na zásobník
        call draw_char           ; zavolat subrutinu pro vykreslení znaku
        ld a, ' '                ; vykreslit za znakem mezeru
        call draw_char           ; zavolat subrutinu pro vykreslení znaku
        pop af                   ; obnovit akumulátor ze zásobníku
        inc a                    ; ASCII kód dalšího znaku
        cp  ' ' + 96             ; jsme již na konci ASCII tabulky?
        jr nz, next_char         ; ne? potom pokračujeme
        ret                      ; návrat z podprogramu
 
 
draw_char:
        ; Vytištění jednoho znaku na obrazovku
        ;
        ; vstupy:
        ; A - kód znaku pro vykreslení
        ; DE - adresa v obrazové paměti pro vykreslení znaku
        ;
        ; výstupy:
        ; DE - adresa v obrazové paměti pro vykreslení dalšího znaku
        ;
        ; změněné registry:
        ; všechny
        ld bc, CHAR_ADR          ; adresa, od níž začínají masky znaků
        ld h, c                  ; C je nulové, protože CHAR_ADR=0x3c00
        ld l, a                  ; kód znaku je nyní ve dvojici HL
 
        add  hl, hl              ; 2x
        add  hl, hl              ; 4x
        add  hl, hl              ; 8x
        add  hl, bc              ; přičíst bázovou adresu masek znaků
 
        ld b, 8                  ; počitadlo zapsaných bajtů
        ld c, d
 
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
        inc  e
        ret  z                   ; D+=8,E=E+1=0
        ld   d, c
        ret                      ; D=D,E=E+1
 
end ENTRY_POINT
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu najdete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/90-copy-ascii-table-F.asm.

Pro úplnost si uveďme u způsob překladu z assembleru do strojového kódu:

SCREEN_ADR      EQU 4000
SCREEN_BLOCK_SIZE EQU 0800
SECOND_SCREEN_BLOCK EQU 4800
CHAR_ADR        EQU 3C00
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:CD5880     CALL 8058
8003:F3         DI
8004:312041     LD SP, 4120
8007:F1         POP AF
8008:C1         POP BC
8009:D1         POP DE
800A:E1         POP HL
800B:D9         EXX
800C:08         EX AF, AF'
800D:F1         POP AF
800E:C1         POP BC
800F:D1         POP DE
8010:E1         POP HL
8011:311048     LD SP, 4810
8014:E5         PUSH HL
8015:D5         PUSH DE
8016:C5         PUSH BC
8017:F5         PUSH AF
8018:D9         EXX
8019:08         EX AF, AF'
801A:E5         PUSH HL
801B:D5         PUSH DE
801C:C5         PUSH BC
801D:F5         PUSH AF
801E:312042     LD SP, 4220
8021:F1         POP AF
8022:C1         POP BC
8023:D1         POP DE
8024:E1         POP HL
8025:D9         EXX
8026:08         EX AF, AF'
8027:F1         POP AF
8028:C1         POP BC
8029:D1         POP DE
802A:E1         POP HL
802B:311049     LD SP, 4910
802E:E5         PUSH HL
802F:D5         PUSH DE
8030:C5         PUSH BC
8031:F5         PUSH AF
8032:D9         EXX
8033:08         EX AF, AF'
8034:E5         PUSH HL
8035:D5         PUSH DE
8036:C5         PUSH BC
8037:F5         PUSH AF
8038:312043     LD SP, 4320
803B:F1         POP AF
803C:C1         POP BC
803D:D1         POP DE
803E:E1         POP HL
803F:D9         EXX
8040:08         EX AF, AF'
8041:F1         POP AF
8042:C1         POP BC
8043:D1         POP DE
8044:E1         POP HL
8045:31104A     LD SP, 4A10
8048:E5         PUSH HL
8049:D5         PUSH DE
804A:C5         PUSH BC
804B:F5         PUSH AF
804C:D9         EXX
804D:08         EX AF, AF'
804E:E5         PUSH HL
804F:D5         PUSH DE
8050:C5         PUSH BC
8051:F5         PUSH AF
8052:310090     LD SP, 9000
8055:FB         EI
8056:           label finito
8056:18FE       JR 8056
8058:           label fill_in_screen
8058:110040     LD DE, 4000
805B:CD5F80     CALL 805F
805E:C9         RET
805F:           label draw_ascii_table
805F:3E20       LD A, 20
8061:           label next_char
8061:F5         PUSH AF
8062:CD7180     CALL 8071
8065:3E20       LD A, 20
8067:CD7180     CALL 8071
806A:F1         POP AF
806B:3C         INC A
806C:FE80       CP 80
806E:20F1       JR NZ, 8061
8070:C9         RET
8071:           label draw_char
8071:01003C     LD BC, 3C00
8074:61         LD H, C
8075:6F         LD L, A
8076:29         ADD HL, HL
8077:29         ADD HL, HL
8078:29         ADD HL, HL
8079:09         ADD HL, BC
807A:0608       LD B, 08
807C:4A         LD C, D
807D:           label loop
807D:7E         LD A, (HL)
807E:12         LD (DE), A
807F:2C         INC L
8080:14         INC D
8081:10FA       DJNZ 807D
8083:1C         INC E
8084:C8         RET Z
8085:51         LD D, C
8086:C9         RET
8087:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8086
Poznámka: povšimněte si, že velikost kódu nám narostla na 134 bajtů. To se sice nemusí z dnešního pohledu zdát jako nějaká velká hodnota, ale v kontextu ZX Spectra s 48 kB RAM je to již znatelné.

6. Makra v assemblerech

Původní utility určené pro generování strojového kódu, které začaly vznikat již na začátku padesátých let minulého století, byly relativně jednoduché nástroje, které pouze převáděly symbolická jména instrukcí na binární kód. Později se ovšem začaly možnosti assemblerů rozšiřovat a vylepšovat, například do nich přibyla podpora textových maker, řízení víceprůchodového překladu, překlad využívající podmínky pro výběr větví kódu, vytváření výstupních sestav s překládanými symboly, později i skutečné linkování s knihovnami atd.). Takto vylepšeným nástrojům se začalo obecně říkat assemblery a jazyku pro symbolický zápis programů pak jazyk symbolických instrukcí či jazyk symbolických adres – assembly language (někdy též zkráceně nazývaný assembler, takže toto slovo má vlastně dodnes oba dva významy). Jednalo se o svým způsobem převratnou myšlenku: sám počítač byl použit pro tvorbu programů, čímž odpadla namáhavá práce s tužkou a papírem.

A speciálně assemblerům, které podporovaly makra, se říkalo makroassemblery. Dnes se již s tímto jménem setkáme jen minimálně, protože všechny moderní assemblery práci s makry podporují, takže tuto vlastnost již není zapotřebí tak zdůrazňovat. A mezi tyto assemblery patří i Pasmo, v němž zapisujeme programy pro ZX Spectrum. Makrům se budeme věnovat v navazujících kapitolách.

Obrázek 4: Vývojové prostředí Atari Macro Assembleru pro konkurenční platformu s mikroprocesorem MOS 6502. Povšimněte si použití třípísmenných mnemotechnických názvů instrukcí.

7. Generování strojového kódu s využitím makra

V assembleru Pasmo je možné makra definovat dvěma způsoby, které vypadají následovně:

jméno_makra MACRO [nepovinný seznam parametrů]
    ...
    ...
    ...
ENDM

Nebo:

MACRO jméno_makra, [nepovinný seznam parametrů]
    ...
    ...
    ...
ENDM

Druhý způsob vypadá poněkud neobvykle, protože vyžaduje, aby se za jménem makra zapsala čárka a za ní seznam parametrů. Z tohoto důvodu (a taktéž proto, že to odpovídá mnoha dalším assemblerům) použijeme první naznačený způsob.

Pro naše potřeby si vytvoříme makro pojmenované copy16bytes, které bude akceptovat dva parametry – zdrojovou a cílovou adresu. Ovšem je nutné počítat s tím, že se jedná o parametry makra vyhodnocované v době překladu. Podobně jako v programovacím jazyku C se vlastně jedná o poměrně primitivní textovou náhradu jména parametru za jeho skutečnou hodnotu použitou při volání makra (což může u složitějších výrazů vyžadovat důsledné „závorkování“). V našem konkrétním případě však žádné složitější výrazy nepoužíváme, takže definice makra je přímočará – vzniklá kopií původního kódu s doplněním jmen parametrů (podtrženo):

copy16bytes MACRO source_address, destination_address
        ld   sp, source_address
        pop  af
        pop  bc
        pop  de
        pop  hl
        exx
        ex   af, af'
        pop  af
        pop  bc
        pop  de
        pop  hl
        ld   sp, destination_address+16
        push hl
        push de
        push bc
        push af
        exx
        ex   af, af'
        push hl
        push de
        push bc
        push af
ENDM

8. Opakované použití makra

Ve chvíli, kdy je makro nadefinováno, nám makroassembler Pasmo umožňuje makro kdykoli použít. Samozřejmě se v tomto případě nejedná o žádné volání makra, ale o jeho expanzi (v době překladu), i když samotný zápis může evokovat volání funkce nebo subrutiny:

copy16bytes SCREEN_ADR+32*(0*8+9), SECOND_SCREEN_BLOCK+(32*8*0)
copy16bytes SCREEN_ADR+32*(1*8+9), SECOND_SCREEN_BLOCK+(32*8*1)
copy16bytes SCREEN_ADR+32*(2*8+9), SECOND_SCREEN_BLOCK+(32*8*2)
copy16bytes SCREEN_ADR+32*(3*8+9), SECOND_SCREEN_BLOCK+(32*8*3)
copy16bytes SCREEN_ADR+32*(4*8+9), SECOND_SCREEN_BLOCK+(32*8*4)
copy16bytes SCREEN_ADR+32*(5*8+9), SECOND_SCREEN_BLOCK+(32*8*5)
copy16bytes SCREEN_ADR+32*(6*8+9), SECOND_SCREEN_BLOCK+(32*8*6)
copy16bytes SCREEN_ADR+32*(7*8+9), SECOND_SCREEN_BLOCK+(32*8*7)

Obrázek 5: Výsledek přenosu osmi 16bajtových bloků (celkem 128 bajtů Video RAM).

Samotné expandované makro je viditelné ve výpisu vygenerovaného strojového kódu. Povšimněte si taktéž, že se už v době překladu vypočetly i obě adresy, s nimiž instrukce v makru pracují:

Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0000 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0000 )
LD SP , source_address
8004:312041     LD SP, 4120
POP AF
8007:F1         POP AF
POP BC
8008:C1         POP BC
POP DE
8009:D1         POP DE
POP HL
800A:E1         POP HL
EXX
800B:D9         EXX
EX AF , AF'
800C:08         EX AF, AF'
POP AF
800D:F1         POP AF
POP BC
800E:C1         POP BC
POP DE
800F:D1         POP DE
POP HL
8010:E1         POP HL
LD SP , destination_address + 0010
8011:311048     LD SP, 4810
PUSH HL
8014:E5         PUSH HL
PUSH DE
8015:D5         PUSH DE
PUSH BC
8016:C5         PUSH BC
PUSH AF
8017:F5         PUSH AF
EXX
8018:D9         EXX
EX AF , AF'
8019:08         EX AF, AF'
PUSH HL
801A:E5         PUSH HL
PUSH DE
801B:D5         PUSH DE
PUSH BC
801C:C5         PUSH BC
PUSH AF
801D:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes

9. Ú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, v němž bude použito výše popsané makro copy16bytes, bude vypadat následovně:

; Example #91:
 
SCREEN_ADR          equ $4000
SCREEN_BLOCK_SIZE   equ 32*64
SECOND_SCREEN_BLOCK equ SCREEN_ADR+SCREEN_BLOCK_SIZE
 
CHAR_ADR            equ $3c00
ENTRY_POINT         equ $8000
 
copy16bytes MACRO source_address, destination_address
        ld   sp, source_address
        pop  af
        pop  bc
        pop  de
        pop  hl
        exx
        ex   af, af'
        pop  af
        pop  bc
        pop  de
        pop  hl
        ld   sp, destination_address+16
        push hl
        push de
        push bc
        push af
        exx
        ex   af, af'
        push hl
        push de
        push bc
        push af
ENDM
 
        org ENTRY_POINT
 
        ; Vstupní bod celého programu
start:
        call fill_in_screen           ; vyplnění obrazovky ASCII tabulkami
        di
 
        copy16bytes SCREEN_ADR+32*(0*8+9), SECOND_SCREEN_BLOCK+(32*8*0)
        copy16bytes SCREEN_ADR+32*(1*8+9), SECOND_SCREEN_BLOCK+(32*8*1)
        copy16bytes SCREEN_ADR+32*(2*8+9), SECOND_SCREEN_BLOCK+(32*8*2)
        copy16bytes SCREEN_ADR+32*(3*8+9), SECOND_SCREEN_BLOCK+(32*8*3)
        copy16bytes SCREEN_ADR+32*(4*8+9), SECOND_SCREEN_BLOCK+(32*8*4)
        copy16bytes SCREEN_ADR+32*(5*8+9), SECOND_SCREEN_BLOCK+(32*8*5)
        copy16bytes SCREEN_ADR+32*(6*8+9), SECOND_SCREEN_BLOCK+(32*8*6)
        copy16bytes SCREEN_ADR+32*(7*8+9), SECOND_SCREEN_BLOCK+(32*8*7)
 
        ld sp, SCREEN_ADR+SECOND_SCREEN_BLOCK+2048
        ei
finito:
        jr finito                     ; ukončit program nekonečnou smyčkou
 
 
fill_in_screen:
        ; Vyplnění obrazovky snadno rozpoznatelným vzorkem - ASCII tabulkami
        ;
        ; vstupy:
        ; žádné
        ld de, SCREEN_ADR        ; adresa pro vykreslení prvního bloku znaků
        call draw_ascii_table    ; vykreslení 96 znaků
        ret                      ; návrat z podprogramu
 
 
draw_ascii_table:
        ; Vytištění ASCII tabulky
        ;       
        ; vstupy:
        ; DE - adresa v obrazové paměti pro vykreslení znaku
        ld a, ' '                ; kód vykreslovaného znaku
next_char:
        push af                  ; uschovat akumulátor na zásobník
        call draw_char           ; zavolat subrutinu pro vykreslení znaku
        ld a, ' '                ; vykreslit za znakem mezeru
        call draw_char           ; zavolat subrutinu pro vykreslení znaku
        pop af                   ; obnovit akumulátor ze zásobníku
        inc a                    ; ASCII kód dalšího znaku
        cp  ' ' + 96             ; jsme již na konci ASCII tabulky?
        jr nz, next_char         ; ne? potom pokračujeme
        ret                      ; návrat z podprogramu
 
 
draw_char:
        ; Vytištění jednoho znaku na obrazovku
        ;
        ; vstupy:
        ; A - kód znaku pro vykreslení
        ; DE - adresa v obrazové paměti pro vykreslení znaku
        ;
        ; výstupy:
        ; DE - adresa v obrazové paměti pro vykreslení dalšího znaku
        ;
        ; změněné registry:
        ; všechny
        ld bc, CHAR_ADR          ; adresa, od níž začínají masky znaků
        ld h, c                  ; C je nulové, protože CHAR_ADR=0x3c00
        ld l, a                  ; kód znaku je nyní ve dvojici HL
 
        add  hl, hl              ; 2x
        add  hl, hl              ; 4x
        add  hl, hl              ; 8x
        add  hl, bc              ; přičíst bázovou adresu masek znaků
 
        ld b, 8                  ; počitadlo zapsaných bajtů
        ld c, d
 
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
        inc  e
        ret  z                   ; D+=8,E=E+1=0
        ld   d, c
        ret                      ; D=D,E=E+1
 
end ENTRY_POINT
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu najdete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/91-copy-ascii-table-G.asm.

Na tomto místě je vhodné si ověřit, jakým způsobem bylo vlastně makro copy16bytes expandováno a jak rozsáhlý je výsledný zdrojový kód. Všechny tyto informace lze nalézt s listingu, který assembler Pasmo může vytvářet (opět se jedná o vlastnost společnou mnoha moderním assemblerům):

SCREEN_ADR      EQU 4000
SCREEN_BLOCK_SIZE EQU 0800
SECOND_SCREEN_BLOCK EQU 4800
CHAR_ADR        EQU 3C00
ENTRY_POINT     EQU 8000
Defining MACRO copy16bytes
Params: source_address, destination_address
                ORG 8000
8000:           label start
8000:CDDA80     CALL 80DA
8003:F3         DI
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0000 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0000 )
LD SP , source_address
8004:312041     LD SP, 4120
POP AF
8007:F1         POP AF
POP BC
8008:C1         POP BC
POP DE
8009:D1         POP DE
POP HL
800A:E1         POP HL
EXX
800B:D9         EXX
EX AF , AF'
800C:08         EX AF, AF'
POP AF
800D:F1         POP AF
POP BC
800E:C1         POP BC
POP DE
800F:D1         POP DE
POP HL
8010:E1         POP HL
LD SP , destination_address + 0010
8011:311048     LD SP, 4810
PUSH HL
8014:E5         PUSH HL
PUSH DE
8015:D5         PUSH DE
PUSH BC
8016:C5         PUSH BC
PUSH AF
8017:F5         PUSH AF
EXX
8018:D9         EXX
EX AF , AF'
8019:08         EX AF, AF'
PUSH HL
801A:E5         PUSH HL
PUSH DE
801B:D5         PUSH DE
PUSH BC
801C:C5         PUSH BC
PUSH AF
801D:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0001 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0001 )
LD SP , source_address
801E:312042     LD SP, 4220
POP AF
8021:F1         POP AF
POP BC
8022:C1         POP BC
POP DE
8023:D1         POP DE
POP HL
8024:E1         POP HL
EXX
8025:D9         EXX
EX AF , AF'
8026:08         EX AF, AF'
POP AF
8027:F1         POP AF
POP BC
8028:C1         POP BC
POP DE
8029:D1         POP DE
POP HL
802A:E1         POP HL
LD SP , destination_address + 0010
802B:311049     LD SP, 4910
PUSH HL
802E:E5         PUSH HL
PUSH DE
802F:D5         PUSH DE
PUSH BC
8030:C5         PUSH BC
PUSH AF
8031:F5         PUSH AF
EXX
8032:D9         EXX
EX AF , AF'
8033:08         EX AF, AF'
PUSH HL
8034:E5         PUSH HL
PUSH DE
8035:D5         PUSH DE
PUSH BC
8036:C5         PUSH BC
PUSH AF
8037:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0002 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0002 )
LD SP , source_address
8038:312043     LD SP, 4320
POP AF
803B:F1         POP AF
POP BC
803C:C1         POP BC
POP DE
803D:D1         POP DE
POP HL
803E:E1         POP HL
EXX
803F:D9         EXX
EX AF , AF'
8040:08         EX AF, AF'
POP AF
8041:F1         POP AF
POP BC
8042:C1         POP BC
POP DE
8043:D1         POP DE
POP HL
8044:E1         POP HL
LD SP , destination_address + 0010
8045:31104A     LD SP, 4A10
PUSH HL
8048:E5         PUSH HL
PUSH DE
8049:D5         PUSH DE
PUSH BC
804A:C5         PUSH BC
PUSH AF
804B:F5         PUSH AF
EXX
804C:D9         EXX
EX AF , AF'
804D:08         EX AF, AF'
PUSH HL
804E:E5         PUSH HL
PUSH DE
804F:D5         PUSH DE
PUSH BC
8050:C5         PUSH BC
PUSH AF
8051:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0003 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0003 )
LD SP , source_address
8052:312044     LD SP, 4420
POP AF
8055:F1         POP AF
POP BC
8056:C1         POP BC
POP DE
8057:D1         POP DE
POP HL
8058:E1         POP HL
EXX
8059:D9         EXX
EX AF , AF'
805A:08         EX AF, AF'
POP AF
805B:F1         POP AF
POP BC
805C:C1         POP BC
POP DE
805D:D1         POP DE
POP HL
805E:E1         POP HL
LD SP , destination_address + 0010
805F:31104B     LD SP, 4B10
PUSH HL
8062:E5         PUSH HL
PUSH DE
8063:D5         PUSH DE
PUSH BC
8064:C5         PUSH BC
PUSH AF
8065:F5         PUSH AF
EXX
8066:D9         EXX
EX AF , AF'
8067:08         EX AF, AF'
PUSH HL
8068:E5         PUSH HL
PUSH DE
8069:D5         PUSH DE
PUSH BC
806A:C5         PUSH BC
PUSH AF
806B:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0004 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0004 )
LD SP , source_address
806C:312045     LD SP, 4520
POP AF
806F:F1         POP AF
POP BC
8070:C1         POP BC
POP DE
8071:D1         POP DE
POP HL
8072:E1         POP HL
EXX
8073:D9         EXX
EX AF , AF'
8074:08         EX AF, AF'
POP AF
8075:F1         POP AF
POP BC
8076:C1         POP BC
POP DE
8077:D1         POP DE
POP HL
8078:E1         POP HL
LD SP , destination_address + 0010
8079:31104C     LD SP, 4C10
PUSH HL
807C:E5         PUSH HL
PUSH DE
807D:D5         PUSH DE
PUSH BC
807E:C5         PUSH BC
PUSH AF
807F:F5         PUSH AF
EXX
8080:D9         EXX
EX AF , AF'
8081:08         EX AF, AF'
PUSH HL
8082:E5         PUSH HL
PUSH DE
8083:D5         PUSH DE
PUSH BC
8084:C5         PUSH BC
PUSH AF
8085:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0005 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0005 )
LD SP , source_address
8086:312046     LD SP, 4620
POP AF
8089:F1         POP AF
POP BC
808A:C1         POP BC
POP DE
808B:D1         POP DE
POP HL
808C:E1         POP HL
EXX
808D:D9         EXX
EX AF , AF'
808E:08         EX AF, AF'
POP AF
808F:F1         POP AF
POP BC
8090:C1         POP BC
POP DE
8091:D1         POP DE
POP HL
8092:E1         POP HL
LD SP , destination_address + 0010
8093:31104D     LD SP, 4D10
PUSH HL
8096:E5         PUSH HL
PUSH DE
8097:D5         PUSH DE
PUSH BC
8098:C5         PUSH BC
PUSH AF
8099:F5         PUSH AF
EXX
809A:D9         EXX
EX AF , AF'
809B:08         EX AF, AF'
PUSH HL
809C:E5         PUSH HL
PUSH DE
809D:D5         PUSH DE
PUSH BC
809E:C5         PUSH BC
PUSH AF
809F:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0006 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0006 )
LD SP , source_address
80A0:312047     LD SP, 4720
POP AF
80A3:F1         POP AF
POP BC
80A4:C1         POP BC
POP DE
80A5:D1         POP DE
POP HL
80A6:E1         POP HL
EXX
80A7:D9         EXX
EX AF , AF'
80A8:08         EX AF, AF'
POP AF
80A9:F1         POP AF
POP BC
80AA:C1         POP BC
POP DE
80AB:D1         POP DE
POP HL
80AC:E1         POP HL
LD SP , destination_address + 0010
80AD:31104E     LD SP, 4E10
PUSH HL
80B0:E5         PUSH HL
PUSH DE
80B1:D5         PUSH DE
PUSH BC
80B2:C5         PUSH BC
PUSH AF
80B3:F5         PUSH AF
EXX
80B4:D9         EXX
EX AF , AF'
80B5:08         EX AF, AF'
PUSH HL
80B6:E5         PUSH HL
PUSH DE
80B7:D5         PUSH DE
PUSH BC
80B8:C5         PUSH BC
PUSH AF
80B9:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( 0007 * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * 0007 )
LD SP , source_address
80BA:312048     LD SP, 4820
POP AF
80BD:F1         POP AF
POP BC
80BE:C1         POP BC
POP DE
80BF:D1         POP DE
POP HL
80C0:E1         POP HL
EXX
80C1:D9         EXX
EX AF , AF'
80C2:08         EX AF, AF'
POP AF
80C3:F1         POP AF
POP BC
80C4:C1         POP BC
POP DE
80C5:D1         POP DE
POP HL
80C6:E1         POP HL
LD SP , destination_address + 0010
80C7:31104F     LD SP, 4F10
PUSH HL
80CA:E5         PUSH HL
PUSH DE
80CB:D5         PUSH DE
PUSH BC
80CC:C5         PUSH BC
PUSH AF
80CD:F5         PUSH AF
EXX
80CE:D9         EXX
EX AF , AF'
80CF:08         EX AF, AF'
PUSH HL
80D0:E5         PUSH HL
PUSH DE
80D1:D5         PUSH DE
PUSH BC
80D2:C5         PUSH BC
PUSH AF
80D3:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
80D4:310090     LD SP, 9000
80D7:FB         EI
80D8:           label finito
80D8:18FE       JR 80D8
80DA:           label fill_in_screen
80DA:110040     LD DE, 4000
80DD:CDE180     CALL 80E1
80E0:C9         RET
80E1:           label draw_ascii_table
80E1:3E20       LD A, 20
80E3:           label next_char
80E3:F5         PUSH AF
80E4:CDF380     CALL 80F3
80E7:3E20       LD A, 20
80E9:CDF380     CALL 80F3
80EC:F1         POP AF
80ED:3C         INC A
80EE:FE80       CP 80
80F0:20F1       JR NZ, 80E3
80F2:C9         RET
80F3:           label draw_char
80F3:01003C     LD BC, 3C00
80F6:61         LD H, C
80F7:6F         LD L, A
80F8:29         ADD HL, HL
80F9:29         ADD HL, HL
80FA:29         ADD HL, HL
80FB:09         ADD HL, BC
80FC:0608       LD B, 08
80FE:4A         LD C, D
80FF:           label loop
80FF:7E         LD A, (HL)
8100:12         LD (DE), A
8101:2C         INC L
8102:14         INC D
8103:10FA       DJNZ 80FF
8105:1C         INC E
8106:C8         RET Z
8107:51         LD D, C
8108:C9         RET
8109:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8108
Poznámka: povšimněte si, že velikost strojového kódu narostla na 264 bajtů, což je již ve světě ZX Spectra poměrně vysoké číslo.

10. Opakování libovolného bloku v assembleru

I přesto, že jsme v předchozím demonstračním příkladu použili makro, které nám umožnilo zápis zjednodušit, není výsledek ideální, protože se makro používá (expanduje) v opakujícím se vzorku:

copy16bytes SCREEN_ADR+32*(0*8+9), SECOND_SCREEN_BLOCK+(32*8*0)
copy16bytes SCREEN_ADR+32*(1*8+9), SECOND_SCREEN_BLOCK+(32*8*1)
copy16bytes SCREEN_ADR+32*(2*8+9), SECOND_SCREEN_BLOCK+(32*8*2)
copy16bytes SCREEN_ADR+32*(3*8+9), SECOND_SCREEN_BLOCK+(32*8*3)
copy16bytes SCREEN_ADR+32*(4*8+9), SECOND_SCREEN_BLOCK+(32*8*4)
copy16bytes SCREEN_ADR+32*(5*8+9), SECOND_SCREEN_BLOCK+(32*8*5)
copy16bytes SCREEN_ADR+32*(6*8+9), SECOND_SCREEN_BLOCK+(32*8*6)
copy16bytes SCREEN_ADR+32*(7*8+9), SECOND_SCREEN_BLOCK+(32*8*7)

Každý programátor by mohl rozpoznat, že by bylo vhodné opakující se řádky nahradit nějakou formou smyčky expandované v době překladu. I je (na rozdíl od makroprocesoru céčka) v Pasmu možné, protože je podporován následující zápis:

REPT počet opakování
   ...
   ...
   ...
ENDM

popř. jsou podporována i počitadla smyčky:

REPT počet opakování, počitadlo, výchozí_hodnota_počitadla
   ...
   ...
   ...
ENDM

11. Realizace kopie osmi bloků, z nichž každý má velikost šestnácti bajtů

Výše uvedená klauzule REPT/ENDM nám umožňuje výše uvedených osm řádků (volání makra) nahradit za:

REPT 8, cnt, 0
copy16bytes SCREEN_ADR+32*(cnt*8+9), SECOND_SCREEN_BLOCK+(32*8*cnt)
ENDM

Přičemž se stále používá stejné makro nazvané copy16bytes:

copy16bytes MACRO source_address, destination_address
        ld   sp, source_address
        pop  af
        pop  bc
        pop  de
        pop  hl
        exx
        ex   af, af'
        pop  af
        pop  bc
        pop  de
        pop  hl
        ld   sp, destination_address+16
        push hl
        push de
        push bc
        push af
        exx
        ex   af, af'
        push hl
        push de
        push bc
        push af
ENDM

Obrázek 6: Výsledek přenosu osmi bloků, každého o velikosti 16 bajtů.

12. Ú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, v němž bude využita opakovaná aplikace makra, bude vypadat následovně:

SCREEN_ADR          equ $4000
SCREEN_BLOCK_SIZE   equ 32*64
SECOND_SCREEN_BLOCK equ SCREEN_ADR+SCREEN_BLOCK_SIZE
 
CHAR_ADR            equ $3c00
ENTRY_POINT         equ $8000
 
copy16bytes MACRO source_address, destination_address
        ld   sp, source_address
        pop  af
        pop  bc
        pop  de
        pop  hl
        exx
        ex   af, af'
        pop  af
        pop  bc
        pop  de
        pop  hl
        ld   sp, destination_address+16
        push hl
        push de
        push bc
        push af
        exx
        ex   af, af'
        push hl
        push de
        push bc
        push af
ENDM
 
        org ENTRY_POINT
 
        ; Vstupní bod celého programu
start:
        call fill_in_screen           ; vyplnění obrazovky ASCII tabulkami
        di
 
        REPT 8, cnt, 0
        copy16bytes SCREEN_ADR+32*(cnt*8+9), SECOND_SCREEN_BLOCK+(32*8*cnt)
        ENDM
 
        ld sp, SCREEN_ADR+SECOND_SCREEN_BLOCK+2048
        ei
finito:
        jr finito                     ; ukončit program nekonečnou smyčkou
 
 
fill_in_screen:
        ; Vyplnění obrazovky snadno rozpoznatelným vzorkem - ASCII tabulkami
        ;
        ; vstupy:
        ; žádné
        ld de, SCREEN_ADR        ; adresa pro vykreslení prvního bloku znaků
        call draw_ascii_table    ; vykreslení 96 znaků
        ret                      ; návrat z podprogramu
 
 
draw_ascii_table:
        ; Vytištění ASCII tabulky
        ;       
        ; vstupy:
        ; DE - adresa v obrazové paměti pro vykreslení znaku
        ld a, ' '                ; kód vykreslovaného znaku
next_char:
        push af                  ; uschovat akumulátor na zásobník
        call draw_char           ; zavolat subrutinu pro vykreslení znaku
        ld a, ' '                ; vykreslit za znakem mezeru
        call draw_char           ; zavolat subrutinu pro vykreslení znaku
        pop af                   ; obnovit akumulátor ze zásobníku
        inc a                    ; ASCII kód dalšího znaku
        cp  ' ' + 96             ; jsme již na konci ASCII tabulky?
        jr nz, next_char         ; ne? potom pokračujeme
        ret                      ; návrat z podprogramu
 
 
draw_char:
        ; Vytištění jednoho znaku na obrazovku
        ;
        ; vstupy:
        ; A - kód znaku pro vykreslení
        ; DE - adresa v obrazové paměti pro vykreslení znaku
        ;
        ; výstupy:
        ; DE - adresa v obrazové paměti pro vykreslení dalšího znaku
        ;
        ; změněné registry:
        ; všechny
        ld bc, CHAR_ADR          ; adresa, od níž začínají masky znaků
        ld h, c                  ; C je nulové, protože CHAR_ADR=0x3c00
        ld l, a                  ; kód znaku je nyní ve dvojici HL
 
        add  hl, hl              ; 2x
        add  hl, hl              ; 4x
        add  hl, hl              ; 8x
        add  hl, bc              ; přičíst bázovou adresu masek znaků
 
        ld b, 8                  ; počitadlo zapsaných bajtů
        ld c, d
 
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
        inc  e
        ret  z                   ; D+=8,E=E+1=0
        ld   d, c
        ret                      ; D=D,E=E+1
 
end ENTRY_POINT
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu najdete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/92-copy-ascii-table-H.asm.

Při expanzi makra vznikne již obrovský kód:

SCREEN_ADR      EQU 4000
SCREEN_BLOCK_SIZE EQU 0800
SECOND_SCREEN_BLOCK EQU 4800
CHAR_ADR        EQU 3C00
ENTRY_POINT     EQU 8000
Defining MACRO copy16bytes
Params: source_address, destination_address
                ORG 8000
8000:           label start
8000:CDAA81     CALL 81AA
8003:F3         DI
                REPT 8
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8004:312041     LD SP, 4120
POP AF
8007:F1         POP AF
POP BC
8008:C1         POP BC
POP DE
8009:D1         POP DE
POP HL
800A:E1         POP HL
EXX
800B:D9         EXX
EX AF , AF'
800C:08         EX AF, AF'
POP AF
800D:F1         POP AF
POP BC
800E:C1         POP BC
POP DE
800F:D1         POP DE
POP HL
8010:E1         POP HL
LD SP , destination_address + 0010
8011:311048     LD SP, 4810
PUSH HL
8014:E5         PUSH HL
PUSH DE
8015:D5         PUSH DE
PUSH BC
8016:C5         PUSH BC
PUSH AF
8017:F5         PUSH AF
EXX
8018:D9         EXX
EX AF , AF'
8019:08         EX AF, AF'
PUSH HL
801A:E5         PUSH HL
PUSH DE
801B:D5         PUSH DE
PUSH BC
801C:C5         PUSH BC
PUSH AF
801D:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
...
...
...
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
818A:313048     LD SP, 4830
POP AF
818D:F1         POP AF
POP BC
818E:C1         POP BC
POP DE
818F:D1         POP DE
POP HL
8190:E1         POP HL
EXX
8191:D9         EXX
EX AF , AF'
8192:08         EX AF, AF'
POP AF
8193:F1         POP AF
POP BC
8194:C1         POP BC
POP DE
8195:D1         POP DE
POP HL
8196:E1         POP HL
LD SP , destination_address + 0010
8197:31204F     LD SP, 4F20
PUSH HL
819A:E5         PUSH HL
PUSH DE
819B:D5         PUSH DE
PUSH BC
819C:C5         PUSH BC
PUSH AF
819D:F5         PUSH AF
EXX
819E:D9         EXX
EX AF , AF'
819F:08         EX AF, AF'
PUSH HL
81A0:E5         PUSH HL
PUSH DE
81A1:D5         PUSH DE
PUSH BC
81A2:C5         PUSH BC
PUSH AF
81A3:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
                ENDM
81A4:310090     LD SP, 9000
81A7:FB         EI
81A8:           label finito
81A8:18FE       JR 81A8
81AA:           label fill_in_screen
81AA:110040     LD DE, 4000
81AD:CDB181     CALL 81B1
81B0:C9         RET
81B1:           label draw_ascii_table
81B1:3E20       LD A, 20
81B3:           label next_char
81B3:F5         PUSH AF
81B4:CDC381     CALL 81C3
81B7:3E20       LD A, 20
81B9:CDC381     CALL 81C3
81BC:F1         POP AF
81BD:3C         INC A
81BE:FE80       CP 80
81C0:20F1       JR NZ, 81B3
81C2:C9         RET
81C3:           label draw_char
81C3:01003C     LD BC, 3C00
81C6:61         LD H, C
81C7:6F         LD L, A
81C8:29         ADD HL, HL
81C9:29         ADD HL, HL
81CA:29         ADD HL, HL
81CB:09         ADD HL, BC
81CC:0608       LD B, 08
81CE:4A         LD C, D
81CF:           label loop
81CF:7E         LD A, (HL)
81D0:12         LD (DE), A
81D1:2C         INC L
81D2:14         INC D
81D3:10FA       DJNZ 81CF
81D5:1C         INC E
81D6:C8         RET Z
81D7:51         LD D, C
81D8:C9         RET
81D9:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 81D8
Poznámka: povšimněte si délky kódu – celých 472 bajtů! To je cena, kterou zaplatíme za rozbalený a velmi rychlý kód pro kopii bloků dat.

13. Kopie bloku pixelů na různá místa na obrazovce

Díky tomu, že máme k dispozici „univerzální“ makro, které dokáže přenést šestnáct bajtů z libovolného místa ROM či RAM do libovolného místa RAM, můžeme provést kopii bloků pixelů na různá místa na obrazovce. Například můžeme přenést celý jeden řádek z ASCII tabulky, tedy 32 bajtů na každém obrazovém řádku:

Obrázek 7: Výsledek přenosu 16×2×16 bajtů v obrazové paměti.

Výše uvedená druhá část obrazovky byla získána kopií první části a byla provedena tímto kódem založeným na opakování expanze makra (resp. přesněji řečeno maker):

REPT 8, cnt, 0
copy16bytes SCREEN_ADR+32*(cnt*8+9), SECOND_SCREEN_BLOCK+(32*8*cnt)
copy16bytes 16+SCREEN_ADR+32*(cnt*8+9), 16+SECOND_SCREEN_BLOCK+(32*8*cnt)
ENDM

14. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu

Úplný zdrojový kód dnešního čtvrtého a současně i posledního demonstračního příkladu, v němž bude využita opakovaná aplikace makra, bude vypadat následovně:

SCREEN_ADR          equ $4000
SCREEN_BLOCK_SIZE   equ 32*64
SECOND_SCREEN_BLOCK equ SCREEN_ADR+SCREEN_BLOCK_SIZE
 
CHAR_ADR            equ $3c00
ENTRY_POINT         equ $8000
 
copy16bytes MACRO source_address, destination_address
        ld   sp, source_address
        pop  af
        pop  bc
        pop  de
        pop  hl
        exx
        ex   af, af'
        pop  af
        pop  bc
        pop  de
        pop  hl
        ld   sp, destination_address+16
        push hl
        push de
        push bc
        push af
        exx
        ex   af, af'
        push hl
        push de
        push bc
        push af
ENDM
 
        org ENTRY_POINT
 
        ; Vstupní bod celého programu
start:
        call fill_in_screen           ; vyplnění obrazovky ASCII tabulkami
        di
 
        REPT 8, cnt, 0
        copy16bytes SCREEN_ADR+32*(cnt*8+9), SECOND_SCREEN_BLOCK+(32*8*cnt)
        copy16bytes 16+SCREEN_ADR+32*(cnt*8+9), 16+SECOND_SCREEN_BLOCK+(32*8*cnt)
        ENDM
 
        ld sp, SCREEN_ADR+SECOND_SCREEN_BLOCK+2048
        ei
finito:
        jr finito                     ; ukončit program nekonečnou smyčkou
 
 
fill_in_screen:
        ; Vyplnění obrazovky snadno rozpoznatelným vzorkem - ASCII tabulkami
        ;
        ; vstupy:
        ; žádné
        ld de, SCREEN_ADR        ; adresa pro vykreslení prvního bloku znaků
        call draw_ascii_table    ; vykreslení 96 znaků
        ret                      ; návrat z podprogramu
 
 
draw_ascii_table:
        ; Vytištění ASCII tabulky
        ;       
        ; vstupy:
        ; DE - adresa v obrazové paměti pro vykreslení znaku
        ld a, ' '                ; kód vykreslovaného znaku
next_char:
        push af                  ; uschovat akumulátor na zásobník
        call draw_char           ; zavolat subrutinu pro vykreslení znaku
        ld a, ' '                ; vykreslit za znakem mezeru
        call draw_char           ; zavolat subrutinu pro vykreslení znaku
        pop af                   ; obnovit akumulátor ze zásobníku
        inc a                    ; ASCII kód dalšího znaku
        cp  ' ' + 96             ; jsme již na konci ASCII tabulky?
        jr nz, next_char         ; ne? potom pokračujeme
        ret                      ; návrat z podprogramu
 
 
draw_char:
        ; Vytištění jednoho znaku na obrazovku
        ;
        ; vstupy:
        ; A - kód znaku pro vykreslení
        ; DE - adresa v obrazové paměti pro vykreslení znaku
        ;
        ; výstupy:
        ; DE - adresa v obrazové paměti pro vykreslení dalšího znaku
        ;
        ; změněné registry:
        ; všechny
        ld bc, CHAR_ADR          ; adresa, od níž začínají masky znaků
        ld h, c                  ; C je nulové, protože CHAR_ADR=0x3c00
        ld l, a                  ; kód znaku je nyní ve dvojici HL
 
        add  hl, hl              ; 2x
        add  hl, hl              ; 4x
        add  hl, hl              ; 8x
        add  hl, bc              ; přičíst bázovou adresu masek znaků
 
        ld b, 8                  ; počitadlo zapsaných bajtů
        ld c, d
 
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
        inc  e
        ret  z                   ; D+=8,E=E+1=0
        ld   d, c
        ret                      ; D=D,E=E+1
 
end ENTRY_POINT
Poznámka: úplný zdrojový kód tohoto demonstračního příkladu najdete na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/93-copy-ascii-table-I.asm.

Vygenerovaný strojový kód tentokrát ponechám vypsaný v plné délce, abychom si dobře uvědomili, jak expanze maker a navíc opakování této expanze dopadá na celkovou délku vygenerovaného strojového kódu – až do situace, která se stává nepraktickou:

SCREEN_ADR      EQU 4000
SCREEN_BLOCK_SIZE EQU 0800
SECOND_SCREEN_BLOCK EQU 4800
CHAR_ADR        EQU 3C00
ENTRY_POINT     EQU 8000
Defining MACRO copy16bytes
Params: source_address, destination_address
                ORG 8000
8000:           label start
8000:CDAA81     CALL 81AA
8003:F3         DI
                REPT 8
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8004:312041     LD SP, 4120
POP AF
8007:F1         POP AF
POP BC
8008:C1         POP BC
POP DE
8009:D1         POP DE
POP HL
800A:E1         POP HL
EXX
800B:D9         EXX
EX AF , AF'
800C:08         EX AF, AF'
POP AF
800D:F1         POP AF
POP BC
800E:C1         POP BC
POP DE
800F:D1         POP DE
POP HL
8010:E1         POP HL
LD SP , destination_address + 0010
8011:311048     LD SP, 4810
PUSH HL
8014:E5         PUSH HL
PUSH DE
8015:D5         PUSH DE
PUSH BC
8016:C5         PUSH BC
PUSH AF
8017:F5         PUSH AF
EXX
8018:D9         EXX
EX AF , AF'
8019:08         EX AF, AF'
PUSH HL
801A:E5         PUSH HL
PUSH DE
801B:D5         PUSH DE
PUSH BC
801C:C5         PUSH BC
PUSH AF
801D:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
801E:313041     LD SP, 4130
POP AF
8021:F1         POP AF
POP BC
8022:C1         POP BC
POP DE
8023:D1         POP DE
POP HL
8024:E1         POP HL
EXX
8025:D9         EXX
EX AF , AF'
8026:08         EX AF, AF'
POP AF
8027:F1         POP AF
POP BC
8028:C1         POP BC
POP DE
8029:D1         POP DE
POP HL
802A:E1         POP HL
LD SP , destination_address + 0010
802B:312048     LD SP, 4820
PUSH HL
802E:E5         PUSH HL
PUSH DE
802F:D5         PUSH DE
PUSH BC
8030:C5         PUSH BC
PUSH AF
8031:F5         PUSH AF
EXX
8032:D9         EXX
EX AF , AF'
8033:08         EX AF, AF'
PUSH HL
8034:E5         PUSH HL
PUSH DE
8035:D5         PUSH DE
PUSH BC
8036:C5         PUSH BC
PUSH AF
8037:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8038:312042     LD SP, 4220
POP AF
803B:F1         POP AF
POP BC
803C:C1         POP BC
POP DE
803D:D1         POP DE
POP HL
803E:E1         POP HL
EXX
803F:D9         EXX
EX AF , AF'
8040:08         EX AF, AF'
POP AF
8041:F1         POP AF
POP BC
8042:C1         POP BC
POP DE
8043:D1         POP DE
POP HL
8044:E1         POP HL
LD SP , destination_address + 0010
8045:311049     LD SP, 4910
PUSH HL
8048:E5         PUSH HL
PUSH DE
8049:D5         PUSH DE
PUSH BC
804A:C5         PUSH BC
PUSH AF
804B:F5         PUSH AF
EXX
804C:D9         EXX
EX AF , AF'
804D:08         EX AF, AF'
PUSH HL
804E:E5         PUSH HL
PUSH DE
804F:D5         PUSH DE
PUSH BC
8050:C5         PUSH BC
PUSH AF
8051:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8052:313042     LD SP, 4230
POP AF
8055:F1         POP AF
POP BC
8056:C1         POP BC
POP DE
8057:D1         POP DE
POP HL
8058:E1         POP HL
EXX
8059:D9         EXX
EX AF , AF'
805A:08         EX AF, AF'
POP AF
805B:F1         POP AF
POP BC
805C:C1         POP BC
POP DE
805D:D1         POP DE
POP HL
805E:E1         POP HL
LD SP , destination_address + 0010
805F:312049     LD SP, 4920
PUSH HL
8062:E5         PUSH HL
PUSH DE
8063:D5         PUSH DE
PUSH BC
8064:C5         PUSH BC
PUSH AF
8065:F5         PUSH AF
EXX
8066:D9         EXX
EX AF , AF'
8067:08         EX AF, AF'
PUSH HL
8068:E5         PUSH HL
PUSH DE
8069:D5         PUSH DE
PUSH BC
806A:C5         PUSH BC
PUSH AF
806B:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
806C:312043     LD SP, 4320
POP AF
806F:F1         POP AF
POP BC
8070:C1         POP BC
POP DE
8071:D1         POP DE
POP HL
8072:E1         POP HL
EXX
8073:D9         EXX
EX AF , AF'
8074:08         EX AF, AF'
POP AF
8075:F1         POP AF
POP BC
8076:C1         POP BC
POP DE
8077:D1         POP DE
POP HL
8078:E1         POP HL
LD SP , destination_address + 0010
8079:31104A     LD SP, 4A10
PUSH HL
807C:E5         PUSH HL
PUSH DE
807D:D5         PUSH DE
PUSH BC
807E:C5         PUSH BC
PUSH AF
807F:F5         PUSH AF
EXX
8080:D9         EXX
EX AF , AF'
8081:08         EX AF, AF'
PUSH HL
8082:E5         PUSH HL
PUSH DE
8083:D5         PUSH DE
PUSH BC
8084:C5         PUSH BC
PUSH AF
8085:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8086:313043     LD SP, 4330
POP AF
8089:F1         POP AF
POP BC
808A:C1         POP BC
POP DE
808B:D1         POP DE
POP HL
808C:E1         POP HL
EXX
808D:D9         EXX
EX AF , AF'
808E:08         EX AF, AF'
POP AF
808F:F1         POP AF
POP BC
8090:C1         POP BC
POP DE
8091:D1         POP DE
POP HL
8092:E1         POP HL
LD SP , destination_address + 0010
8093:31204A     LD SP, 4A20
PUSH HL
8096:E5         PUSH HL
PUSH DE
8097:D5         PUSH DE
PUSH BC
8098:C5         PUSH BC
PUSH AF
8099:F5         PUSH AF
EXX
809A:D9         EXX
EX AF , AF'
809B:08         EX AF, AF'
PUSH HL
809C:E5         PUSH HL
PUSH DE
809D:D5         PUSH DE
PUSH BC
809E:C5         PUSH BC
PUSH AF
809F:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
80A0:312044     LD SP, 4420
POP AF
80A3:F1         POP AF
POP BC
80A4:C1         POP BC
POP DE
80A5:D1         POP DE
POP HL
80A6:E1         POP HL
EXX
80A7:D9         EXX
EX AF , AF'
80A8:08         EX AF, AF'
POP AF
80A9:F1         POP AF
POP BC
80AA:C1         POP BC
POP DE
80AB:D1         POP DE
POP HL
80AC:E1         POP HL
LD SP , destination_address + 0010
80AD:31104B     LD SP, 4B10
PUSH HL
80B0:E5         PUSH HL
PUSH DE
80B1:D5         PUSH DE
PUSH BC
80B2:C5         PUSH BC
PUSH AF
80B3:F5         PUSH AF
EXX
80B4:D9         EXX
EX AF , AF'
80B5:08         EX AF, AF'
PUSH HL
80B6:E5         PUSH HL
PUSH DE
80B7:D5         PUSH DE
PUSH BC
80B8:C5         PUSH BC
PUSH AF
80B9:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
80BA:313044     LD SP, 4430
POP AF
80BD:F1         POP AF
POP BC
80BE:C1         POP BC
POP DE
80BF:D1         POP DE
POP HL
80C0:E1         POP HL
EXX
80C1:D9         EXX
EX AF , AF'
80C2:08         EX AF, AF'
POP AF
80C3:F1         POP AF
POP BC
80C4:C1         POP BC
POP DE
80C5:D1         POP DE
POP HL
80C6:E1         POP HL
LD SP , destination_address + 0010
80C7:31204B     LD SP, 4B20
PUSH HL
80CA:E5         PUSH HL
PUSH DE
80CB:D5         PUSH DE
PUSH BC
80CC:C5         PUSH BC
PUSH AF
80CD:F5         PUSH AF
EXX
80CE:D9         EXX
EX AF , AF'
80CF:08         EX AF, AF'
PUSH HL
80D0:E5         PUSH HL
PUSH DE
80D1:D5         PUSH DE
PUSH BC
80D2:C5         PUSH BC
PUSH AF
80D3:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
80D4:312045     LD SP, 4520
POP AF
80D7:F1         POP AF
POP BC
80D8:C1         POP BC
POP DE
80D9:D1         POP DE
POP HL
80DA:E1         POP HL
EXX
80DB:D9         EXX
EX AF , AF'
80DC:08         EX AF, AF'
POP AF
80DD:F1         POP AF
POP BC
80DE:C1         POP BC
POP DE
80DF:D1         POP DE
POP HL
80E0:E1         POP HL
LD SP , destination_address + 0010
80E1:31104C     LD SP, 4C10
PUSH HL
80E4:E5         PUSH HL
PUSH DE
80E5:D5         PUSH DE
PUSH BC
80E6:C5         PUSH BC
PUSH AF
80E7:F5         PUSH AF
EXX
80E8:D9         EXX
EX AF , AF'
80E9:08         EX AF, AF'
PUSH HL
80EA:E5         PUSH HL
PUSH DE
80EB:D5         PUSH DE
PUSH BC
80EC:C5         PUSH BC
PUSH AF
80ED:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
80EE:313045     LD SP, 4530
POP AF
80F1:F1         POP AF
POP BC
80F2:C1         POP BC
POP DE
80F3:D1         POP DE
POP HL
80F4:E1         POP HL
EXX
80F5:D9         EXX
EX AF , AF'
80F6:08         EX AF, AF'
POP AF
80F7:F1         POP AF
POP BC
80F8:C1         POP BC
POP DE
80F9:D1         POP DE
POP HL
80FA:E1         POP HL
LD SP , destination_address + 0010
80FB:31204C     LD SP, 4C20
PUSH HL
80FE:E5         PUSH HL
PUSH DE
80FF:D5         PUSH DE
PUSH BC
8100:C5         PUSH BC
PUSH AF
8101:F5         PUSH AF
EXX
8102:D9         EXX
EX AF , AF'
8103:08         EX AF, AF'
PUSH HL
8104:E5         PUSH HL
PUSH DE
8105:D5         PUSH DE
PUSH BC
8106:C5         PUSH BC
PUSH AF
8107:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8108:312046     LD SP, 4620
POP AF
810B:F1         POP AF
POP BC
810C:C1         POP BC
POP DE
810D:D1         POP DE
POP HL
810E:E1         POP HL
EXX
810F:D9         EXX
EX AF , AF'
8110:08         EX AF, AF'
POP AF
8111:F1         POP AF
POP BC
8112:C1         POP BC
POP DE
8113:D1         POP DE
POP HL
8114:E1         POP HL
LD SP , destination_address + 0010
8115:31104D     LD SP, 4D10
PUSH HL
8118:E5         PUSH HL
PUSH DE
8119:D5         PUSH DE
PUSH BC
811A:C5         PUSH BC
PUSH AF
811B:F5         PUSH AF
EXX
811C:D9         EXX
EX AF , AF'
811D:08         EX AF, AF'
PUSH HL
811E:E5         PUSH HL
PUSH DE
811F:D5         PUSH DE
PUSH BC
8120:C5         PUSH BC
PUSH AF
8121:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8122:313046     LD SP, 4630
POP AF
8125:F1         POP AF
POP BC
8126:C1         POP BC
POP DE
8127:D1         POP DE
POP HL
8128:E1         POP HL
EXX
8129:D9         EXX
EX AF , AF'
812A:08         EX AF, AF'
POP AF
812B:F1         POP AF
POP BC
812C:C1         POP BC
POP DE
812D:D1         POP DE
POP HL
812E:E1         POP HL
LD SP , destination_address + 0010
812F:31204D     LD SP, 4D20
PUSH HL
8132:E5         PUSH HL
PUSH DE
8133:D5         PUSH DE
PUSH BC
8134:C5         PUSH BC
PUSH AF
8135:F5         PUSH AF
EXX
8136:D9         EXX
EX AF , AF'
8137:08         EX AF, AF'
PUSH HL
8138:E5         PUSH HL
PUSH DE
8139:D5         PUSH DE
PUSH BC
813A:C5         PUSH BC
PUSH AF
813B:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
813C:312047     LD SP, 4720
POP AF
813F:F1         POP AF
POP BC
8140:C1         POP BC
POP DE
8141:D1         POP DE
POP HL
8142:E1         POP HL
EXX
8143:D9         EXX
EX AF , AF'
8144:08         EX AF, AF'
POP AF
8145:F1         POP AF
POP BC
8146:C1         POP BC
POP DE
8147:D1         POP DE
POP HL
8148:E1         POP HL
LD SP , destination_address + 0010
8149:31104E     LD SP, 4E10
PUSH HL
814C:E5         PUSH HL
PUSH DE
814D:D5         PUSH DE
PUSH BC
814E:C5         PUSH BC
PUSH AF
814F:F5         PUSH AF
EXX
8150:D9         EXX
EX AF , AF'
8151:08         EX AF, AF'
PUSH HL
8152:E5         PUSH HL
PUSH DE
8153:D5         PUSH DE
PUSH BC
8154:C5         PUSH BC
PUSH AF
8155:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8156:313047     LD SP, 4730
POP AF
8159:F1         POP AF
POP BC
815A:C1         POP BC
POP DE
815B:D1         POP DE
POP HL
815C:E1         POP HL
EXX
815D:D9         EXX
EX AF , AF'
815E:08         EX AF, AF'
POP AF
815F:F1         POP AF
POP BC
8160:C1         POP BC
POP DE
8161:D1         POP DE
POP HL
8162:E1         POP HL
LD SP , destination_address + 0010
8163:31204E     LD SP, 4E20
PUSH HL
8166:E5         PUSH HL
PUSH DE
8167:D5         PUSH DE
PUSH BC
8168:C5         PUSH BC
PUSH AF
8169:F5         PUSH AF
EXX
816A:D9         EXX
EX AF , AF'
816B:08         EX AF, AF'
PUSH HL
816C:E5         PUSH HL
PUSH DE
816D:D5         PUSH DE
PUSH BC
816E:C5         PUSH BC
PUSH AF
816F:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
8170:312048     LD SP, 4820
POP AF
8173:F1         POP AF
POP BC
8174:C1         POP BC
POP DE
8175:D1         POP DE
POP HL
8176:E1         POP HL
EXX
8177:D9         EXX
EX AF , AF'
8178:08         EX AF, AF'
POP AF
8179:F1         POP AF
POP BC
817A:C1         POP BC
POP DE
817B:D1         POP DE
POP HL
817C:E1         POP HL
LD SP , destination_address + 0010
817D:31104F     LD SP, 4F10
PUSH HL
8180:E5         PUSH HL
PUSH DE
8181:D5         PUSH DE
PUSH BC
8182:C5         PUSH BC
PUSH AF
8183:F5         PUSH AF
EXX
8184:D9         EXX
EX AF , AF'
8185:08         EX AF, AF'
PUSH HL
8186:E5         PUSH HL
PUSH DE
8187:D5         PUSH DE
PUSH BC
8188:C5         PUSH BC
PUSH AF
8189:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
Expanding MACRO copy16bytes
source_address= 0010 + SCREEN_ADR + 0020 * ( cnt * 0008 + 0009 )
destination_address= 0010 + SECOND_SCREEN_BLOCK + ( 0020 * 0008 * cnt )
LD SP , source_address
818A:313048     LD SP, 4830
POP AF
818D:F1         POP AF
POP BC
818E:C1         POP BC
POP DE
818F:D1         POP DE
POP HL
8190:E1         POP HL
EXX
8191:D9         EXX
EX AF , AF'
8192:08         EX AF, AF'
POP AF
8193:F1         POP AF
POP BC
8194:C1         POP BC
POP DE
8195:D1         POP DE
POP HL
8196:E1         POP HL
LD SP , destination_address + 0010
8197:31204F     LD SP, 4F20
PUSH HL
819A:E5         PUSH HL
PUSH DE
819B:D5         PUSH DE
PUSH BC
819C:C5         PUSH BC
PUSH AF
819D:F5         PUSH AF
EXX
819E:D9         EXX
EX AF , AF'
819F:08         EX AF, AF'
PUSH HL
81A0:E5         PUSH HL
PUSH DE
81A1:D5         PUSH DE
PUSH BC
81A2:C5         PUSH BC
PUSH AF
81A3:F5         PUSH AF
ENDM
                ENDM
End of MACRO copy16bytes
                ENDM
81A4:310090     LD SP, 9000
81A7:FB         EI
81A8:           label finito
81A8:18FE       JR 81A8
81AA:           label fill_in_screen
81AA:110040     LD DE, 4000
81AD:CDB181     CALL 81B1
81B0:C9         RET
81B1:           label draw_ascii_table
81B1:3E20       LD A, 20
81B3:           label next_char
81B3:F5         PUSH AF
81B4:CDC381     CALL 81C3
81B7:3E20       LD A, 20
81B9:CDC381     CALL 81C3
81BC:F1         POP AF
81BD:3C         INC A
81BE:FE80       CP 80
81C0:20F1       JR NZ, 81B3
81C2:C9         RET
81C3:           label draw_char
81C3:01003C     LD BC, 3C00
81C6:61         LD H, C
81C7:6F         LD L, A
81C8:29         ADD HL, HL
81C9:29         ADD HL, HL
81CA:29         ADD HL, HL
81CB:09         ADD HL, BC
81CC:0608       LD B, 08
81CE:4A         LD C, D
81CF:           label loop
81CF:7E         LD A, (HL)
81D0:12         LD (DE), A
81D1:2C         INC L
81D2:14         INC D
81D3:10FA       DJNZ 81CF
81D5:1C         INC E
81D6:C8         RET Z
81D7:51         LD D, C
81D8:C9         RET
81D9:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 81D8

15. Shrnutí: blokové přesuny dat na mikroprocesoru Z80

Pro úplnost si shrňme všechny metody pro blokové přesuny bloků dat. Uvedeny budou amortizované rychlosti přenosu jednoho bajtu pro velké bloky (například 1kB a více) i přibližné doby přenosu menších bloků:

Metoda Cyklů (velký blok) Cyklů (krátký blok)
smyčka 50 50
LDIR 21 21×(n-1) + 16
sekvence LDI 16 16 + cca 30 pro skok do smyčky
sekvence PUSH/POP 12,75 13 pro bloky předem známé délky + 8 (EI+DI)
DMA 6 6 + stovky cyklů pro přípravu (viz dále)
Poznámka: skutečná doba provádění bude odlišná v případě, že je povoleno přerušení a skutečně dojde k vyvolání přerušovací subrutiny.

16. Kam dál?

Na některých počítačích založených na mikroprocesoru Zilog Z80 je možné pro blokové přenosy dat použít DMA, tedy přenos dat bez přispění mikroprocesoru (Direct Memory Access). Bude se obecně jednat o nejrychlejší možné řešení, protože se zcela odstraní cykly nutné pro načítání instrukcí a i vlastní výpočet adres by měl být rychlejší. Pro Z80 existuje čip Z80-DMA od společnosti Zilog, který sice není ve standardním ZX Spectru použit, ale byl součástí MBO2+ v DataGearu. Rychlost přenosu v tomto případě dosahuje šesti cyklů na bajt, je tedy dvakrát rychlejší, než přenos přes zásobník (za předpokladu, že je inicializace DMA amortizována – jedná se tedy o delší bloky, ideálně delší než cca 0,5 kB).

17. Čtení stisknutých kláves

Nyní již máme alespoň rámcovou představu, jakým způsobem se vykreslují objekty na obrazovku ZX Spectra. Ovšem abychom se mohli pustit do tvorby nějaké jednoduché hry, potřebujeme mít možnost tuto hru ovládat. K dispozici je klávesnice nebo joystick (kterých navíc existuje několik typů – liší se nikoli svou konstrukcí, ale způsobem připojení k ZX Spectru). V navazujícím článku si ukážeme základní práci s klávesnicí.

prace_s_linuxem_tip

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 jedenácti článcích [1] [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], 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 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
 
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

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
       
91 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

Autor článku

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