Hlavní navigace

Vývoj pro ZX Spectrum: mikroprocesor Zilog Z80 a smyčky v assembleru

14. 2. 2023
Doba čtení: 34 minut

Sdílet

 Autor: Depositphotos
Ve druhém článku o vývoji programů pro ZX Spectrum si popíšeme mikroprocesor Zilog Z80 a následně si ukážeme, jak realizovat a následně optimalizovat jednoduché počítané programové smyčky v assembleru.

Obsah

1. Krátké zopakování z minula: symboly zastupující konkrétní hodnoty

2. Kódy barev a bitové operace podporované assemblerem Pasmo

3. Automatické vygenerování „loaderu“ pro obraz magnetofonové pásky

4. Mikroprocesor Zilog Z80 z pohledu programátora

5. Sada univerzálních a speciálních registrů

6. Sada instrukcí mikroprocesoru Z80

7. Proč se jména instrukcí na Z80 odlišují od Intel 8080?

8. Adresovací režimy a všudypřítomná instrukce LD

9. Vybrané instrukce použité v demonstračních příkladech

10. Jednoduchá počítaná programová smyčka: naivní varianta

11. Celková délka kódu, časování instrukcí

12. Zkrácení kódu pro vynulování použitých pracovních registrů

13. Optimalizace skoku na konci smyčky (instrukce DJNZ)

14. Optimalizace využití pracovních registrů

15. Náhrada registrového páru HL za index registry IX nebo IY?

16. Porovnání kódů založených na registrech HL, IX a IY

17. Programová smyčka se šestnáctibitovým počitadlem: naivní varianta

18. Dosažení prakticky stejné rychlosti, jako v případě smyčky s osmibitovým počitadlem

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Krátké zopakování z minula: symboly zastupující konkrétní hodnoty

Na úvodní článek o vývoji programů pro legendární osmibitový domácí mikropočítač ZX Spectrum dnes navážeme. Ve stručnosti si popíšeme mikroprocesor Zilog Z80 z pohledu programátora a následně si ukážeme, jak lze realizovat jednoduché počítané programové smyčky v assembleru. Uvidíme, že původní (naivní) realizaci programové smyčky je možné různým způsobem optimalizovat, a to buď na celkovou délku kódu či na jeho rychlost.

Nejprve si však připomeňme, jak je možné v assembleru napsat program, který po svém spuštění modifikuje první barvový atribut v obrazové paměti takovým způsobem, že se v dané oblasti 8×8 pixelů objeví blikající čtverec. Program je triviální – musí pouze zapsat vhodnou konstantu na adresu 0×5800, což je začátek oblasti paměti s barvovými atributy:

        org $8000
 
start:
        ld a,%11010110
        ld ($5800),a
        ret

Pro lepší čitelnost i modifikovatelnost celého programu je vhodné všechny „magické konstanty“ pojmenovat, a to (v případě použití assembleru Pasmo) s využitím direktivy equ:

attribute_adr equ $5800
entry_point   equ $8000
 
 
        org entry_point
 
start:
        ld a,%11010110
        ld (attribute_adr),a
        ret

Použití symbolických konstant přitom nijak nezvětšuje velikost výsledného strojového kódu.

Výsledek by měl po překladu a spuštění vypadat následovně (obraz se periodicky mezi oběma snímky přepíná):

Obrázek 1: Blikající atribut v levém horním rohu obrazovky.

Obrázek 2: Blikající atribut v levém horním rohu obrazovky.

2. Kódy barev a bitové operace podporované assemblerem Pasmo

Assembler Pasmo, ostatně podobně jako mnohé další moderní assemblery, podporuje již v době překladu provádět výpočty nad konstantami i symbolickými hodnotami. Podporovány jsou například bitové operátory součtu a součinu, stejně jako bitové posuny. To nám umožní nechat si v čase překladu (tedy z pohledu výsledného programu „zadarmo“) spočítat hodnotu bajtu ukládaného do atributové paměti na základě zadaných barev popředí a pozadí i hodnot bitu řídicího blikání a vysokou intenzitu popředí. Nejprve si příslušné konstanty nadefinujeme, a to přímo v binární podobě (začínají znakem procenta a nikoli dolaru, jako v případě hexadecimálních hodnot):

blink_bit     equ %10000000
intensity_bit equ %01000000
black_color   equ %000
blue_color    equ %001
red_color     equ %010
magenta_color equ %011
green_color   equ %100
cyan_color    equ %101
yellow_color  equ %110
white_color   equ %111

Následně je možné hodnotu ukládanou do atributového bajtu vypočítat podobně jako v céčku s využitím bitových posunů a bitové operace součtu:

        ld a,blink_bit | intensity_bit | (blue_color << 3) | white_color

Upravený zdrojový kód příkladu bude nyní vypadat následovně:

attribute_adr equ $5800
entry_point   equ $8000
 
blink_bit     equ %10000000
intensity_bit equ %01000000
black_color   equ %000
blue_color    equ %001
red_color     equ %010
magenta_color equ %011
green_color   equ %100
cyan_color    equ %101
yellow_color  equ %110
white_color   equ %111
 
 
        org entry_point
 
start:
        ld a,blink_bit | intensity_bit | (blue_color << 3) | white_color
        ld (attribute_adr),a
        ret

Z výsledku překladu je zřejmé, že se z výrazu vypočetla konstanta 0×CF (neboli %11001111) použitá v instrukci LD:

attribute_adr   EQU 5800
entry_point     EQU 8000
blink_bit       EQU 0080
intensity_bit   EQU 0040
black_color     EQU 0000
blue_color      EQU 0001
red_color       EQU 0002
magenta_color   EQU 0003
green_color     EQU 0004
cyan_color      EQU 0005
yellow_color    EQU 0006
white_color     EQU 0007
                ORG 8000
8000:           label start
8000:3ECF       LD A, CF
8002:320058     LD (5800), A
8005:C9         RET
Emiting TAP from 8000 to 8005
Poznámka: mnozí programátoři dávají přednost tomu, aby se symbolická jména zapisovala velkými písmeny, tedy následovně:
ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
BLINK_BIT     equ %10000000
INTENSITY_BIT equ %01000000
BLACK_COLOR   equ %000
BLUE_COLOR    equ %001
RED_COLOR     equ %010
MAGENTA_COLOR equ %011
GREEN_COLOR   equ %100
CYAN_COLOR    equ %101
YELLOW_COLOR  equ %110
WHITE_COLOR   equ %111
 
 
        org ENTRY_POINT
 
start:
        ld a,BLINK_BIT | INTENSITY_BIT | (BLUE_COLOR << 3) | WHITE_COLOR
        ld (ATTRIBUTE_ADR),a
        ret

