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
7. Generování strojového kódu s využitím 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
18. Příloha: upravený soubor Makefile pro překlad demonstračních příkladů
19. Repositář s demonstračními příklady
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
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
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
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
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
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
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
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
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) |
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í.
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
- z80 standalone assembler
https://www.asm80.com/onepage/asmz80.html - The ZX BASIC Compiler
https://www.boriel.com/pages/the-zx-basic-compiler.html - Z80 Assembly programming for the ZX Spectrum
https://www.chibiakumas.com/z80/ZXSpectrum.php - 8-BIT SMACKDOWN! 65C02 vs. Z80: slithy VLOGS #6
https://www.youtube.com/watch?v=P1paVoFEvyc - Instrukce mikroprocesoru Z80
https://clrhome.org/table/ - Z80 instructions: adresní režimy atd.
https://jnz.dk/z80/instructions.html - Z80 Instruction Groups
https://jnz.dk/z80/instgroups.html - Elena, New programming language for the ZX Spectrum Next
https://vintageisthenewold.com/elena-new-programming-language-for-the-zx-spectrum-next/ - Sinclair BASIC
https://worldofspectrum.net/legacy-info/sinclair-basic/ - Grafika na osmibitových počítačích firmy Sinclair
https://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair/ - Grafika na osmibitových počítačích firmy Sinclair II
https://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/ - HiSoft BASIC
https://worldofspectrum.net/infoseekid.cgi?id=0008249 - YS MegaBasic
https://worldofspectrum.net/infoseekid.cgi?id=0008997 - Beta Basic
https://worldofspectrum.net/infoseekid.cgi?id=0007956 - BASIC+
https://worldofspectrum.net/infoseekid.php?id=0014277 - Spectrum ROM Memory Map
https://skoolkit.ca/disassemblies/rom/maps/all.html - Goto subroutine
https://skoolkit.ca/disassemblies/rom/asm/7783.html - Spectrum Next: The Evolution of the Speccy
https://www.specnext.com/about/ - Sedmdesátiny assemblerů: lidsky čitelný strojový kód
https://www.root.cz/clanky/sedmdesatiny-assembleru-lidsky-citelny-strojovy-kod/ - Programovací jazyk BASIC na osmibitových mikropočítačích
https://www.root.cz/clanky/programovaci-jazyk-basic-na-osmibitovych-mikropocitacich/ - Programovací jazyk BASIC na osmibitových mikropočítačích (2)
https://www.root.cz/clanky/programovaci-jazyk-basic-na-osmibitovych-mikropocitacich-2/#k06 - Programovací jazyk BASIC na osmibitových mikropočítačích (3)
https://www.root.cz/clanky/programovaci-jazyk-basic-na-osmibitovych-mikropocitacich-3/ - Sinclair BASIC (Wikipedia CZ)
http://cs.wikipedia.org/wiki/Sinclair_BASIC - Assembly Language: Still Relevant Today
http://wilsonminesco.com/AssyDefense/ - Programovani v assembleru na OS Linux
http://www.cs.vsb.cz/grygarek/asm/asmlinux.html - Why Assembly Language Programming? (Why Learning Assembly Language Is Still a Good Idea)
https://wdc65×x.com/markets/education/why-assembly-language-programming/ - Low Fat Computing
http://www.ultratechnology.com/lowfat.htm - Assembly Language
https://www.cleverism.com/skills-and-tools/assembly-language/ - Why do we need assembly language?
https://cs.stackexchange.com/questions/13287/why-do-we-need-assembly-language - Assembly language (Wikipedia)
https://en.wikipedia.org/wiki/Assembly_language#Historical_perspective - Assembly languages
https://curlie.org/Computers/Programming/Languages/Assembly/ - vasm
http://sun.hasenbraten.de/vasm/ - B-ELITE
https://jsj.itch.io/b-elite - ZX-Spectrum Child
http://www.dotkam.com/2008/11/19/zx-spectrum-child/ - Speccy.cz
http://www.speccy.cz/ - Planet Sinclair
http://www.nvg.ntnu.no/sinclair/ - World of Spectrum
http://www.worldofspectrum.org/ - The system variables
https://worldofspectrum.org/ZXBasicManual/zxmanchap25.html - ZX Spectrum manual: chapter #17 Graphics
https://worldofspectrum.org/ZXBasicManual/zxmanchap17.html - Why does Sinclair BASIC have two formats for storing numbers in the same structure?
https://retrocomputing.stackexchange.com/questions/8834/why-does-sinclair-basic-have-two-formats-for-storing-numbers-in-the-same-structu - Plovoucí řádová čárka na ZX Spectru
https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/#k05 - Norma IEEE 754 a příbuzní: formáty plovoucí řádové tečky
https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/#k05 - 1A1B: THE ‚REPORT AND LINE NUMBER PRINTING‘ SUBROUTINE
https://skoolkid.github.io/rom/asm/1A1B.html - 2DE3: THE ‚PRINT A FLOATING-POINT NUMBER‘ SUBROUTINE
https://skoolkid.github.io/rom/asm/2DE3.html - 5C63: STKBOT – Address of bottom of calculator stack
https://skoolkid.github.io/rom/asm/5C63.html - 5C65: STKEND – Address of start of spare space
https://skoolkid.github.io/rom/asm/5C65.html - Why does Sinclair BASIC have two formats for storing numbers in the same structure?
https://retrocomputing.stackexchange.com/questions/8834/why-does-sinclair-basic-have-two-formats-for-storing-numbers-in-the-same-structu - Chapter 24: The memory
https://worldofspectrum.org/ZXBasicManual/zxmanchap24.html - Survey of Floating-Point Formats
https://mrob.com/pub/math/floatformats.html - Convert an 8bit number to hex in z80 assembler
https://stackoverflow.com/questions/22838444/convert-an-8bit-number-to-hex-in-z80-assembler - 80 MICROPROCESSOR Instruction Set Summary
http://www.textfiles.com/programming/CARDS/z80 - Extended Binary Coded Decimal Interchange Code
http://en.wikipedia.org/wiki/EBCDIC - ASCII/EBCDIC Conversion Table
http://docs.hp.com/en/32212–90008/apcs01.html - EBCDIC
http://www.hansenb.pdx.edu/DMKB/dict/tutorials/ebcdic.php - EBCDIC tables
http://home.mnet-online.de/wzwz.de/temp/ebcdic/cc_en.htm - The Mainframe Blog
http://mainframe.typepad.com/blog/2006/11/my_personal_mai.html - Binary-coded decimal
https://en.wikipedia.org/wiki/Binary-coded_decimal - BCD
https://cs.wikipedia.org/wiki/BCD - Z80 heaven: Floating Point
http://z80-heaven.wikidot.com/floating-point - Z80, the 8-bit Number Cruncher
http://www.andreadrian.de/oldcpu/Z80_number_cruncher.html - Floating-point library for Z80
https://github.com/DW0RKiN/Floating-point-Library-for-Z80 - z80float
https://github.com/Zeda/z80float - Fixed point arithmetic
https://www.root.cz/clanky/fixed-point-arithmetic/ - ZX Spectrum BASIC Programming – 2nd Edition
https://archive.org/details/zx-spectrum-basic-programming/page/n167/mode/2up - ZX Spectrum BASIC Programming – 2nd Edition
https://archive.org/details/zx-spectrum-basic-programming/page/n169/mode/2up - How fast is memcpy on the Z80?
https://retrocomputing.stackexchange.com/questions/4744/how-fast-is-memcpy-on-the-z80 - How do Z80 Block Transfer instructions work?
https://retrocomputing.stackexchange.com/questions/5416/how-do-z80-block-transfer-instructions-work - Retro Programming Made Simple: Keyboard
http://www.breakintoprogram.co.uk/hardware/computers/zx-spectrum/keyboard