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
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
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.
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 |
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í |
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
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).
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 |
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:
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
- 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/ - Z80 Assembly Language for the ZX Spectrum Tutorial, Episode 1: The Basics
https://www.youtube.com/watch?v=_J4ahkWtNYw - Z80 assembly resources when starting programming in assembler
https://www.youtube.com/watch?v=mjLHSnQmHV4 - Setting up Visual Studio Code with Pasmo, Sprite Example ZX Spectrum Next
https://www.youtube.com/watch?v=lKDaFWPObLY - RetroCoder ZX Spectrum development (Z80 Assembly)- Day 1 – Hello World.asm
https://www.youtube.com/watch?v=Xv6NAC–x24 - Rozšíření paměti
https://wiki.ilnx.cz/doku.php/lnxspectrum:memorymap - ZX-Spectrum 48K video memory map
https://www.reddit.com/r/zxspectrum/comments/phi7lt/zxspectrum_48k_video_memory_map/ - Memory Map: 48K Spectrum
http://www.breakintoprogram.co.uk/hardware/computers/zx-spectrum/memory-map - ZX Basic: Git repository
https://github.com/boriel/zxbasic - ZX Basic Wiki
https://zxbasic.readthedocs.io/en/docs/ - ZX Spectrum Games: svět osmibitové herní legendy
https://www.zx-spectrum.cz/ - TAP format
https://sinclair.wiki.zxnet.co.uk/wiki/TAP_format - Contended memory
https://worldofspectrum.org/faq/reference/48kreference.htm#Contention - Screen Memory Layout
http://www.breakintoprogram.co.uk/hardware/computers/zx-spectrum/screen-memory-layout - OpenSE BASIC
https://zxdesign.itch.io/opense - Domácí a školní mikropočítače řady Didaktik
https://www.root.cz/clanky/domaci-a-skolni-mikropocitace-rady-didaktik/ - Z80 Assembler for Dummies
https://www.msx.org/wiki/Z80_Assembler_for_Dummies - Z80 Resources
https://www.assemblytutorial.com/z80/ - How do Z80 Block Transfer instructions work?
https://retrocomputing.stackexchange.com/questions/5416/how-do-z80-block-transfer-instructions-work - How fast is memcpy on the Z80?
https://retrocomputing.stackexchange.com/questions/4744/how-fast-is-memcpy-on-the-z80 - Comparing Datapoint 2200, 8008, 8080 and Z80 Instruction Sets
https://bread80.com/comparing-datapoint-2200–8008–8080-and-z80-instruction-sets/ - 8080/Z80 Instruction Set
https://retroprogramming.it/2021/02/8080-z80-instruction-set/ - Zilog Z80A Technical Information
https://worldofspectrum.org/faq/reference/z80reference.htm - Z80 programming techniques – Loops
http://map.grauw.nl/articles/fast_loops.php