Na reálnou podobu výsledného strojového kódu to ovšem pochopitelně nebude mít žádný vliv:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
BLINK_BIT       EQU 0080
INTENSITY_BIT   EQU 0040
BLACK_COLOR     EQU 0000
BLUE_COLOR      EQU 0001
RED_COLOR       EQU 0002
MAGENTA_COLOR   EQU 0003
GREEN_COLOR     EQU 0004
CYAN_COLOR      EQU 0005
YELLOW_COLOR    EQU 0006
WHITE_COLOR     EQU 0007
                ORG 8000
8000:           label start
8000:3ECF       LD A, CF
8002:320058     LD (5800), A
8005:C9         RET
Emiting TAP from 8000 to 8005

Obrázek 3: Loading screen hry Indiana Jones a Chrám zkázy.

3. Automatické vygenerování „loaderu“ pro obraz magnetofonové pásky

Mezi jednu z užitečných vlastností assembleru Pasmo patří schopnost automatického vygenerování BASICovského „loaderu“, jenž zajistí načtení strojového kódu s jeho následným spuštěním. V praxi to znamená, že není nutné psát příkazy LOAD ""CODE a RANDOMIZE USR 32768 nebo PRINT USR 32768 (popř. uvést odlišnou adresu). K tomu, aby se loader automaticky přidal na obraz pásky, je nutné při spuštění assembleru použít volbu –tapbas a ukončit program klauzulí end:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
BLINK_BIT     equ %10000000
INTENSITY_BIT equ %01000000
BLACK_COLOR   equ %000
BLUE_COLOR    equ %001
RED_COLOR     equ %010
MAGENTA_COLOR equ %011
GREEN_COLOR   equ %100
CYAN_COLOR    equ %101
YELLOW_COLOR  equ %110
WHITE_COLOR   equ %111
 
 
        org ENTRY_POINT
 
start:
        ld a,BLINK_BIT | INTENSITY_BIT | (BLUE_COLOR << 3) | WHITE_COLOR
        ld (ATTRIBUTE_ADR),a
        ret
 
end

Povšimněte si, že assembler vypsal informaci o tom, že generuje loader (viz zvýrazněnou zprávu):

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
BLINK_BIT       EQU 0080
INTENSITY_BIT   EQU 0040
BLACK_COLOR     EQU 0000
BLUE_COLOR      EQU 0001
RED_COLOR       EQU 0002
MAGENTA_COLOR   EQU 0003
GREEN_COLOR     EQU 0004
CYAN_COLOR      EQU 0005
YELLOW_COLOR    EQU 0006
WHITE_COLOR     EQU 0007
                ORG 8000
8000:           label start
8000:3ECF       LD A, CF
8002:320058     LD (5800), A
8005:C9         RET
8006:           END
Emiting TAP basic loader
Emiting TAP from 8000 to 8005
ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
BLINK_BIT     equ %10000000
INTENSITY_BIT equ %01000000
BLACK_COLOR   equ %000
BLUE_COLOR    equ %001
RED_COLOR     equ %010
MAGENTA_COLOR equ %011
GREEN_COLOR   equ %100
CYAN_COLOR    equ %101
YELLOW_COLOR  equ %110
WHITE_COLOR   equ %111
 
        org ENTRY_POINT
 
start:
        ld a,BLINK_BIT | INTENSITY_BIT | (BLUE_COLOR << 3) | WHITE_COLOR
        ld (ATTRIBUTE_ADR),a
        ret
 
end ENTRY_POINT
ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
BLINK_BIT       EQU 0080
INTENSITY_BIT   EQU 0040
BLACK_COLOR     EQU 0000
BLUE_COLOR      EQU 0001
RED_COLOR       EQU 0002
MAGENTA_COLOR   EQU 0003
GREEN_COLOR     EQU 0004
CYAN_COLOR      EQU 0005
YELLOW_COLOR    EQU 0006
WHITE_COLOR     EQU 0007
                ORG 8000
8000:           label start
8000:3ECF       LD A, CF
8002:320058     LD (5800), A
8005:C9         RET
8006:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8005

Podívejme se nyní na to, jak vypadá loader, který byl assemblerem Pasmo vygenerován:

Obrázek 4: Takto vypadá loader automaticky vygenerovaný assemblerem.

Poznámka: nyní je již možné příklad spustit pouze inicializací emulátoru s uvedením jména souboru s obrazem pásky. Popř. v případě použití volně dostupné varianty ROM postačuje pouze program spustit.

4. Mikroprocesor Zilog Z80 z pohledu programátora

Programátorský model mikroprocesoru Zilog Z80 se v mnoha ohledech odlišuje od jeho největšího dobového konkurenta – mikroprocesoru MOS 6502. Zatímco MOS 6502 byl v mnoha ohledech čistě osmibitovým čipem (i index registry měly šířku pouhých osmi bitů) a většina operací byla implicitně prováděna s akumulátorem A, je tomu u Z80 jinak. Z80 totiž obsahuje větší počet univerzálních registrů a i když většina ALU operací vyžadovala jako jeden z operandů akumulátor, další operace se prováděly i s ostatními registry. Navíc se pracovní registry mohly spojovat do šestnáctibitových registrových párů a existovalo i několik čistě šestnáctibitových aritmetických instrukcí (zvýšení a snížení obsahu registrového páru atd.). K dispozici byla i dvojice čistě 16bitových index registrů atd.

Obrázek 5: Tento obrázek jsem viděl dosti často.

Výsledkem je, že tvorba programů vyžaduje zcela odlišné přístupy. U MOS 6502 se do značné míry využívá nulté stránky paměti (prvních 256 bajtů) zkombinované s pokročilými adresovacími režimy, zatímco u Z80 se spíše využívá široké sady pracovních registrů s tím, že adresování je oproti MOS 6502 poněkud omezeno. Některé přístupy si ostatně budeme moci porovnat s programy (či jejich částmi) psanými pro MOS 6502 a herní konzoli NES.

5. Sada univerzálních a speciálních registrů

Mikroprocesor Zilog Z80 obsahuje celkem osmnáct osmibitových registrů a čtyři šestnáctibitové registry, přičemž všechny registry jsou implementovány formou statické RAM. Všechny zmíněné registry jsou rozděleny do několika skupin. Začneme univerzálními registry (i když pojem „univerzální“ zde má poněkud jiný význam, než u moderních mikroprocesorů, protože mnoho operací se provádí pouze s akumulátorem). Těch je sedm:

Registr Registr Pár
A   (AF)
B C BC
D E DE
H L HL (M)

Z tabulky je patrné, že vždy dvojici osmibitových registrů lze použít jako registrový pár, tedy jako 16bitovou hodnotu, například pro adresování atd. U Intelu 8080 se registrový pár HL nazýval M (memory). Navíc se setkáme i s registrovým párem AF, což je akumulátor spojený s příznakovými bity.

