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