Obsah
1. Kopie datových bloků na ZX Spectru (1)
2. Úplný zdrojový kód dnešního prvního demonstračního příkladu
3. Blokový přenos realizovaný naivně naprogramovanou smyčkou
4. Rychlost blokové kopie dat explicitně zapsanou programovou smyčkou
5. Úplný zdrojový kód dnešního druhého demonstračního příkladu
6. Kopie paměťového bloku s využitím instrukce LDIR
7. Rychlost blokové kopie dat instrukcí LDIR
8. Úplný zdrojový kód dnešního třetího demonstračního příkladu
10. Programová smyčka založená na instrukci LDI
11. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu
12. Instrukce LDI v rozbalené smyčce
13. Doba přenosu jednoho bajtu při použití rozbalené smyčky
14. Úplný zdrojový kód dnešního pátého demonstračního příkladu
15. Korektní forma rozbalené smyčky pro všechny velikosti bloků
16. Úplný zdrojový kód dnešního šestého demonstračního příkladu
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 (1)
V pořadí již jedenácté části seriálu o vývoji programů pro legendární osmibitový domácí mikropočítač ZX Spectrum se začneme zabývat zdánlivě triviální problematikou. Jedná se o implementaci podprogramu (subrutiny) zajišťující kopii datových bloků, tj. přenesení určitého množství bajtů z jedné adresy na adresu jinou.
Tato operace je na ZX Spectru velmi často používána, už jen z toho důvodu, že grafický subsystém ZX Spectra nepodporuje ani hardwarově vykreslované sprity ani hardwarový scrolling nebo double buffering (ten lze realizovat až na ZX Spectru 128+). To mj. znamená, že všechny pohybující se objekty ve hrách jsou vlastně bloky dat, které se musí přenášet (což je navíc poněkud komplikováno strukturou video paměti), popř. pohybující se pozadí je taktéž řešeno přes blokové přenosy.
Obrázek 1: Každý pohybující se objekt ve slavné hře Jet Pac se na obrazovku dostává nějakou formou blokového přesunu dat.
Dnes se zaměříme především na techniky, které bloky přenáší bajt po bajtu. Ovšem existují taktéž techniky pro přenos po dvojici bajtů, což může být (při správném naprogramování) pochopitelně rychlejší, a to i na osmibitovém mikroprocesoru s osmibitovou datovou sběrnicí.
Obrázek 2: Prostředí známé hry R-type, v němž můžeme vidět rozdělení obrazovky na dvoubarevné bloky 8×8 pixelů. Při kolizích střel či rakety s okolními předměty docházelo ke vzniku barevných chyb, ostatně jako v mnoha dalších hrách. Scrolling i pohyby předmětů ve hře – vše je nutné realizovat blokovými přenosy.
2. Úplný zdrojový kód dnešního prvního demonstračního příkladu
Dnešní první demonstrační příklad bude sloužit jako základ pro příklady vysvětlené v navazujících kapitolách. V tomto příkladu je realizován již známý postup pro vykreslení ASCII tabulky do první třetiny obrazové paměti. Výsledek by měl vypadat následovně:
Obrázek 3: ASCII tabulka vykreslená do první třetiny obrazovky.
Všechny subrutiny použité v příkladu jsme si již v tomto seriálu vysvětlili, takže si pro úplnost pouze uvedeme jeho programový kód:
SCREEN_ADR equ $4000
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
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
Způsob překladu z assembleru do strojového kódu vypadá následovně:
SCREEN_ADR EQU 4000
CHAR_ADR EQU 3C00
ENTRY_POINT EQU 8000
ORG 8000
8000: label start
8000:CD0580 CALL 8005
8003: label finito
8003:18FE JR 8003
8005: label fill_in_screen
8005:110040 LD DE, 4000
8008:CD0C80 CALL 800C
800B:C9 RET
800C: label draw_ascii_table
800C:3E20 LD A, 20
800E: label next_char
800E:F5 PUSH AF
800F:CD1E80 CALL 801E
8012:3E20 LD A, 20
8014:CD1E80 CALL 801E
8017:F1 POP AF
8018:3C INC A
8019:FE80 CP 80
801B:20F1 JR NZ, 800E
801D:C9 RET
801E: label draw_char
801E:01003C LD BC, 3C00
8021:61 LD H, C
8022:6F LD L, A
8023:29 ADD HL, HL
8024:29 ADD HL, HL
8025:29 ADD HL, HL
8026:09 ADD HL, BC
8027:0608 LD B, 08
8029:4A LD C, D
802A: label loop
802A:7E LD A, (HL)
802B:12 LD (DE), A
802C:2C INC L
802D:14 INC D
802E:10FA DJNZ 802A
8030:1C INC E
8031:C8 RET Z
8032:51 LD D, C
8033:C9 RET
8034: END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8033
3. Blokový přenos realizovaný naivně naprogramovanou smyčkou
Nyní se podívejme na některé způsoby, které lze použít pro přenos bloku obsahující první třetinu obrazových dat (32×64=2048 bajtů) do druhé třetiny obrazovky. Připravíme si vlastní podprogram (subrutinu) pro kopii dat, přičemž v registrovém páru BC bude uložena zdrojová adresa, v registrovém páru DE cílová adresa a konečně v registrovém páru HL velikost bloku, tj. délka kopírovaných dat:
ld bc, SCREEN_ADR
ld de, SECOND_SCREEN_BLOCK
ld hl, SCREEN_BLOCK_SIZE
call mem_copy
Poněkud naivní podoba subrutiny pro blokový přenos dat může vypadat následovně:
mem_copy:
ld A, (BC) ; načtení bajtu ze zdrojového bloku
ld (DE), A ; uložení bajtu do cílového bloku
inc BC ; zvýšení ukazatele do zdrojového bloku
inc DE ; zvýšení ukazatele do cílového bloku
dec HL ; snížení hodnoty počitadla
ld A, H ; test, zda počitadlo dosáhlo nuly
or L
jp NZ, mem_copy ; opakovat, pokud se nedosáhlo nuly
ret ; návrat z podprogramu
Vidíme, že se nejdříve ve dvou instrukcích provede přenos jednoho bajtu a zbytek smyčky realizuje zvýšení ukazatelů, snížení hodnoty počitadla a kontrolu, zda již počitadlo dosáhlo nuly.
Výsledek by měl vypadat následovně:
Obrázek 4: Druhá ASCII tabulka vznikla blokovou kopií tabulky první.
4. Rychlost blokové kopie dat explicitně zapsanou programovou smyčkou
Programová smyčka ukázaná v předchozí kapitole je sice pro bloky nenulové délky funkční, ale jak je rychlá? To si můžeme celkem snadno spočítat:
ld A, (BC) 7
ld (DE), A 7
inc BC 6
inc DE 6
dec HL 6
ld A, H 4
or L 4
jp NZ, mem_copy 10
------------------------
50
Celkem je tedy jediný bajt přenesen v padesáti cyklech a celý blok 2048 bajtů ve více než sto tisících cyklech! A to je již poměrně velká hodnota, kterou stojí za to optimalizovat.
5. Ú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 s naivně implementovanou subrutinou pro kopii paměťového bloku vypadá 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
org ENTRY_POINT
; Vstupní bod celého programu
start:
call fill_in_screen ; vyplnění obrazovky ASCII tabulkami
ld bc, SCREEN_ADR
ld de, SECOND_SCREEN_BLOCK
ld hl, SCREEN_BLOCK_SIZE
call mem_copy
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
mem_copy:
ld A, (BC) ; načtení bajtu ze zdrojového bloku
ld (DE), A ; uložení bajtu do cílového bloku
inc BC ; zvýšení ukazatele do zdrojového bloku
inc DE ; zvýšení ukazatele do cílového bloku
dec HL ; snížení hodnoty počitadla
ld A, H ; test, zda počitadlo dosáhlo nuly
or L
jp NZ, mem_copy ; opakovat
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
Uveďme si 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:CD1180 CALL 8011
8003:010040 LD BC, 4000
8006:110048 LD DE, 4800
8009:210008 LD HL, 0800
800C:CD1880 CALL 8018
800F: label finito
800F:18FE JR 800F
8011: label fill_in_screen
8011:110040 LD DE, 4000
8014:CD2380 CALL 8023
8017:C9 RET
8018: label mem_copy
8018:0A LD A, (BC)
8019:12 LD (DE), A
801A:03 INC BC
801B:13 INC DE
801C:2B DEC HL
801D:7C LD A, H
801E:B5 OR L
801F:C21880 JP NZ, 8018
8022:C9 RET
8023: label draw_ascii_table
8023:3E20 LD A, 20
8025: label next_char
8025:F5 PUSH AF
8026:CD3580 CALL 8035
8029:3E20 LD A, 20
802B:CD3580 CALL 8035
802E:F1 POP AF
802F:3C INC A
8030:FE80 CP 80
8032:20F1 JR NZ, 8025
8034:C9 RET
8035: label draw_char
8035:01003C LD BC, 3C00
8038:61 LD H, C
8039:6F LD L, A
803A:29 ADD HL, HL
803B:29 ADD HL, HL
803C:29 ADD HL, HL
803D:09 ADD HL, BC
803E:0608 LD B, 08
8040:4A LD C, D
8041: label loop
8041:7E LD A, (HL)
8042:12 LD (DE), A
8043:2C INC L
8044:14 INC D
8045:10FA DJNZ 8041
8047:1C INC E
8048:C8 RET Z
8049:51 LD D, C
804A:C9 RET
804B: END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 804A
6. Kopie paměťového bloku s využitím instrukce LDIR
Programová smyčka pro kopii bloku dat nebyla příliš rychlá, protože pro kopii každého bajtu (až na bajt poslední) vyžadovala 50 strojových cyklů. Mnohem kratší a taktéž rychlejší je použití specializované instrukce LDIR, která tento přenos (včetně opakování) provádí. Tato instrukce vyžaduje totožné vstupy, jako naše první subrutina, ovšem zapsané v jiných registrech: v registrovém páru HL bude uložena zdrojová adresa, v registrovém páru DE cílová adresa a konečně v registrovém páru BC velikost bloku, tj. délka kopírovaných dat (registr B či dvojice BC je pro tyto účely používána poměrně konzistentně).
Prováděnou operaci můžeme popsat tímto pseudokódem:
do {
(DE) ← (HL)
DE ← DE+1
HL ←- HL+1
BC ←- BC-1
} while (BC != 0)
Namísto volání subrutiny pro kopii bloku tedy můžeme jednoduše psát:
ld hl, SCREEN_ADR ; adresa zdrojového bloku
ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku
ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat
ldir ; provést blokový přenos
S totožným výsledkem, jaký jsme dosáhli explicitně zapsanou programovou smyčkou:
Obrázek 5: Druhá ASCII tabulka vznikla blokovou kopií tabulky první.
7. Rychlost blokové kopie dat explicitně zapsanou programovou smyčkou
Interně je instrukce LDIR implementována poněkud neefektivně, neboť pro každý přenášený bajt znovu a znovu načítá instrukční kód (2 bajty), provádí ho a poté posunuje obsah registru PC zpět. Z tohoto důvodu, i když ostatní operace mohou být částečně překryty, je délka trvání přenosu jednoho bajtu rovna 21 strojovým cyklům:
fetch instrukce 4
fetch instrukce 4
čtení bajtu 3
zápis bajtu 5
opakování 5
-------------------
21
Poslední běh je poněkud rychlejší, neboť již neobsahuje opakování (de facto zpětný skok), takže se ušetří pět cyklů:
fetch instrukce 4
fetch instrukce 4
čtení bajtu 3
zápis bajtu 5
-------------------
16
8. Úplný zdrojový kód dnešního třetího demonstračního příkladu
Třetí demonstrační příklad, v němž je realizována kopie bloku s využitím instrukce LDIR, vypadá takto:
; Example #86:
; Print ASCII table on screen + copy it to second part of screen using LDIR instruction.
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
ld hl, SCREEN_ADR ; adresa zdrojového bloku
ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku
ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat
ldir ; provést blokový přenos
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řeklad tohoto demonstračního příkladu do strojového kódu bude vypadat následovně:
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:CD1080 CALL 8010
8003:210040 LD HL, 4000
8006:110048 LD DE, 4800
8009:010008 LD BC, 0800
800C:EDB0 LDIR
800E: label finito
800E:18FE JR 800E
8010: label fill_in_screen
8010:110040 LD DE, 4000
8013:CD1780 CALL 8017
8016:C9 RET
8017: label draw_ascii_table
8017:3E20 LD A, 20
8019: label next_char
8019:F5 PUSH AF
801A:CD2980 CALL 8029
801D:3E20 LD A, 20
801F:CD2980 CALL 8029
8022:F1 POP AF
8023:3C INC A
8024:FE80 CP 80
8026:20F1 JR NZ, 8019
8028:C9 RET
8029: label draw_char
8029:01003C LD BC, 3C00
802C:61 LD H, C
802D:6F LD L, A
802E:29 ADD HL, HL
802F:29 ADD HL, HL
8030:29 ADD HL, HL
8031:09 ADD HL, BC
8032:0608 LD B, 08
8034:4A LD C, D
8035: label loop
8035:7E LD A, (HL)
8036:12 LD (DE), A
8037:2C INC L
8038:14 INC D
8039:10FA DJNZ 8035
803B:1C INC E
803C:C8 RET Z
803D:51 LD D, C
803E:C9 RET
803F: END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 803E
9. Instrukce LDI
Připomeňme si, že při použití instrukce LDIR se interně použije celých pět strojových cyklů na vrácení hodnoty registru PC zpět o dva bajty, aby se stejná instrukce mohla načíst znovu. To je poněkud neefektivní a současně je to jediná vlastnost LDIRu, kterou můžeme eliminovat. Namísto LDIR totiž můžeme použít instrukci LDI (bez R na konci), která provádí „pouze“ tuto operaci:
(DE) ← (HL) DE ← DE+1 HL ←- HL+1 BC ←- BC-1
Oproti LDIR zde tedy chybí test hodnoty BC a zpětný skok (opakování smyčky). A právě z tohoto důvodu je dobra trvání této instrukce rovna 16 strojovým cyklům (což již není špatná hodnota).
Této vlastnosti můžeme využít, protože nám to umožňuje použít instrukci LDI několikrát za sebou a test konce smyčky nedělat pokaždé (samozřejmě za předpokladu, že dopředu víme, že velikost bloku je například dělitelná šestnácti atd.). Jedná se tedy o klasickou optimalizaci nazývanou rozbalení smyčky neboli loop unrolling.
10. Programová smyčka založená na instrukci LDI
Prozatím ovšem rozbalení smyčky nebudeme provádět a ukážeme si pouze, jak by vypadalo (opět poněkud naivní) použití instrukce LDI v programové smyčce, v níž se test na nulovost BC provádí v každé iteraci, tj. po přesunu jediného bajtu:
ld hl, SCREEN_ADR ; adresa zdrojového bloku
ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku
ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat
repeat:
ldi ; provést přenos jednoho bajtu
jp pe, repeat ; ukončit blokový přenos při BC==0
Povšimněte si, že se v instrukci podmíněného skoku testuje příznak P, protože právě tento příznak je instrukcí LDI nastavován, i když logicky by se mělo jednat o příznak Z.
Doba jedné iterace je 16 cyklů pro LDI a 10 cyklů pro JP, což je více, než při použití LDIR. Z toho plyne, že tento způsob využití LDIR není v žádném případě vhodný a je nutné provést rozbalení smyčky, jak uvidíme v dalším textu.
11. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu
Dnešní čtvrtý demonstrační příklad, v němž je kopie bloku realizována programovou smyčkou s instrukcí LDI, vypadá následovně:
; Example #87:
; Print ASCII table on screen + copy it to second part of screen using LDI+JP instructions.
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
ld hl, SCREEN_ADR ; adresa zdrojového bloku
ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku
ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat
repeat:
ldi ; provést přenos jednoho bajtu
jp pe, repeat ; ukončit blokový přenos při BC==0
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
Opět si uvedeme, jak se tento demonstrační příklad přeloží 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:CD1380 CALL 8013
8003:210040 LD HL, 4000
8006:110048 LD DE, 4800
8009:010008 LD BC, 0800
800C: label repeat
800C:EDA0 LDI
800E:EA0C80 JP PE, 800C
8011: label finito
8011:18FE JR 8011
8013: label fill_in_screen
8013:110040 LD DE, 4000
8016:CD1A80 CALL 801A
8019:C9 RET
801A: label draw_ascii_table
801A:3E20 LD A, 20
801C: label next_char
801C:F5 PUSH AF
801D:CD2C80 CALL 802C
8020:3E20 LD A, 20
8022:CD2C80 CALL 802C
8025:F1 POP AF
8026:3C INC A
8027:FE80 CP 80
8029:20F1 JR NZ, 801C
802B:C9 RET
802C: label draw_char
802C:01003C LD BC, 3C00
802F:61 LD H, C
8030:6F LD L, A
8031:29 ADD HL, HL
8032:29 ADD HL, HL
8033:29 ADD HL, HL
8034:09 ADD HL, BC
8035:0608 LD B, 08
8037:4A LD C, D
8038: label loop
8038:7E LD A, (HL)
8039:12 LD (DE), A
803A:2C INC L
803B:14 INC D
803C:10FA DJNZ 8038
803E:1C INC E
803F:C8 RET Z
8040:51 LD D, C
8041:C9 RET
8042: END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8041
12. Instrukce LDI v rozbalené smyčce
Blok, který přenášíme, má velikost 2048 bajtů a jeho délka v bajtech je tedy dělitelná šestnácti. To znamená, že test na nulovost registrového páru BC můžeme provést vždy až po přenosu šestnácti bajtů a přitom nenastane situace, kdybychom konec smyčky „minuli“. Již výše zmíněný loop unrolling může vypadat následovně:
ld hl, SCREEN_ADR ; adresa zdrojového bloku
ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku
ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat
repeat:
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
jp pe, repeat ; ukončit blokový přenos při BC==0
Vidíme, že test na nulovost BC je skutečně proveden až po přenosu 16 bajtů a proto je tento kód vhodný pouze pro bloky, které mají velikost celočíselného násobku 16.
13. Doba přenosu jednoho bajtu při použití rozbalené smyčky
Za kolik strojových cyklů se tedy celý blok přenese? Zkusme počítat:
- Provede se 2048 instrukcí "DI: 2048×16=32768 cyklů
- Provede se 2048/16=128 skoků: 128×10=1280 (u JP nezáleží na tom, zda je skok proveden či nikoli)
Celkem je tedy vyžadováno 32768+1280=34048 cyklů. Pro přenos jediného bajtu tedy potřebujeme 34048/2048=16.62 cyklů, což je opět znatelné urychlení oproti 50 cyklům resp. 21 cyklům (přesněji řečeno necelým 21 cyklům, protože poslední běh instrukce LDIR by byl zkrácen o pět cyklů – to je však u takto velkého bloku zanedbatelné).
14. Úplný zdrojový kód dnešního pátého demonstračního příkladu
V pátém demonstračním příkladu je realizována kopie bloku s využitím sekvence instrukcí LDI. Jeho úplný zdrojový kód vypadá následovně:
; Example #88:
; Print ASCII table on screen + copy it to second part of screen using unrolled loop.
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
ld hl, SCREEN_ADR ; adresa zdrojového bloku
ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku
ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat
repeat:
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
jp pe, repeat ; ukončit blokový přenos při BC==0
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
A takto je demonstrační příklad přeložen 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:CD3180 CALL 8031
8003:210040 LD HL, 4000
8006:110048 LD DE, 4800
8009:010008 LD BC, 0800
800C: label repeat
800C:EDA0 LDI
800E:EDA0 LDI
8010:EDA0 LDI
8012:EDA0 LDI
8014:EDA0 LDI
8016:EDA0 LDI
8018:EDA0 LDI
801A:EDA0 LDI
801C:EDA0 LDI
801E:EDA0 LDI
8020:EDA0 LDI
8022:EDA0 LDI
8024:EDA0 LDI
8026:EDA0 LDI
8028:EDA0 LDI
802A:EDA0 LDI
802C:EA0C80 JP PE, 800C
802F: label finito
802F:18FE JR 802F
8031: label fill_in_screen
8031:110040 LD DE, 4000
8034:CD3880 CALL 8038
8037:C9 RET
8038: label draw_ascii_table
8038:3E20 LD A, 20
803A: label next_char
803A:F5 PUSH AF
803B:CD4A80 CALL 804A
803E:3E20 LD A, 20
8040:CD4A80 CALL 804A
8043:F1 POP AF
8044:3C INC A
8045:FE80 CP 80
8047:20F1 JR NZ, 803A
8049:C9 RET
804A: label draw_char
804A:01003C LD BC, 3C00
804D:61 LD H, C
804E:6F LD L, A
804F:29 ADD HL, HL
8050:29 ADD HL, HL
8051:29 ADD HL, HL
8052:09 ADD HL, BC
8053:0608 LD B, 08
8055:4A LD C, D
8056: label loop
8056:7E LD A, (HL)
8057:12 LD (DE), A
8058:2C INC L
8059:14 INC D
805A:10FA DJNZ 8056
805C:1C INC E
805D:C8 RET Z
805E:51 LD D, C
805F:C9 RET
8060: END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 805F
15. Korektní forma rozbalené smyčky pro všechny velikosti bloků
Rozbalení smyčky s instrukcí LDI se zdá být správná cesta pro to, aby se dosáhlo rychlého přenosu bloků dat. Ovšem ideální by bylo, aby se jednalo o univerzální smyčku vhodnou pro bloky o libovolné délce. I toho je možné dosáhnout a jeden z použitých triků je založen na samomodifikujícím se kódu. To znamená, že program na základě nějakého výpočtu mění sám sebe. Podívejme se tedy na to, jak daný trik vypadá v tomto konkrétním případě.
Podprogram pro kopii bloku dat je nyní rozdělen na tři části. V první části se testuje, zda není blok prázdný a navíc se zde počítá relativní adresa 0 až 30 konkrétní instrukce LDI v rozbalené smyčce. Tato relativní adresa je počítána z velikosti přenášeného bloku dat:
mem_copy:
ld a, b ; kontrola BC na nulu
or c
ret z ; při prázdném bloku podprogram ukončíme
ld a, 16 ; na základě hodnoty v C vypočteme, kolik
sub c ; insturkcí LDI se má na začátku přenosu přeskočit
and 15 ; maximálně se přeskočí 15 instrukcí ze 16
add a, a ; vynásobit dvěma protože LDI je dvoubajtová instrukce
Pro bloky o velikosti dělitelné 16 je relativní adresa rovna nule, pro bloky, jejichž velikost je o bajt větší se vypočte adresa 30 atd.
Nyní nastal čas pro použití samomodifikujícího se kódu: vypočtenou relativní adresu uložíme za operační kód instrukce JR (relativní skok), protože víme, že tato instrukce má délku dvou bajtů: operační kód+relativní adresa. To se v assembleru Pasmo provede takto (a trik bude funkční i v dalších assemblerech, které rozeznávají symbol $ jako aktuální adresu zpracovávané instrukce):
ld (jump_address), a ; uložíme offset do tohoto paměťového místa
jr $ ; relativní skok přečte offset z ^
jump_address equ $-1 ; trik jak zasáhnout do operandu instrukce JR
Nyní je již situace jednoduchá, protože JR provede skok doprostřed rozbalené smyčky. Pro bloky dělitelné 16 skočí na začátek smyčky, pro ostatní bloky na jednu ze zbývajících 15 instrukcí LDI. Další iterace pak budou provedeny pro všech 16 instrukcí LDI:
repeat:
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
jp pe, repeat ; ukončit blokový přenos při BC==0
ret ; návrat z podprogramu
16. Úplný zdrojový kód dnešního šestého demonstračního příkladu
Na závěr si ukažme, jak vypadá úplný kód demonstračního příkladu realizujícího kopii paměťových bloků založeného na sekvenci instrukcí LDI:
; Example #88:
; Print ASCII table on screen + copy it to second part of screen using unrolled loop.
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
ld hl, SCREEN_ADR ; adresa zdrojového bloku
ld de, SECOND_SCREEN_BLOCK ; adresa cílového bloku
ld bc, SCREEN_BLOCK_SIZE ; velikost přenášených dat
call mem_copy
finito:
jr finito ; ukončit program nekonečnou smyčkou
mem_copy:
ld a, b ; kontrola BC na nulu
or c
ret z ; při prázdném bloku podprogram ukončíme
ld a, 16 ; na základě hodnoty v C vypočteme, kolik
sub c ; insturkcí LDI se má na začátku přenosu přeskočit
and 15 ; maximálně se přeskočí 15 instrukcí ze 16
add a, a ; vynásobit dvěma protože LDI je dvoubajtová instrukce
ld (jump_address), a ; uložíme offset do tohoto paměťového místa
jr $ ; relativní skok přečte offset z ^
jump_address equ $-1 ; trik jak zasáhnout do operandu instrukce JR
repeat:
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
ldi ; provést přenos jednoho bajtu
jp pe, repeat ; ukončit blokový přenos při BC==0
ret ; návrat z podprogramu
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řeklad 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:CD4380 CALL 8043
8003:210040 LD HL, 4000
8006:110048 LD DE, 4800
8009:010008 LD BC, 0800
800C:CD1180 CALL 8011
800F: label finito
800F:18FE JR 800F
8011: label mem_copy
8011:78 LD A, B
8012:B1 OR C
8013:C8 RET Z
8014:3E10 LD A, 10
8016:91 SUB C
8017:E60F AND 0F
8019:87 ADD A, A
801A:321E80 LD (801E), A
801D:18FE JR 801D
jump_address EQU 801E
801F: label repeat
801F:EDA0 LDI
8021:EDA0 LDI
8023:EDA0 LDI
8025:EDA0 LDI
8027:EDA0 LDI
8029:EDA0 LDI
802B:EDA0 LDI
802D:EDA0 LDI
802F:EDA0 LDI
8031:EDA0 LDI
8033:EDA0 LDI
8035:EDA0 LDI
8037:EDA0 LDI
8039:EDA0 LDI
803B:EDA0 LDI
803D:EDA0 LDI
803F:EA1F80 JP PE, 801F
8042:C9 RET
8043: label fill_in_screen
8043:110040 LD DE, 4000
8046:CD4A80 CALL 804A
8049:C9 RET
804A: label draw_ascii_table
804A:3E20 LD A, 20
804C: label next_char
804C:F5 PUSH AF
804D:CD5C80 CALL 805C
8050:3E20 LD A, 20
8052:CD5C80 CALL 805C
8055:F1 POP AF
8056:3C INC A
8057:FE80 CP 80
8059:20F1 JR NZ, 804C
805B:C9 RET
805C: label draw_char
805C:01003C LD BC, 3C00
805F:61 LD H, C
8060:6F LD L, A
8061:29 ADD HL, HL
8062:29 ADD HL, HL
8063:29 ADD HL, HL
8064:09 ADD HL, BC
8065:0608 LD B, 08
8067:4A LD C, D
8068: label loop
8068:7E LD A, (HL)
8069:12 LD (DE), A
806A:2C INC L
806B:14 INC D
806C:10FA DJNZ 8068
806E:1C INC E
806F:C8 RET Z
8070:51 LD D, C
8071:C9 RET
8072: END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8071
17. Obsah navazujícího článku
Mohlo by se zdát, že trik s rozbalenou programovou smyčkou založenou na instrukci LDI je to nejlepší, čeho můžeme na mikroprocesoru Z80 dosáhnout. Ve skutečnosti tomu tak není, protože byl objeven i další trik, který využívá instrukce pro ukládání a obnovování obsahu registrových párů (dvou bajtů!) na zásobníku. Příště si ukážeme, jak byl tento trik využíván. Jen pro ukázku – přenos šestnácti bajtů může být realizován i takto, přičemž doba trvání přenosu jednoho bajtu klesne na přibližně 13 cyklů:
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
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 deseti článcích [1] [2], [3], [4], [5], [6], [7], [8], [9], [10], 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
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
x.tap: x.asm
$(ASSEMBLER) -v -d --tapbas $< $@ > x.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ů využívá zásobník | https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/90-copy-ascii-table-F.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