Všechny univerzální registry existují ve dvou kopiích – jsou tedy zdvojeny (navíc je zdvojen i příznakový registr). To například umožňuje snadnou tvorbu přerušovacích rutin atd. a současně je adresování registrů v instrukcích stále velmi krátké (tři bity):

Registr Registr
A'  
B' C'
D' E'
H' L'

Obrázek 6: Belegost pro ZX Spectrum – loading screen.

Všechny další registry mají speciální význam a neexistují jejich kopie:

Registr Šířka Popis
IX 16b indexový resp. bázový registr pro adresování
IY 16b indexový resp. bázový registr pro adresování
SP 16b ukazatel na vrchol zásobníku (Stack Pointer)
PC 16b programový čítač (Program Counter)
I 8b vyšší bajt adresy tabulky obsluhy přerušení
R 8b(7b) automaticky zvyšovaný čítač pro občerstvování (Refresh) pamětí DRAM

Obrázek 7: Belegost pro ZX Spectrum – grafické uživatelské rozhraní hry.

6. Sada instrukcí mikroprocesoru Z80

Procesor Zilog Z80 podporuje všechny instrukce svého ideového předchůdce Intelu 8080 a navíc přidává mnoho instrukcí dalších; další instrukce byly naopak upraveny tak, že akceptují další operandy. Navíc došlo k přejmenování mnemotechnických zkratek instrukcí (viz navazující kapitolu), což je z pohledu programátora asi jen dobře (8080 totiž obsahoval zbytečné „špeky“ typu LXI, LHLD, SPHL atd.). Celou instrukční sadu lze zhruba rozdělit do těchto kategorií:

Přesuny osmibitových operandů (zde nazýváno load, nikoli move)
Přesuny šestnáctibitových operandů
Blokové přesuny a vyhledávání
Výměna operandů (exchange)
Osmibitové aritmetické instrukce
Šestnáctibitové aritmetické instrukce
Rotace a bitové posuny
Vynulování, nastavení a test bitů
Řízení CPU atd.
Skoky (podmíněné/nepodmíněné, absolutní/relativní)
Volání podprogramů a návrat z podprogramů
Operace se vstupními a výstupními porty

Dnes si popíšeme pouze malou část instrukcí; podrobnější přehled bude uveden příště.

7. Proč se jména instrukcí na Z80 odlišují od Intel 8080?

I když je mikroprocesor Zilog Z80 do značné míry binárně zpětně kompatibilní s mikroprocesorem Intel 8080, používají se v assembleru odlišné názvy instrukcí. Je tomu tak ze dvou důvodů. Prvním důvodem je, že společnost Intel tvrdila, že drží vlastnická práva na mnemotechnické zkratky instrukcí (které původně pochází z Datapointu 2200 a byly poměrně náhodně doplňovány o další instrukce), druhý důvod spočívá ve snaze o zjednodušení celé instrukční sady a o dosažení alespoň zdání její ortogonality (například se již nerozlišuje mezi instrukcemi s registrem a osmibitovou konstantou atd.). Někdy je ovšem nové jméno instrukce maličko matoucí, což je případ CMP versus CP, která měla původně jiný význam. Celá „Rosettská deska“ je dostupná na adrese https://retroprogramming.it/2021/02/8080-z80-instruction-set/ a v následující tabulce jsou změny přehledně vypsány (vynechávám ovšem variant s dalšími pracovními registry atd.):

Zápis podle Intel 8080 Zápis podle Zilog Z80 Poznámka
XCHG EX DE,HL náhrada za obecnější instrukci EX
XTHL EX (SP),HL náhrada za obecnější instrukci EX
ADD A ADD A,A explicitní uvedení obou operandů
ADD B ADD A,B dtto
ADD M ADD A,(HL) explicitní uvedení adresovacího režimu a registrového páru
SBB A SBC A explicitní uvedení obou operandů
SBB B SBC B dtto
SBB M SBC (HL) explicitní uvedení adresovacího režimu a registrového páru
DAD B ADD HL,BC explicitní uvedení obou registrových párů, náhrada za obecnější instrukci ADD
DAD D ADD HL,DE dtto
DAD H ADD HL,HL dtto
DAD SP ADD HL,SP dtto
INR A INC A náhrada za obecnější instrukci INC
INR B INC B náhrada za obecnější instrukci INC
INR M INC (HL) náhrada za obecnější instrukci INC, zápis registrového páru
DCR A DEC A dtto jako DCR/DEC
DCR B DEC B dtto jako DCR/DEC
DCR M DEC (HL) dtto jako DCR/DEC
INX B INC BC explicitní uvedení obou registrového páru, náhrada za obecnější instrukci INC
INX D INC DE dtto
INX H INC HL dtto
INX SP INC SP dtto
DCX B DEC BC explicitní uvedení obou registrového páru, náhrada za obecnější instrukci DEC
DCX D DEC DE dtto
DCX H DEC HL dtto
DCX SP DEC SP dtto
RLC RLCA  
RRC RRCA  
RAL RLA  
RAR RRA  
ANA A AND A náhrada za obecnější instrukci AND
ANA B AND B dtto
ANA M AND (HL) explicitní uvedení adresovacího režimu a registrového páru
ANI byte AND byte náhrada za obecnější instrukci AND i pro konstanty
XRA A XOR A náhrada za obecnější instrukci XOR
XRA B XOR B dtto
XRA M XOR (HL) explicitní uvedení adresovacího režimu a registrového páru
XRI byte XOR byte náhrada za obecnější instrukci XOR
ORA A OR A náhrada za obecnější instrukci OR i pro konstanty
ORA B OR B dtto
ORA M OR (HL) explicitní uvedení adresovacího režimu a registrového páru
ORI byte OR byte náhrada za obecnější instrukci OR
CMP A CP A náhrada za obecnější instrukci CP
CMP B CP B dtto
CMP M CP (HL) explicitní uvedení adresovacího režimu a registrového páru
CPI byte CP byte náhrada za obecnější instrukci CP i pro konstanty
JMP address JP address všechny skoky jsou nyní zapisovány formou JP+případná podmínka
JNZ address JP NZ,address dtto
JZ address JP Z,address dtto
JNC address JP NC,address dtto
JC address JP C,address dtto
JPO address JP PO,address dtto
JPE address JP PE,address dtto
JP address JP P,address dtto
JM address JP M,address dtto
PCHL JP (HL) nepřímý skok na adresu uloženou v registrovém páru
CALL address CALL address
CNZ address CALL NZ,address všechna volání jsou nyní zapisována formou CALL+případná podmínka
CZ address CALL Z,address dtto
CNC address CALL NC,address dtto
CC address CALL C,address dtto
CPO address CALL PO,address dtto
CPE address CALL PE,address dtto
CP address CALL P,address dtto
CM address CALL M,address dtto
RNZ RET NZ všechny návraty z podprogramů jsou nyní zapisovány formou RET+případná podmínka
RZ RET Z dtto
RNC RET NC dtto
RC RET C dtto
RPO RET PO dtto
RPE RET PE dtto
RP RET P dtto
RM RET M dtto
RST 0 RST 0 explicitní uvedení adresy vektoru
RST 1 RST 8 dtto
RST 2 RST 10H dtto
RST 3 RST 18H dtto
RST 4 RST 20H dtto
RST 5 RST 28H dtto
RST 6 RST 30H dtto
RST 7 RST 38H dtto
PUSH B PUSH BC explicitní uvedení celého jména registrového páru
PUSH D PUSH DE dtto
PUSH H PUSH HL dtto
PUSH PSW PUSH AF dtto (náhrada za program status word)
POP B POP BC explicitní uvedení celého jména registrového páru
POP D POP DE dtto
POP H POP HL dtto
POP PSW POP AF dtto (náhrada za program status word)
IN byte IN A,(byte) explicitní uvedení operandu + naznačení nepřímé adresace
OUT byte OUT (byte),A dtto
Poznámka: instrukce LD, která na Zilogu Z80 nahrazuje hned několik instrukcí mikroprocesoru Intel 8080, bude uvedena v navazující kapitole.

8. Adresovací režimy a všudypřítomná instrukce LD

Největší změny v instrukční sadě Z80 oproti 8080 se týkají instrukcí pro přenosy dat mezi registry, mezi registrem a pamětí, mezi registrovými páry a taktéž instrukcí pro načtení konstanty do registru nebo registrového páru. Celá sada původně odlišných instrukcí byla nahrazena jedinou instrukcí LD neboli load. Kromě toho došlo k náhradě pseudoregistru M za (HL), protože se nejedná o nic jiného, než o přístup k obsahu adresy uložené právě v registrovém páru HL (který navíc nikdy neměl výsadní postavení – viz použití dalších párů):

Zápis podle Intel 8080 Zápis podle Zilog Z80 Poznámka
MOV A,A LD A,A náhrada jména instrukce
MOV A,B LD A,B dtto
MOV A,M LD A,(HL) náhrada M za explicitní jméno registrového páru
LDAX B LD A,(BC) explicitní uvedení celého registrového páru i způsobu adresování
LDAX D LD A,(DE) dtto
LDA word LD A,(word) explicitní uvedení způsobu adresování
MOV M,A LD (HL),A náhrada M za explicitní jméno registrového páru
MVI A,byte LD A,byte už se nerozlišuje mezi MOVE IMMEDIATE a prostou instrukcí LD s konstantou
STAX B LD (BC),A explicitní uvedení celého registrového páru i způsobu adresování
STAX D LD (DE),A dtto
LXI B,word LD BC,word už se nerozlišuje mezi LOAD IMMEDIATE a prostou instrukcí LD s konstantou
LXI SP,word LD SP,word dtto
LHLD word LD HL,(word) explicitní uvedení celého registrového páru i způsobu adresování
SHLD word LD (word),HL explicitní uvedení celého registrového páru i způsobu adresování
SPHL LD SP,HL náhrada univerzálnější instrukcí
Poznámka: původní „poslepovaná“ sada instrukcí 8080 (v podstatě sada instrukcí pro 8008 rozšířená o 16bitové instrukce) se tak opět stává více ortogonální.

Navíc došlo i k přidání nového adresovacího režimu (IX+d) a (IY+d), v němž nové index registry obsahují bázovou adresu, ke které se přičítá zadaná osmibitová konstanta.

9. Vybrané instrukce použité v demonstračních příkladech

V demonstračních příkladech uvedených v následujících kapitolách je použito pouze několik instrukcí. Ty jsou, společně se stručným popisem, vypsány v následující tabulce:

Strojový kód Instrukce Operandy Stručný popis instrukce
3Exx LD A, xx načtení osmibitové konstanty do akumulátoru
06×x LD B, xx načtení osmibitové konstanty do registru B
78 LD A, B přesun dat z registru B do akumulátoru
47 LD B, A přesun dat z akumulátoru do registru B
45 LD B, L přesun dat z registru L do registru B
01×xxx LD BC, xxxx načtení šestnáctibitové konstanty do registrového páru BC
21×xxx LD HL, xxxx načtení šestnáctibitové konstanty do registrového páru HL
DD21×xxx LD IX, xxxx načtení šestnáctibitové konstanty do index registru IX
FD21×xxx LD IY, xxxx načtení šestnáctibitové konstanty do index registru IY
32×xxx LD (xxxx), A uložení akumulátoru na adresu xxxx
77 LD (HL), A uložení akumulátoru na adresu uloženou v registrovém páru HL
75 LD (HL), L uložení registru L na adresu uloženou v registrovém páru HL
DD77×x LD (IX+xx), A uložení akumulátoru na adresu uloženou v registru IX zvýšenou o offset xx
FD77×x LD (IY+xx), A uložení akumulátoru na adresu uloženou v registru IY zvýšenou o offset xx
3C INC A zvýšení obsahu akumulátoru o jedničku
DD23 INC IX zvýšení obsahu index registru IX o jedničku
FD23 INC IY zvýšení obsahu index registru IY o jedničku
3D DEC A snížení obsahu akumulátoru o jedničku (nastaví se příznaky)
05 DEC B snížení obsahu registru B o jedničku (nastaví se příznaky)
0B DEC BC snížení registrového páru BC o jedničku (bez nastavení příznaků)
2C INC L snížení obsahu registru L o jedničku (nastaví se příznaky)
23 INC HL snížení registrového páru HL o jedničku (bez nastavení příznaků)
AF XOR A vynulování akumulátoru
B1 OR C logická operace součtu mezi akumulátorem a registrem C
C2×xxx JP NZ, xxxx absolutní skok na adresu při splnění podmínky not zero flag
20rr JR NZ, rr relativní skok na adresu při splnění podmínky not zero flag
10rr DJNZ rr snížení hodnoty registru B a relativní skok za podmínky, že nová hodnota registru B není nulová
C9 RET   návrat z podprogramu

Obrázek 8: Typické pruhy na okraji obrazovky, které jsou zobrazovány při nahrávání programů z kazety. Tyto pruhy se vytváří programově a je možné je například využít ve chvíli, kdy je zapotřebí odladit nějakou časově náročnější rutinu atd. (zvýrazní se začátek a konec rutiny). I příklady uvedené v dalším textu mají s těmito pruhy mnoho společného – jakákoli změna smyčky (například typický „off by one error“) bude ihned vizuálně viditelná na obrazovce. Z tohoto pohledu přináší speccy poměrně vyspělé ladicí techniky (a to myslím zcela vážně).

10. Jednoduchá počítaná programová smyčka: naivní varianta

Ve druhé části dnešního článku si některé operace vyzkoušíme na praktických příkladech. Začneme zdánlivě triviální úlohou, která by na moderních čipech (AArch64, RISC-V apod.) měla jen jedno správné řešení. Jak ovšem uvidíme dále, na Zilogu Z80 je situace z pohledu programátora mnohem zábavnější a úlohu lze řešit různými způsoby.

Samotné zadání je jednoduché: máme vyplnit 256bajtový blok paměti od adresy 0×5800 sekvencí hodnot 0 až 255. Připomeňme si, že od adresy 0×5800 začíná blok barvových atributů, takže po spuštění programu ihned uvidíme, zda náš program pracuje korektně. Výsledek by měl vypadat následovně (vyplňujeme jen barvové atributy, takže text vypisovaný interpretrem BASICu do změněné oblasti, je jen třešničkou na dortu):

Obrázek 9: První třetina atributové paměti vyplněná hodnotami 0..255 (varianta po spuštění se standardní ROM, první obrázek).

Nastavení nejvyššího bitu atributu vede k blikání dané oblasti 8×8 pixelů:

Obrázek 10: První třetina atributové paměti vyplněná hodnotami 0..255 (varianta po spuštění se standardní ROM, po „blink“).

Varianta po spuštění s emulátorem s ROM s OpenSE BASICem:

Obrázek 11: Příklad spuštění v emulátoru s OpenSE BASICem.

Obrázek 12: Příklad spuštění v emulátoru s OpenSE BASICem.

Podívejme se nyní na poněkud naivní (nebo spíše přímočarý) způsob realizace programové smyčky s řešením výše uvedeného zadání. Ve smyčce se používá dvojregistr HL ve funkci ukazatele do právě zapisované paměťové buňky, takže se v každé iteraci jeho hodnota zvyšuje o jedničku. Dále používáme registr A, jenž obsahuje zapisovanou hodnotu (opět musíme obsah registru postupně zvyšovat) a registr B slouží jako počitadlo smyčky, které se naopak postupně snižuje od hodnoty 0 (resp. 256, protože první dekrementace nuly vrátí výsledek 255) k nule. Teprve po dosažení nuly operací dec B se nastaví příznakový bit „zero“ a smyčka se ukončí:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld hl, ATTRIBUTE_ADR  ; adresa pro zápis
        ld a, 0               ; zapisovaná hodnota
        ld b, 0               ; počitadlo smyčky
 
loop:
        ld (hl),a             ; zápis hodnoty na adresu (HL)
        inc hl                ; zvýšení adresy
        inc a                 ; zvýšení zapisované hodnoty
        dec b                 ; snížení hodnoty počitadla
        jp NZ, loop           ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT
Poznámka: už nyní jste možná našli mnoho částí kódu, které se dají zkrátit či urychlit.

11. Celková délka kódu, časování instrukcí

Nyní se podívejme na to, jakým způsobem se předchozí programová smyčka přeložila do strojového kódu:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:210058     LD HL, 5800
8003:3E00       LD A, 00
8005:0600       LD B, 00
8007:           label loop
8007:77         LD (HL), A
8008:23         INC HL
8009:3C         INC A
800A:05         DEC B
800B:C20780     JP NZ, 8007
800E:C9         RET
800F:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 800E

Výsledný strojový kód má délku patnácti bajtů (což později dokážeme zkrátit na devět bajtů). A jak je tomu s rychlostí výpočtu, resp. počtem cyklů na instrukci? Pomůže nám následující velmi užitečná stránka https://clrhome.org/table/, protože jednou z výhod osmibitových mikroprocesorů je, že počet cyklů lze spočítat poměrně snadno (nejsou superskalární, nemají spekulativní provádění instrukcí ani branch delay sloty):

Instrukce Počet cyklů × počet iterací
ld hl, adresa 10 10
ld a, 0 7 7
ld b, 0 7 7
ld (hl),a 7 1792
inc hl 6 1536
inc a 4 1024
dec b 4 1024
jp NZ, loop 10 2560
ret 10 10
Celkem:   7970

Teoreticky by tedy smyčka měla být dokončena za 7970 cyklů. V praxi to bude více, a to kvůli obsluhám přerušení a přístupům čipu ULA do paměti (což si vysvětlíme později).

Poznámka: pokud nahradíme JP za JR, ušetříme jeden bajt, ale doba trvání se zvýší o 255×2–7=503 cyklů.

12. Zkrácení kódu pro vynulování použitých pracovních registrů

V první verzi programu se před vstupem do programové smyčky vynulovaly osmibitové pracovní registry A i B instrukcemi, které jsou uloženy ve dvou bajtech (druhý bajt pochopitelně obsahuje zapisovanou hodnotu):

8003:3E00       LD A, 00
8005:0600       LD B, 00

Ovšem stejného výsledku můžeme dosáhnout malým trikem: vynulováním obsahu registru A například instrukcí XOR (se sebou samým) nebo SUB (opět se sebou samým) a následně přesunout výslednou nulovou hodnotu do registru B instrukcí LD B,A. Upravený zdrojový kód příkladu bude nyní vypadat následovně:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld hl, ATTRIBUTE_ADR  ; adresa pro zápis
        xor a                 ; zapisovaná hodnota
        ld b, a               ; počitadlo smyčky
 
loop:
        ld (hl),a             ; zápis hodnoty na adresu (HL)
        inc hl                ; zvýšení adresy
        inc a                 ; zvýšení zapisované hodnoty
        dec b                 ; snížení hodnoty počitadla
        jp NZ, loop           ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

Po překladu takto upraveného příkladu je zřejmé, že program bude kratší o dva bajty. Tato úspora z dnešního pohledu nemusí znamenat mnoho, ale na počítačích s 16kB nebo 48kB paměti RAM se i podobné „maličkosti“ mohou postupně nasčítat:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:210058     LD HL, 5800
8003:AF         XOR A
8004:47         LD B, A
8005:           label loop
8005:77         LD (HL), A
8006:23         INC HL
8007:3C         INC A
8008:05         DEC B
8009:C20580     JP NZ, 8005
800C:C9         RET
800D:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 800C

A jak je to s urychlením celého programu? Porovnejme si oba přístupy:

Instrukce Cyklů Instrukce Cyklů
ld a, 0 7 xor a 4
ld b, 0 7 ld b, a 4
Celkem: 14 Celkem: 8
Poznámka: jedná se o inicializaci smyčky, která se neopakuje, takže reálná úspora nebude velká, pokud se ovšem progrm nevolá například při každém snímku atd.

13. Optimalizace skoku na konci smyčky (instrukce DJNZ)

Další optimalizace, kterou se můžeme pokusit provést, je náhrada dvojice instrukcí:

        dec b                 ; snížení hodnoty počitadla
        jp NZ, loop           ; skok pokud se ještě nedosáhlo nuly

Za jedinou instrukci:

        djnz loop             ; kombinace dec b + jp NZ, loop
                              ; snížení hodnoty počitadla
                              ; skok pokud se ještě nedosáhlo nuly

Instrukce DJNZ implicitně pracuje s registrem B, takže další změny v programu již nejsou nutné (mimochodem, na 8086 se v podobné instrukci LOOP pracuje s registrem CX, což se lépe pamatuje protože C=counter).

Upravený program založený na instrukci DJNZ pro řízení průchodu smyčkou nyní bude vypadat takto:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld hl, ATTRIBUTE_ADR  ; adresa pro zápis
        xor a                 ; zapisovaná hodnota
        ld b, a               ; počitadlo smyčky
 
loop:
        ld (hl),a             ; zápis hodnoty na adresu (HL)
        inc hl                ; zvýšení adresy
        inc a                 ; zvýšení zapisované hodnoty
        djnz loop             ; kombinace dec b + jp NZ, loop
                              ; snížení hodnoty počitadla
                              ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

Po překladu zjistíme, že opět došlo ke zkrácení celkové délky strojového kódu, nyní na jedenáct bajtů:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:210058     LD HL, 5800
8003:AF         XOR A
8004:47         LD B, A
8005:           label loop
8005:77         LD (HL), A
8006:23         INC HL
8007:3C         INC A
8008:10FB       DJNZ 8005
800A:C9         RET
800B:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 800A

A jak je to s urychlením celého programu? Porovnejme si oba přístupy:

Instrukce Cyklů Instrukce Cyklů
dec b 4 djnz loop 13 (8)
jp NZ, loop 10    
Celkem: 256×14=3584 Celkem: 255×13+8=3323

V případě instrukce DJNZ se délka provádění odvíjí od toho, zda ke skoku dojde či nikoli. Při opakování smyčky bude tato instrukce trvat 13 taktů, po posledním průchodu jen 8 taktů.

14. Optimalizace využití pracovních registrů

Program je možné modifikovat ještě jiným způsobem. Postačuje si totiž uvědomit, že pokud do registrového páru HL zapisujeme hodnotu 0×5800, znamená to vlastně, že do registru H se zapíše hodnota 0×58 a do registru L hodnota 0. To vlastně znamená, že registr L, jenž je postupně zvyšován o jedničku, může být použit i pro zápis atributu namísto registru A. Program tedy můžeme změnit takto:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld hl, ATTRIBUTE_ADR  ; adresa pro zápis
        ld b, l               ; zapisovaná hodnota + počitadlo smyčky
 
loop:
        ld (hl),l             ; zápis hodnoty na adresu (HL)
        inc hl                ; zvýšení adresy i zapisované hodnoty
        djnz loop             ; kombinace dec b + jp NZ, loop
                              ; snížení hodnoty počitadla
                              ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

Opět pochopitelně dojde ke zkrácení výsledného strojového kódu, a to na pouhých devět bajtů:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:210058     LD HL, 5800
8003:45         LD B, L
8004:           label loop
8004:75         LD (HL), L
8005:23         INC HL
8006:10FC       DJNZ 8004
8008:C9         RET
8009:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8008

Na tomto místě si navíc můžeme uvědomit, že nemá smysl zvyšovat hodnotu registrového páru HL, ale pouze registru L, protože blok má délku jen 256 bajtů. Výsledek nebude kratší z hlediska obsazeného místa paměti, ale z hlediska rychlosti výpočtu ano (ušetříme 6–4=2 cykly v každé iteraci, tedy 512 cyklů):

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld hl, ATTRIBUTE_ADR  ; adresa pro zápis
        ld b, l               ; zapisovaná hodnota + počitadlo smyčky
 
loop:
        ld (hl),l             ; zápis hodnoty na adresu (HL)
        inc l                 ; zvýšení adresy i zapisované hodnoty
        djnz loop             ; kombinace dec b + jp NZ, loop
                              ; snížení hodnoty počitadla
                              ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

15. Náhrada registrového páru HL za index registry IX nebo IY?

V páté kapitole jsme si mj. řekli, že mikroprocesor Zilog Z80 obsahuje i dvojici index registrů nazvaných IX a IY. Jedná se o šestnáctibitové registry, které mohou kromě indexů obsahovat i bázové adresy, protože u některých instrukcí lze použít adresování typu bázová adrese+offset. Můžeme tedy tyto registry použít v naší smyčce namísto registrového páru HL? Samozřejmě to možné je, ovšem ztratíme možnost triku – přímého přístupu ke spodnímu bajtu registrového páru HL:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld ix, ATTRIBUTE_ADR  ; adresa pro zápis
        xor a                 ; zapisovaná hodnota
        ld b, a               ; počitadlo smyčky
 
loop:
        ld (ix),a             ; zápis hodnoty na adresu (IX)
        inc ix                ; zvýšení adresy
        inc a                 ; zvýšení zapisované hodnoty
        djnz loop             ; kombinace dec b + jp NZ, loop
                              ; snížení hodnoty počitadla
                              ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

Překlad do strojového kódu dopadne následovně:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:DD210058   LD IX, 5800
8004:AF         XOR A
8005:47         LD B, A
8006:           label loop
8006:DD7700     LD (IX+00), A
8009:3C         INC A
800A:DD23       INC IX
800C:10F8       DJNZ 8006
800E:C9         RET
800F:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 800E

Příklad bude plně funkční, ovšem povšimněte si, že instrukce pracující s registrem IX jsou delší o prefix 0×DD (na předchozím výpisu zvýrazněno).

Podobně si můžeme vyzkoušet náhradu IX za IY:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld iy, ATTRIBUTE_ADR  ; adresa pro zápis
        xor a                 ; zapisovaná hodnota
        ld b, a               ; počitadlo smyčky
 
loop:
        ld (iy),a             ; zápis hodnoty na adresu (IY)
        inc iy                ; zvýšení adresy
        inc a                 ; zvýšení zapisované hodnoty
        djnz loop             ; kombinace dec b + jp NZ, loop
                              ; snížení hodnoty počitadla
                              ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

S jiným výsledkem, resp. přesněji řečeno s odlišným prefixem 0×FD namísto 0×DD:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:FD210058   LD IY, 5800
8004:AF         XOR A
8005:47         LD B, A
8006:           label loop
8006:FD7700     LD (IY+00), A
8009:FD23       INC IY
800B:3C         INC A
800C:10F8       DJNZ 8006
800E:C9         RET
800F:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 800E

16. Porovnání kódů založených na registrech HL, IX a IY

Porovnání kódů založených na registrovém páru HL, popř. na index registrech IX a IY nám do určité míry prozradí, jak jsou instrukce zakódovány. Tento rozdílný způsob zakódování je do značné míry způsobený snahou o dosažení zpětné binární kompatibility s Intelem 8080. Porovnáme si ty programy, které provádí naprosto stejné operace, pouze s odlišnými registry:

ATTRIBUTE_ADR   EQU 5800           ATTRIBUTE_ADR   EQU 5800          ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000           ENTRY_POINT     EQU 8000          ENTRY_POINT     EQU 8000
                ORG 8000                           ORG 8000                          ORG 8000
8000:           label start        8000:           label start       8000:           label start
8000:210058     LD HL, 5800        8000:DD210058   LD IX, 5800       8000:FD210058   LD IY, 5800
8003:AF         XOR A              8004:AF         XOR A             8004:AF         XOR A
8004:47         LD B, A            8005:47         LD B, A           8005:47         LD B, A
8005:           label loop         8006:           label loop        8006:           label loop
8005:77         LD (HL), A         8006:DD7700     LD (IX+00), A     8006:FD7700     LD (IY+00), A
8006:23         INC HL             8009:DD23       INC IX            8009:FD23       INC IY
8007:3C         INC A              800B:3C         INC A             800B:3C         INC A
8008:10FB       DJNZ 8005          800C:10F8       DJNZ 8006         800C:10F8       DJNZ 8006
800A:C9         RET                800E:C9         RET               800E:C9         RET
800B:           END 8000           800F:           END 8000          800F:           END 8000
Emiting TAP basic loader           Emiting TAP basic loader          Emiting TAP basic loader
Emiting TAP from 8000 to 800A      Emiting TAP from 8000 to 800E     Emiting TAP from 8000 to 800E

Rozdíly tedy spočívají v použití prefixů 0×DD resp. 0×FD. Navíc se u instrukcí LD (IX), A přidává nulový offset, což tyto instrukce o jeden bajt zvětší (a zpomalí).

17. Programová smyčka se šestnáctibitovým počitadlem: naivní varianta

Na závěr dnešního článku si ukažme, jak by mohla vypadat (poněkud naivní) programová smyčka používající šestnáctibitové počitadlo. Předchozí demonstrační příklady upravíme takovým způsobem, aby se nevyplnilo pouze prvních 256 barvových atributů, ale 512 atributů (nebo klidně i celý blok o délce 768 bajtů). Jako počitadlo bude použit registrový pár BC (nikoli jen B) a pro kontrolu, zda šestnáctibitové počitadlo dosáhlo nuly jednoduše oba osmibitové části počitadla zkombinujeme operací logického součtu bit po bitu. Výsledek bude nulový pouze tehdy, pokud budou nulové i oba operandy logického součtu:

        ld a,b
        or c
        ; následuje test na nulovost výsledku

První varianta smyčky se šestnáctibitovým počitadlem může vypadat následovně:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld hl, ATTRIBUTE_ADR  ; adresa pro zápis
        ld bc, 512            ; počitadlo smyčky
 
loop:
        ld (hl),l             ; zápis hodnoty na adresu (HL)
        inc hl                ; zvýšení adresy i zapisované hodnoty
        dec bc                ; snížení hodnoty počitadla
        ld a,b
        or c
        jp NZ, loop           ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

Způsob překladu do strojového kódu:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:210058     LD HL, 5800
8003:010002     LD BC, 0200
8006:           label loop
8006:75         LD (HL), L
8007:23         INC HL
8008:0B         DEC BC
8009:78         LD A, B
800A:B1         OR C
800B:C20680     JP NZ, 8006
800E:C9         RET
800F:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 800E

A takto bude vypadat obrazovka po spuštění tohoto demonstračního příkladu:

Obrázek 13: Obrazovka po nastavení 512 atributových bajtů (2/3 atributové paměti).

Obrázek 14: Obrazovka po nastavení 512 atributových bajtů (2/3 atributové paměti).

18. Dosažení prakticky stejné rychlosti, jako v případě smyčky s osmibitovým počitadlem

Předchozí řešení nebylo příliš rychlé, neboť vyžadovalo použití relativně velkého množství operací pro zjištění nulovosti počitadla. Ovšem smyčku můžeme přepsat do podoby, která je prakticky stejně rychlá, jako smyčka s osmibitovým počitadlem. Trik spočívá v tom, že se smyčka rozdělí na vnitřní (ta se v našem případě vždy provede 256×) a na vnější (ta se provede dvakrát). Vnitřní smyčka je naprosto stejně rychlá jako čistě osmibitová varianta a vnější smyčka (resp. její testy) se zopakuje pouze 2× a tudíž nemá na celkovou délku provedení žádný zásadní vliv. Mimochodem – vhodnou volbou konstant předávných do registrů A a B můžeme docílit prakticky libovolného množství celkového počtu opakování; nemusí se tudíž jednat o násobky 256:

ict ve školství 24

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld hl, ATTRIBUTE_ADR  ; adresa pro zápis
        ld a, 2               ; počet opakování bloku s 256 zápisy
        ld b, 0               ; počitadlo vnitřní smyčky
 
loop:
        ld (hl),l             ; zápis hodnoty na adresu (HL)
        inc hl                ; zvýšení adresy i zapisované hodnoty
        djnz loop             ; vnitřní smyčka: blok s 256 zápisy
        dec a                 ; počitadlo vnější smyčky
        jp NZ, loop           ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

A takto bude vypadat překlad do strojového kódu:

ATTRIBUTE_ADR   EQU 5800
ENTRY_POINT     EQU 8000
                ORG 8000
8000:           label start
8000:210058     LD HL, 5800
8003:3E02       LD A, 02
8005:0600       LD B, 00
8007:           label loop
8007:75         LD (HL), L
8008:23         INC HL
8009:10FC       DJNZ 8007
800B:3D         DEC A
800C:C20780     JP NZ, 8007
800F:C9         RET
8010:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 800F

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 Makefile Makefile pro překlad a slinkování všech příkladů https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/Makefile

20. Odkazy na Internetu

  1. z80 standalone assembler
    https://www.asm80.com/one­page/asmz80.html
  2. The ZX BASIC Compiler
    https://www.boriel.com/pages/the-zx-basic-compiler.html
  3. Z80 Assembly programming for the ZX Spectrum
    https://www.chibiakumas.com/z80/ZXSpec­trum.php
  4. 8-BIT SMACKDOWN! 65C02 vs. Z80: slithy VLOGS #6
    https://www.youtube.com/wat­ch?v=P1paVoFEvyc
  5. Instrukce mikroprocesoru Z80
    https://clrhome.org/table/
  6. Z80 instructions: adresní režimy atd.
    https://jnz.dk/z80/instructions.html
  7. Z80 Instruction Groups
    https://jnz.dk/z80/instgroups.html
  8. Elena, New programming language for the ZX Spectrum Next
    https://vintageisthenewold.com/elena-new-programming-language-for-the-zx-spectrum-next/
  9. Sinclair BASIC
    https://worldofspectrum.net/legacy-info/sinclair-basic/
  10. Grafika na osmibitových počítačích firmy Sinclair
    https://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair/
  11. Grafika na osmibitových počítačích firmy Sinclair II
    https://www.root.cz/clanky/grafika-na-osmibitovych-pocitacich-firmy-sinclair-ii/
  12. HiSoft BASIC
    https://worldofspectrum.net/in­foseekid.cgi?id=0008249
  13. YS MegaBasic
    https://worldofspectrum.net/in­foseekid.cgi?id=0008997
  14. Beta Basic
    https://worldofspectrum.net/in­foseekid.cgi?id=0007956
  15. BASIC+
    https://worldofspectrum.net/in­foseekid.php?id=0014277
  16. Spectrum ROM Memory Map
    https://skoolkit.ca/disas­semblies/rom/maps/all.html
  17. Goto subroutine
    https://skoolkit.ca/disas­semblies/rom/asm/7783.html
  18. Spectrum Next: The Evolution of the Speccy
    https://www.specnext.com/about/
  19. Sedmdesátiny assemblerů: lidsky čitelný strojový kód
    https://www.root.cz/clanky/sed­mdesatiny-assembleru-lidsky-citelny-strojovy-kod/
  20. Programovací jazyk BASIC na osmibitových mikropočítačích
    https://www.root.cz/clanky/pro­gramovaci-jazyk-basic-na-osmibitovych-mikropocitacich/
  21. Programovací jazyk BASIC na osmibitových mikropočítačích (2)
    https://www.root.cz/clanky/pro­gramovaci-jazyk-basic-na-osmibitovych-mikropocitacich-2/#k06
  22. Programovací jazyk BASIC na osmibitových mikropočítačích (3)
    https://www.root.cz/clanky/pro­gramovaci-jazyk-basic-na-osmibitovych-mikropocitacich-3/
  23. Sinclair BASIC (Wikipedia CZ)
    http://cs.wikipedia.org/wi­ki/Sinclair_BASIC
  24. Assembly Language: Still Relevant Today
    http://wilsonminesco.com/AssyDefense/
  25. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/gryga­rek/asm/asmlinux.html
  26. Why Assembly Language Programming? (Why Learning Assembly Language Is Still a Good Idea)
    https://wdc65×x.com/market­s/education/why-assembly-language-programming/
  27. Low Fat Computing
    http://www.ultratechnology­.com/lowfat.htm
  28. Assembly Language
    https://www.cleverism.com/skills-and-tools/assembly-language/
  29. Why do we need assembly language?
    https://cs.stackexchange.com/qu­estions/13287/why-do-we-need-assembly-language
  30. Assembly language (Wikipedia)
    https://en.wikipedia.org/wi­ki/Assembly_language#Histo­rical_perspective
  31. Assembly languages
    https://curlie.org/Computer­s/Programming/Languages/As­sembly/
  32. vasm
    http://sun.hasenbraten.de/vasm/
  33. B-ELITE
    https://jsj.itch.io/b-elite
  34. ZX-Spectrum Child
    http://www.dotkam.com/2008/11/19/zx-spectrum-child/
  35. Speccy.cz
    http://www.speccy.cz/
  36. Planet Sinclair
    http://www.nvg.ntnu.no/sinclair/
  37. World of Spectrum
    http://www.worldofspectrum.org/
  38. Z80 Assembly Language for the ZX Spectrum Tutorial, Episode 1: The Basics
    https://www.youtube.com/wat­ch?v=_J4ahkWtNYw
  39. Z80 assembly resources when starting programming in assembler
    https://www.youtube.com/wat­ch?v=mjLHSnQmHV4
  40. Setting up Visual Studio Code with Pasmo, Sprite Example ZX Spectrum Next
    https://www.youtube.com/wat­ch?v=lKDaFWPObLY
  41. RetroCoder ZX Spectrum development (Z80 Assembly)- Day 1 – Hello World.asm
    https://www.youtube.com/watch?v=Xv6NAC–x24
  42. Rozšíření paměti
    https://wiki.ilnx.cz/doku­.php/lnxspectrum:memorymap
  43. ZX-Spectrum 48K video memory map
    https://www.reddit.com/r/zxspec­trum/comments/phi7lt/zxspec­trum_48k_video_memory_map/
  44. Memory Map: 48K Spectrum
    http://www.breakintoprogram­.co.uk/hardware/computers/zx-spectrum/memory-map
  45. ZX Basic: Git repository
    https://github.com/boriel/zxbasic
  46. ZX Basic Wiki
    https://zxbasic.readthedoc­s.io/en/docs/
  47. ZX Spectrum Games: svět osmibitové herní legendy
    https://www.zx-spectrum.cz/
  48. TAP format
    https://sinclair.wiki.zxnet­.co.uk/wiki/TAP_format
  49. Contended memory
    https://worldofspectrum.or­g/faq/reference/48kreferen­ce.htm#Contention
  50. Screen Memory Layout
    http://www.breakintoprogram­.co.uk/hardware/computers/zx-spectrum/screen-memory-layout
  51. OpenSE BASIC
    https://zxdesign.itch.io/opense
  52. Domácí a školní mikropočítače řady Didaktik
    https://www.root.cz/clanky/domaci-a-skolni-mikropocitace-rady-didaktik/
  53. Z80 Assembler for Dummies
    https://www.msx.org/wiki/Z80_As­sembler_for_Dummies
  54. Z80 Resources
    https://www.assemblytutorial.com/z80/
  55. How do Z80 Block Transfer instructions work?
    https://retrocomputing.stac­kexchange.com/questions/5416/how-do-z80-block-transfer-instructions-work
  56. How fast is memcpy on the Z80?
    https://retrocomputing.stac­kexchange.com/questions/4744/how-fast-is-memcpy-on-the-z80
  57. Comparing Datapoint 2200, 8008, 8080 and Z80 Instruction Sets
    https://bread80.com/comparing-datapoint-2200–8008–8080-and-z80-instruction-sets/
  58. 8080/Z80 Instruction Set
    https://retroprogramming.it/2021/02/8080-z80-instruction-set/
  59. Zilog Z80A Technical Information
    https://worldofspectrum.or­g/faq/reference/z80referen­ce.htm
  60. Z80 programming techniques – Loops
    http://map.grauw.nl/articles/fas­t_loops.php

Autor článku

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