Hlavní navigace

Tisk hexadecimálních hodnot s využitím instrukce DAA na ZX Spectru

20. 4. 2023
Doba čtení: 47 minut

Sdílet

 Autor: Depositphotos
Dnešní článek o vývoji programů pro mikropočítač ZX Spectrum je rozdělen na dvě části. V úvodní části si ukážeme využití instrukce DAA při tisku hexadecimálních hodnot a v části druhé se seznámíme se základy zpracování hodnot s plovoucí řádovou čárkou.

Obsah

1. Tisk hexadecimálních hodnot podruhé

2. Úplný zdrojový kód dnešního prvního demonstračního příkladu

3. Malá odbočka: přetečení hodnoty cifry

4. Úplný zdrojový kód dnešního druhého demonstračního příkladu

5. Využití instrukce DAA namísto podmíněného skoku

6. Podrobný popis operací prováděných při převodu hexadecimální cifry na ASCII znak

7. Úplný zdrojový kód dnešního třetího demonstračního příkladu

8. Vylepšené vytištění osmibitové hexadecimální hodnoty

9. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu

10. Reprezentace numerických hodnot ve formátu pevné a plovoucí řádové (binární) tečky

11. Vybrané způsoby reprezentace numerických hodnot v operační paměti počítače

12. Uložení numerických hodnot ve formátu pevné řádové binární tečky

13. Přednosti a zápory formátu pevné řádové tečky

14. Uložení čísel ve formátu plovoucí řádové (binární) tečky

15. Formát uložení FP hodnot na ZX Spectru

16. Zásobník FP hodnot na ZX Spectru, subrutina pro tisk FP hodnot

17. Úplný zdrojový kód dnešního pátého demonstračního příkladu

18. Příloha: upravený soubor Makefile pro překlad demonstračních příkladů

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

20. Odkazy na Internetu

1. Tisk hexadecimálních hodnot podruhé

V dnešním článku si nejprve ukážeme trik pro tisk hexadecimálních hodnot. Tento trik je založen na instrukci DAA a osmibitové aritmetice. A posléze se zaměříme na zcela odlišnou oblast – jak se dá pracovat s numerickými hodnotami reprezentovanými v systému pevné i plovoucí řádové tečky (fixed point, floating point).

Zaměřme se nyní na způsob zobrazení jediné hexadecimální cifry, tj. znaku „0“, „1“,… „9“, „A“ … „F“. Úloha je to vlastně velmi snadná: na vstupu budeme mít hodnotu 0 až 15 uloženou v akumulátoru A a výsledkem má být jeden z výše zmíněných znaků vytisknutých na obrazovku ZX Spectra. Znaky pro tisk se přitom pochopitelně vybírají z ASCII tabulky resp. přesněji řečeno z její „spektrácké“ verze, která vypadá následovně:

Obrázek 1: „Spektrácká“ verze ASCII tabulky (zobrazeny jsou jen tisknutelné znaky).

Jediný problém spočívá v tom, že převod hodnoty 0 až 15 na kód znaku není zcela přímočarý, protože znaky jsou v ASCII kódu uspořádány takovým způsobem, že se mezi znakem „9“ a znakem „A“ nachází sedm jiných znaků, konkrétně znaky „:“, „;“, „<“, „=“, „>“, „?“ a „@“:

Obrázek 2: Hexadecimální cifry nejsou v ASCII umístěny za sebou.

K dispozici jsou tři rozumná řešení:

  1. Použití převodní tabulky (ovšem ztratíme šestnáct bajtů RAM + další bajty pro kód)
  2. Použití podmínky + skoku, který řeší „mezeru“
  3. Aritmetické triky zajišťující, že se pro hodnoty vyšší než 9 provede operace +7

Druhý způsob už jsme si ukázali minule, ale pro jistotu si ho ještě zopakujme. Princip je jednoduchý – vypočteme ASCII hodnotu znaku tak, jakoby byly všechny cifry umístěny za sebou a posléze provedeme úpravu +7 pro cifry s hodnotou vyšší než 9:

print_hex_digit_nl:
        push AF             ; uschovat A pro pozdější využití
 
        cp   0x0a           ; test, jestli je číslice menší než 10
        jr c, print_0_to_9  ; ok, hodnota je menší než 10, budeme tedy tisknout desítkovou číslici
        add  A, 65-10-48    ; ASCII kód znaku 'A', ovšem začínáme od desítky, ne od nuly (+ update pro další ADD)
print_0_to_9:
        add  A, 48          ; ASCII kód znaku '0'
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
 
new_line:
        ld   A, 0x0d        ; kód znaku pro odřádkování
        rst  0x10           ; zavolání rutiny v ROM
        pop  AF             ; obnovit A
        ret                 ; návrat ze subrutiny

2. Úplný zdrojový kód dnešního prvního demonstračního příkladu

Výše uvedený kód je použit pro tisk těchto hodnot:

Obrázek 3: Hexadecimální cifry vypočtené a zobrazené dnešním prvním demonstračním příkladem.

Úplný zdrojový kód demonstračního příkladu popsaného v úvodní kapitole je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/79-print-hex-digit-jmp.asm:

ENTRY_POINT   equ $8000
ROM_CLS       equ $0DAF
OUT_NUM_1     equ $1A1B
 
 
        org ENTRY_POINT
 
start:
        call ROM_CLS        ; smazání obrazovky a otevření kanálu číslo 2 (screen)
 
        xor  A              ; hodnota cifry, která se má vytisknout
        ld   B, 16          ; počitadlo smyčky
loop:                       ; vytisknout hexa cifru s přechodem na nový řádek
        call print_hex_digit_nl
        inc  A              ; zvýšit hodnotu tištěné cifry
        djnz loop           ; opakování smyčky se snížením hodnoty počitadla
 
        ret                 ; návrat z programu do BASICu
 
print_hex_digit_nl:
        push AF             ; uschovat A pro pozdější využití
 
        cp   0x0a           ; test, jestli je číslice menší než 10
        jr c, print_0_to_9  ; ok, hodnota je menší než 10, budeme tedy tisknout desítkovou číslici
        add  A, 65-10-48    ; ASCII kód znaku 'A', ovšem začínáme od desítky, ne od nuly (+ update pro další ADD)
print_0_to_9:
        add  A, 48          ; ASCII kód znaku '0'
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
 
new_line:
        ld   A, 0x0d        ; kód znaku pro odřádkování
        rst  0x10           ; zavolání rutiny v ROM
        pop  AF             ; obnovit A
        ret                 ; návrat ze subrutiny
 
 
end ENTRY_POINT

Samozřejmě si opět pro úplnost ukážeme, jak byl tento zdrojový text přeložen do strojového kódu:

ENTRY_POINT     EQU 8000
ROM_CLS         EQU 0DAF
OUT_NUM_1       EQU 1A1B
                ORG 8000
8000:           label start
8000:CDAF0D     CALL 0DAF
8003:AF         XOR A
8004:0610       LD B, 10
8006:           label loop
8006:CD0D80     CALL 800D
8009:3C         INC A
800A:10FA       DJNZ 8006
800C:C9         RET
800D:           label print_hex_digit_nl
800D:F5         PUSH AF
800E:FE0A       CP 0A
8010:3802       JR C, 8014
8012:C607       ADD A, 07
8014:           label print_0_to_9
8014:C630       ADD A, 30
8016:D7         RST 10
8017:           label new_line
8017:3E0D       LD A, 0D
8019:D7         RST 10
801A:F1         POP AF
801B:C9         RET
801C:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 801B

3. Malá odbočka: přetečení hodnoty cifry

Zajímavé bude si vyzkoušet, co se stane ve chvíli, kdy se pokusíme o převod a vytištění dekadické hodnoty větší než 15. Velmi snadno si můžeme upravit počítanou programovou smyčku v předchozím příkladu takovým způsobem, aby se smyčka nezastavila na hodnotě 15, ale až na hodnotě 19 (více řádků se již nevytiskne, protože se ROM rutina zastaví a čeká na vstup od uživatele):

        xor  A              ; hodnota cifry, která se má vytisknout
        ld   B, 20          ; počitadlo smyčky
loop:                       ; vytisknout hexa cifru s přechodem na nový řádek
        call print_hex_digit_nl
        inc  A              ; zvýšit hodnotu tištěné cifry
        djnz loop           ; opakování smyčky se snížením hodnoty počitadla

Z výsledků je patrné, že se vypisují další cifry „G“ atd. až do „Z“, což vlastně znamená, že máme k dispozici rutinu platnou pro třiceti šestkovou číselnou soustavu (10 numerických znaků + 26 znaků anglické abecedy):

Obrázek 4: Tisk „hexadecimálních“ hodnot odpovídajících dekadickým hodnotám od 0 do 19. Povšimněte si, že ve skutečnosti máme k dispozici celou 36 číselnou soustavu.

Poznámka: řešení představené v dalším textu již bude jiné a bude skutečně platné pouze pro hexadecimální soustavu.

4. Úplný zdrojový kód dnešního druhého demonstračního příkladu

Úplný zdrojový kód dnešního druhého demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/80-print-hex-digit-overflow.asm:

ENTRY_POINT   equ $8000
ROM_CLS       equ $0DAF
OUT_NUM_1     equ $1A1B
 
 
        org ENTRY_POINT
 
start:
        call ROM_CLS        ; smazání obrazovky a otevření kanálu číslo 2 (screen)
 
        xor  A              ; hodnota cifry, která se má vytisknout
        ld   B, 20          ; počitadlo smyčky
loop:                       ; vytisknout hexa cifru s přechodem na nový řádek
        call print_hex_digit_nl
        inc  A              ; zvýšit hodnotu tištěné cifry
        djnz loop           ; opakování smyčky se snížením hodnoty počitadla
 
        ret                 ; návrat z programu do BASICu
 
print_hex_digit_nl:
        push AF             ; uschovat A pro pozdější využití
 
        cp   0x0a           ; test, jestli je číslice menší než 10
        jr c, print_0_to_9  ; ok, hodnota je menší než 10, budeme tedy tisknout desítkovou číslici
        add  A, 65-10-48    ; ASCII kód znaku 'A', ovšem začínáme od desítky, ne od nuly (+ update pro další ADD)
print_0_to_9:
        add  A, 48          ; ASCII kód znaku '0'
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
 
new_line:
        ld   A, 0x0d        ; kód znaku pro odřádkování
        rst  0x10           ; zavolání rutiny v ROM
        pop  AF             ; obnovit A
        ret                 ; návrat ze subrutiny
 
 
end ENTRY_POINT

Způsob překladu do strojového kódu je prakticky totožný s příkladem prvním (a to zcela podle očekávání):

ENTRY_POINT     EQU 8000
ROM_CLS         EQU 0DAF
OUT_NUM_1       EQU 1A1B
                ORG 8000
8000:           label start
8000:CDAF0D     CALL 0DAF
8003:AF         XOR A
8004:0614       LD B, 14
8006:           label loop
8006:CD0D80     CALL 800D
8009:3C         INC A
800A:10FA       DJNZ 8006
800C:C9         RET
800D:           label print_hex_digit_nl
800D:F5         PUSH AF
800E:FE0A       CP 0A
8010:3802       JR C, 8014
8012:C607       ADD A, 07
8014:           label print_0_to_9
8014:C630       ADD A, 30
8016:D7         RST 10
8017:           label new_line
8017:3E0D       LD A, 0D
8019:D7         RST 10
801A:F1         POP AF
801B:C9         RET
801C:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 801B

5. Využití instrukce DAA namísto podmíněného skoku

Víme již, že problém, který řešíme, je následující: pro hodnoty větší než 9 musíme ASCII hodnotu příslušného znaku vypočítat posunutou o sedm pozic, protože takovým způsobem je uspořádána ASCII tabulka. Řešení spočívá v „chytrém“ použití instrukce DAA, která nám umožní automaticky (bez nutnosti použití podmíněného skoku) zvýšit hodnotu o 6. Zbývá nám pouze zajistit přičtení jedničky. K tomu slouží instrukce ADC, pro přičtení konstanty a případného přenosu (carry):

print_hex_digit_nl:
        push AF             ; uschovat A pro pozdější využití
        or   $f0            ; nastavit horní čtyři bity + příznaky
                            ; nyní je v A jedna z hodnot:
                            ;    0xf0 0xf1 0xf2 ... 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff
        daa                 ; desítková korekce pro původní hodnoty A-F
                            ; +  0x60 0x60 0x60 ... 0x60 0x66 0x66 0x66 0x66 0x66 0x66
                            ; nyní je v A jedna z hodnot:
                            ;    0x50 0x51 0x52 ... 0x59 0x60 0x61 0x62 0x63 0x64 0x65
        add A, $a0          ; přičtení konstanty
                            ; nyní je v A jedna z hodnot:
                            ;    0xf0 0xf1 0xf2 ... 0xf9 0x00 0x01 0x02 0x03 0x04 0x05
                            ; C    0    0    0        0    1    1    1    1    1    1
        adc A, $40          ; přičtení konstanty a navíc i příznaku carry
                            ;    0x30 0x31 0x31 ... 0x39 0x41 0x42 0x43 0x44 0x45 0x46
                            ; >   '0'  '1'  '2'      '9'  'A'  'B'  'C'  'D'  'E'  'F'
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku

6. Podrobný popis operací prováděných při převodu hexadecimální cifry na ASCII znak

Výše uvedený kód provádí jak převod cifry na ASCII znak, tak i úpravu +7, tedy přeskočení těchto znaků:

Obrázek 5: Sedmice znaků v ASCII tabulce, které je nutné přeskočit.

Pojďme si nyní podrobněji popsat jednotlivé fáze výpočtu. Nejprve se pokusíme o převod hodnoty (cifry) 0, ovšem naprosto stejný popis bude platný i pro cifry od 1 do 9 (hodnoty jsou vždy uložené v akumulátoru a znak dolaru značí hexadecimální hodnotu):

Hodnota před instrukcí Instrukce Provedená operace Hodnota po instrukci Carry Stručný popis
$00 or $f0 or $f0 $f0 0 nastavit horní čtyři bity
$f0 daa $f0+$60 $50 1 desítková korekce pro horní čtyři bity (dolní jsou stále nulové)
$50 add A, $a0 $50+$a0 $f0 0 přičtení takové hodnoty, aby se nenastavil carry
$f0 adc A, $40 $f0+$40+0 $30 1 hodnota $30 skutečně odpovídá znaku „0“

Naproti tomu výpočet pro cifry A až F bude naprosto odlišný. Ukažme si to na hodnotě 0×0a (resp. kvůli větší konzistenci $0a):

Hodnota před instrukcí Instrukce Provedená operace Hodnota po instrukci Carry Stručný popis
$0a or $f0 or $f0 $fa 0 nastavit horní čtyři bity
$fa daa $fa+$66 $60 1 desítková korekce pro horní i dolní čtyři bity (kýžené přičtení +6)
$60 add A, $a0 $60+$a0 $00 1 přičtení takové hodnoty, aby se nastavil carry (kýžené přičtení +1)
$00 adc A, $40 $00+$40+1 $41 0 hodnota $41 skutečně odpovídá znaku „A“
Poznámka: a pak že programování v assembleru není krásné a elegantní.

7. Úplný zdrojový kód dnešního třetího demonstračního příkladu

Výše uvedený kód můžeme bez dalších úprav použít pro tisk hexadecimálních cifer 0 až F:

Obrázek 6: Cifry 0 až F vytištěné demonstračním příkladem.

Úplný zdrojový kód dnešního třetího demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/81-print-hex-digit-daa.asm:

ENTRY_POINT   equ $8000
ROM_CLS       equ $0DAF
OUT_NUM_1     equ $1A1B
 
 
        org ENTRY_POINT
 
start:
        call ROM_CLS        ; smazání obrazovky a otevření kanálu číslo 2 (screen)
 
        xor  A              ; hodnota cifry, která se má vytisknout
        ld   B, 16          ; počitadlo smyčky
loop:                       ; vytisknout hexa cifru s přechodem na nový řádek
        call print_hex_digit_nl
        inc  A              ; zvýšit hodnotu tištěné cifry
        djnz loop           ; opakování smyčky se snížením hodnoty počitadla
 
        ret                 ; návrat z programu do BASICu
 
print_hex_digit_nl:
        push AF             ; uschovat A pro pozdější využití
        or   $f0            ; nastavit horní čtyři bity + příznaky
                            ; nyní je v A jedna z hodnot:
                            ;    0xf0 0xf1 0xf2 ... 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff
        daa                 ; desítková korekce pro původní hodnoty A-F
                            ; +  0x60 0x60 0x60 ... 0x60 0x66 0x66 0x66 0x66 0x66 0x66
                            ; nyní je v A jedna z hodnot:
                            ;    0x50 0x51 0x52 ... 0x59 0x60 0x61 0x62 0x63 0x64 0x65
        add A, $a0          ; přičtení konstanty
                            ; nyní je v A jedna z hodnot:
                            ;    0xf0 0xf1 0xf2 ... 0xf9 0x00 0x01 0x02 0x03 0x04 0x05
                            ; C    0    0    0        0    1    1    1    1    1    1
        adc A, $40          ; přičtení konstanty a navíc i příznaku carry
                            ;    0x30 0x31 0x31 ... 0x39 0x41 0x42 0x43 0x44 0x45 0x46
                            ; >   '0'  '1'  '2'      '9'  'A'  'B'  'C'  'D'  'E'  'F'
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
 
new_line:
        ld   A, 0x0d        ; kód znaku pro odřádkování
        rst  0x10           ; zavolání rutiny v ROM
        pop  AF             ; obnovit A
        ret                 ; návrat ze subrutiny
 
 
end ENTRY_POINT

A pro úplnost se podívejme na způsob překladu výše uvedeného zdrojového kódu do assembleru:

ENTRY_POINT     EQU 8000
ROM_CLS         EQU 0DAF
OUT_NUM_1       EQU 1A1B
                ORG 8000
8000:           label start
8000:CDAF0D     CALL 0DAF
8003:AF         XOR A
8004:0610       LD B, 10
8006:           label loop
8006:CD0D80     CALL 800D
8009:3C         INC A
800A:10FA       DJNZ 8006
800C:C9         RET
800D:           label print_hex_digit_nl
800D:F5         PUSH AF
800E:F6F0       OR F0
8010:27         DAA
8011:C6A0       ADD A, A0
8013:CE40       ADC A, 40
8015:D7         RST 10
8016:           label new_line
8016:3E0D       LD A, 0D
8018:D7         RST 10
8019:F1         POP AF
801A:C9         RET
801B:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 801A

8. Vylepšené vytištění osmibitové hexadecimální hodnoty

Nyní, když již známe způsob efektivního převodu jedné hexadecimální cifry na ASCII znak, můžeme upravit původní demonstrační příklad, který tiskne osmibitovou hexadecimální hodnotu na obrazovku (a tedy dvě cifry). Nejprve se vytiskne vyšší cifra, poté cifra nižší. Povšimněte si, že podprogram print_hex_digit se volá jen jedenkrát – a to pro vyšší cifru. Pro nižší cifru není volání implementováno, protože podprogram print_hex_digit je umístěn přímo za print_hex_number, jak správně upozornil kolega _dw:

print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        call print_hex_digit; vytisknout první hexa cifru
 
        pop  AF             ; obnovit A
                            ; vytisknout druhou hexa cifru
 
print_hex_digit:
        or   $f0            ; nastavit horní čtyři bity + příznaky
        daa                 ; desítková korekce pro původní hodnoty A-F
        add A, $a0          ; přičtení konstanty
        adc A, $40          ; přičtení konstanty a navíc i příznaku carry
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
        ret                 ; návrat ze subrutiny

Výsledek bude vypadat následovně:

Obrázek 7: Několik osmibitových hexadecimálních hodnot vytištěných touto subrutinou.

9. Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu

Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/82-print-hex-numbers-daa.asm:

ENTRY_POINT   equ $8000
ROM_CLS       equ $0DAF
OUT_NUM_1     equ $1A1B
 
 
        org ENTRY_POINT
 
start:
        call ROM_CLS           ; smazání obrazovky a otevření kanálu číslo 2 (screen)
 
        ld   HL, numbers       ; statické pole s hodnotami, které se mají vytisknout + zarážkou
loop:
        ld   A, (HL)           ; načíst další hodnotu ze statického pole
        or   A                 ; test na nulu
        ret  z                 ; návrat z programu do BASICU
        inc  HL                ; adresa dalšího prvku v poli
        call print_hex_number  ; tisk hexadecimální hodnoty
        call new_line          ; s přechodem na nový řádek
        jp   loop              ; zpracování další hodnoty
 
numbers:
        db 0x01, 0x02, 0x09, 0x0a, 0x10, 0x99, 0xa0, 0xaa, 0xaf, 0xf0, 0xff, 0x00
 
print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        call print_hex_digit; vytisknout první hexa cifru
 
        pop  AF             ; obnovit A
                            ; vytisknout druhou hexa cifru
 
print_hex_digit:
        or   $f0            ; nastavit horní čtyři bity + příznaky
        daa                 ; desítková korekce pro původní hodnoty A-F
        add A, $a0          ; přičtení konstanty
        adc A, $40          ; přičtení konstanty a navíc i příznaku carry
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
        ret                 ; návrat ze subrutiny
 
new_line:
        ld   A, 0x0d        ; kód znaku pro odřádkování
        rst  0x10           ; zavolání rutiny v ROM
        ret                 ; návrat ze subrutiny
 
 
end ENTRY_POINT

A pro úplnost se opět podívejme na způsob překladu výše uvedeného zdrojového kódu do assembleru:

ENTRY_POINT     EQU 8000
ROM_CLS         EQU 0DAF
OUT_NUM_1       EQU 1A1B
                ORG 8000
8000:           label start
8000:CDAF0D     CALL 0DAF
8003:211380     LD HL, 8013
8006:           label loop
8006:7E         LD A, (HL)
8007:B7         OR A
8008:C8         RET Z
8009:23         INC HL
800A:CD1F80     CALL 801F
800D:CD3180     CALL 8031
8010:C30680     JP 8006
8013:           label numbers
8013:0102090A   DEFB of 12 bytes
8017:1099A0AA
801B:AFF0FF00
801F:           label print_hex_number
801F:F5         PUSH AF
8020:0F         RRCA
8021:0F         RRCA
8022:0F         RRCA
8023:0F         RRCA
8024:CD2880     CALL 8028
8027:F1         POP AF
8028:           label print_hex_digit
8028:F6F0       OR F0
802A:27         DAA
802B:C6A0       ADD A, A0
802D:CE40       ADC A, 40
802F:D7         RST 10
8030:C9         RET
8031:           label new_line
8031:3E0D       LD A, 0D
8033:D7         RST 10
8034:C9         RET
8035:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8034

10. Reprezentace numerických hodnot ve formátu pevné a plovoucí řádové (binární) tečky

Ve druhé části dnešního článku si ve stručnosti popíšeme některé ze způsobů reprezentace (resp. přesněji řečeno způsobu uložení) podmnožiny racionálních numerických hodnot (zkráceně čísel) v operační paměti počítače a/nebo v registrech jeho mikroprocesoru (CPU) či matematického koprocesoru (FPU). Jedná se o uložení vybrané množiny numerických hodnot v takzvaném systému pevné řádové (typicky binární nebo desetinné) tečky popř. naopak v systému plovoucí řádové tečky.

Poznámka: V tomto textu se ovšem budeme záměrně dopouštět drobného prohřešku oproti stávající normě českého jazyka, protože budeme neustále psát o řádové, desetinné a binární tečce a nikoli o čárce – z hlediska anglické terminologie to bude více konzistentní, i když z češtinářského hlediska by bylo zcela jistě korektnější psát o řádové čárce, protože se v češtině celá část čísla od části desetinné odděluje právě čárkou a nikoli tečkou, jak je tomu zvykem v anglosaských zemích (na druhou stranu programátoři, kterým je tento článek určen především, však tuto skutečnost zcela jistě znají).

V anglické literatuře se první zmíněná forma reprezentace číselných hodnot označuje zkratkou FX nebo FXP (tato zkratka je odvozena od fixed point), zatímco dnes častěji používaná reprezentace v systému plovoucí řádové tečky se všeobecně označuje zkratkou FP (odvozeno od floating point).

Poznámka: V jednom článku jsem mimochodem dokonce místo zkratky FX viděl i zkratku XP, ale to bylo před mnoha lety, ještě v době Windows 95 :-).

Nejprve si vysvětlíme princip obou metod použitých pro ukládání podmnožiny racionálních čísel a posléze si také řekneme, jaké výhody a nevýhody jednotlivé principy přináší v každodenní programátorské praxi a ve kterých situacích je vhodnější použít pevnou řádovou čárku. V dalším textu budeme formát pevné binární řádové tečky zkracovat na FX formát a formát používající plovoucí řádovou tečku budeme zapisovat jako FP formát.

11. Vybrané způsoby reprezentace numerických hodnot v operační paměti počítače

Při ukládání numerických hodnot do operační paměti počítače záhy narazíme na některé problémy, z nichž některé souvisí s konečným počtem bitů, které pro uložení dané hodnoty „obětujeme“ a další vycházejí ze způsobu zpracování hodnot mikroprocesorem či matematickým koprocesorem (což není případ Z80). V konečném počtu bitů je totiž možné uložit pouze konečné množství různých hodnot a je plně v rukou programátora, jak efektivně daný počet bitů využije či naopak promrhá ukládáním nepodstatných informací. Poměrně často se totiž stává, že i program využívající dvojitou či dokonce rozšířenou přesnost čísel při FP operacích (tj. datové typy double a extended/temporary) dává nesprávné výsledky dané nepochopením principu práce FP aritmetiky a přitom je možné se přesnějších výsledků dobrat i při použití pouhých 32 bitů, ale s pečlivě vyváženými aritmetickými a bitovými operacemi.

Na druhou stranu nejsou dnes používané mikroprocesory tak univerzálními zařízeními, jak by se na první pohled mohlo zdát. Mikroprocesory jsou totiž (většinou) navrženy tak, aby účinně, například v rámci jedné operace či instrukce, zpracovávaly pouze konstantní počet bitů. Příkladem mohou být dnes velmi rozšířené procesory řady x86, které jsou velmi dobré při práci s 32 bitovými hodnotami, ale při požadavku na aritmetické výpočty probíhající na (řekněme) 21 bitech se veškerá jejich efektivita ztrácí a procesor se širokými vnitřními sběrnicemi, matematickým koprocesorem atd. se potýká s prohazováním jednotlivých bitů.

Mnohem lepší situace nastane v případě, že se nějaká operace implementuje na programovatelném poli FPGA – zde je možné vytvořit obvody provádějící matematické a logické operace s libovolným počtem bitů, čímž se oproti univerzálním řešením (např. konstantní bitová šířka sběrnice a/nebo registrů) ušetří mnoho plochy těchto velmi zajímavých obvodů (FPGA mohou mimochodem znamenat i velkou šanci pro hnutí open source – pomocí nich by mohlo vznikat, a někde už vzniká open hardware, které by mohlo odstranit závislost na „uzavřených“ síťových a grafických kartách apod.).

Poznámka: mnohé FPGA však již mají své vlastní ALU a dokonce i násobičky, takže se z tohoto pohledu již nejedná o zcela univerzální obvody připravené pro jakékoli reprezentace numerických hodnot.

Vraťme se však ke způsobům reprezentace číselných hodnot v operační paměti. Nejprve předpokládejme, že pro reprezentaci vlastností určitého objektu či stavu z reálného světa použijeme N binárních číslic (bitů), tj. základních jednotek informace, která může nabývat pouze jedné ze dvou povolených hodnot (ty se značí například symboly yes/no nebo true/false, ale my se budeme spíše držet označení 0 a 1). Pomocí této uspořádané N-tice je možné popsat celkem:

20×21×22 … 2N-1=2N

jednoznačných, tj. navzájem odlišných, stavů. Množina těchto stavů může reprezentovat prakticky jakýkoliv abstraktní či reálný objekt. Přitom si musíme uvědomit, že u této množiny není implicitně řečeno ani myšleno, že se jedná například o celá kladná čísla, to je pouze jedna z mnoha možných interpretací zvolené N-tice (my programátoři máme tendenci považovat celá kladná čísla za přirozenou interpretaci bitové N-tice, to však vychází pouze z našeho pohledu na svět a z našich zkušeností). Reprezentaci momentálního stavu abstraktního či reálného objektu si můžeme představit jako zobrazení z množiny binárních stavů na elementy vzorové (a obecně neuspořádané) množiny. Nejčastěji používanými zobrazeními jsou zobrazení množiny binárních stavů na interval celých kladných čísel (Unsigned Integers), popřípadě na interval celých čísel (Signed Integers).

12. Uložení numerických hodnot ve formátu pevné řádové binární tečky

Numerické hodnoty zapsané ve formátu pevné řádové binární tečky se chápou jako podmnožina racionálních čísel, což jsou taková čísla, jejichž hodnoty je možné vyjádřit vztahem:

xFX=a/b    a,b leží v Z, b ≠ 0

Číselné hodnoty z uvažované podmnožiny jsou navíc omezeny podmínkou:

b=2k b leží v Z, k leží v Z+

Protože b je celočíselnou mocninou dvojky (a ne desítky či jiného základu), určuje jeho hodnota n polohu binární tečky v uloženém čísle. Další podmínkou, která má však spíše implementační charakter, je zachování stejného počtu binárních cifer v každém reprezentovaném čísle, což mimo jiné znamená, že všechna čísla mají řádovou binární tečku umístěnou na stejném místě – z této podmínky ostatně plyne i název popisovaného způsobu reprezentace vybrané podmnožiny racionálních čísel. Tak jako i v jiných reprezentacích čísel, jsou nulové číslice před první nenulovou cifrou a za poslední nenulovou cifrou nevýznamné, proto je není zapotřebí uvádět.

Prakticky může být číselná hodnota v systému pevné řádové tečky uložena na osmi bitech například následujícím způsobem (uvažujeme pouze kladné hodnoty):

Pozice bitu 8     7     6     5     4     3     2     1    
Váha bitu 24 23 22 21 20 2-1 2-2 2-3
Desítková váha bitu 16 8 4 2 1 0,5 0,25 0,125
Poznámka: pochopitelně se jedná pouze o jednu konkrétní vybranou reprezentaci, protože je to programátor, který určuje, na kterém místě se řádová tečka nachází. Její pozice totiž není součástí vlastní hodnoty, ale je „zadrátována“ v algoritmech, které numerické hodnoty zpracovávají (součet, součin, CORDIC atd. atd.). Výběr pozice řádové tečky závisí především na tom, které hodnoty je zapotřebí reprezentovat a jaké ztráty přesnosti jsme ochotni akceptovat.

13. Přednosti a zápory formátu pevné řádové tečky

Ve výše uvedeném příkladu je binární řádová tečka umístěna vždy mezi třetím a čtvrtým bitem. Vzhledem k tomu, že je tato skutečnost dopředu známá algoritmu, který provádí zpracování čísel, není zapotřebí spolu s číslem uchovávat i pozici binární tečky, což výrazně snižuje počet bitů, které je zapotřebí rezervovat pro čísla ze zadaného rozsahu. To je tedy první přednost systému pevné řádové tečky – pokud programátor dopředu zná rozsah všech zpracovávaných hodnot a požadovanou přesnost, může být výhodné tento systém použít. Programátor také díky explicitním určení polohy řádové tečky může určit, ve kterém místě programu se musí přesnost či rozsah zvýšit a kdy naopak snížit. Lépe se tak využije počet bitů, které můžeme pro uložení jednoho čísla obětovat (typicky je tento počet bitů roven délce slova mikroprocesoru, popř. jeho celočíselnému násobku či naopak podílu).

Navíc je možné základní matematické operace (sčítání, odčítání, násobení a dělení) poměrně jednoduše implementovat i při použití formátu pevné řádové tečky. V případě, že není k dispozici specializovaný (a současně velmi komplikovaný) matematický koprocesor, je mnohdy mnohem jednodušší a rychlejší implementovat matematické operace v FX formátu. To je případ mnoha jednočipových mikroprocesorů (mikrořadičů), našeho oblíbeného čipu Zilog Z80, signálových procesorů, ale i specializovaných zařízení obsahujících programovatelné obvody CPLD či FPGA. Dnes sice mají komplikovanější (a dražší) FPGA implementovanou i jednotku FPU, ale mnohdy je výhodnější použít FPGA bez této jednotky a potřebné operace si do tohoto obvodu „vypálit“ po svém.

Třetí výhodou je fakt, že u FX formátu může programátor navrhnout a posléze také dodržet požadovanou přesnost všech prováděných výpočtů. To je velký rozdíl oproti FP formátu (resp. jeho podmnožinám, které se nejčastěji používají). Není vzácností narazit na programy, které používají datové typy float či double a přitom jsou výpočty prováděné v těchto programech zatíženy velkou chybou, protože si programátoři plně neuvědomují některé limity FP formátu. Kritické jsou například výpočty s peněžními hodnotami, ale i pouhé sčítání čísel, jež se od sebe o mnoho řádů liší, vede k velkým chybám, které dokonce mohou zapříčinit vznik nekonečných smyček, populární dělení nulou atd.

FX formát má však i některé nevýhody. První nevýhoda spočívá v tom, že tento formát není příliš podporován, a to ani po programové stránce (podpora v programovacích jazycích), ani výrobci mikroprocesorů pro počítače PC. Situace je však odlišná v oblasti jednočipových mikropočítačů, signálových procesorů (DSP), řídicích systémů, nebo například u IBM RS 6000, který kromě jednotky FPU obsahuje i FXU – jednotku pro provádění výpočtů v pevné řádové binární čárce. Na platformě x86 je možné pro FX formát použít instrukce MMX nebo jejich novější varinty (AVX atd.).

Dále může být použití FX formátu nevýhodné v případě, že se mají zpracovávat numerické hodnoty, které mají velkou dynamiku, tj. poměr mezi nejvyšší a nejnižší absolutní hodnotou. V takovém případě by se mohlo stát, že by se při použití FX formátu muselo pro každé číslo alokovat velké množství bitů, které by mohlo dokonce překročit počet bitů nutných pro FP formát. Také v případě, kdy dopředu nevíme, jaké hodnoty se budou zpracovávat, může být výhodnější použití FP formátu. Zde se však nabízí otázka, ve kterých případech nevíme, jaké hodnoty můžeme na vstupu získat: většinou je již z podstaty úlohy dopředu známé, s čím je možné počítat a které hodnoty jsou naprosto nesmyslné. Je však pravdou, že takovou analýzu málokdo dělá a když při výpočtech ve floatech dochází k chybám, tak se bez přemýšlení program přepíše na doubly a problém se tak buď odstraní, nebo alespoň odsune na pozdější dobu, například do chvíle, kdy jsou programu předložena reálná data a ne „pouze“ data testovací.

14. Uložení čísel ve formátu plovoucí řádové (binární) tečky

Uložení racionálních čísel ve formátu plovoucí řádové tečky (FP formát) se od FX formátu odlišuje především v tom, že si každá numerická hodnota sama v sobě nese polohu řádové tečky. Z tohoto důvodu je kromě bitů, které jsou rezervovány pro uložení významných číslic numerické hodnoty, nutné pro každou numerickou hodnotu rezervovat i další bity, pomocí nichž je určena mocnina o nějakém základu (typicky 2, 8, 10 či 16), kterou musí být významné číslice vynásobeny resp. vyděleny. První část čísla uloženého v FP formátu se nazývá mantisa, druhá část exponent. Obecný formát uložení a způsob získání původního čísla je následující:

xFP=be×m

kde:

  1. xFX značí reprezentovanou numerickou hodnotu z podmnožiny reálných čísel
  2. b je báze, někdy také nazývaná radix
  3. e je hodnota exponentu (může být i záporná)
  4. m je mantisa, která může být i záporná

Konkrétní formát numerických hodnot reprezentovaných v systému plovoucí řádové tečky závisí především na volbě báze (radixu) a také na počtu bitů rezervovaných pro uložení mantisy a exponentu. V minulosti existovalo mnoho různých formátů plovoucí řádové tečky (vzpomíná si někdo na Turbo Pascal s jeho šestibytovým datovým typem real?), dnes se však, ustálilo použití formátů specifikovaných v normě IEEE 754. Ovšem to není případ ZX Spectra, které má svůj vlastní formát, jenž si popíšeme v dalším textu.

15. Formát uložení FP hodnot na ZX Spectru

Při volbě konkrétního formátu uložení FP hodnot mají programátoři poměrně dosti volné ruce. Musí především zvolit:

  1. Celkovou šířku slova s FP hodnotou
  2. Zda bude mantisa a exponent uložena v binárním či BCD formátu (pro Z80 se BCD naprosto nehodí, na rozdíl od 6502)
  3. Bitová šířka mantisy
  4. Zda je první bit mantisy pevně nastaven na jedničku (a tudíž se nemusí ukládat)
  5. Základ exponentu (typicky 2, 10 či 16)
  6. Bitová šířka exponentu
  7. Formát uložení, tj. kde bude uloženo znaménko, kde mantisa a kde exponent (FP hodnota je vlastně bitové pole)

Existuje mnoho formátů, z nichž nejzajímavější či nejpoužívanější vypadají následovně:

IBM 1130, IBM 1800 32 Bit single precision floating point
 
 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+---------------------------------------------+---------------+
|S|                mantissa                     |exp. with bias |
+-+---------------------------------------------+---------------+
 
 
GE-635 36 Bit single precision floating point
 
 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3
+-+-------------+-+-----------------------------------------------------+
|S|  Two's exp. |S|            Two's complement mantissa                |
+-+-------------+-+-----------------------------------------------------+
 
 
Intel 8008, 8080 floating point UCRL-51940
 
 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+-+-------------+-----------------------------------------------+
|S|  Two's exp. |                 positive mantissa             |
+-+-------------+-----------------------------------------------+
 
 
Altair BASIC 8080 floating point
 
 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+---------------+-+---------------------------------------------+
|exp. with bias |S|       positive mantissa, MSB = 2^-1         |
+---------------+-+---------------------------------------------+
 
 
Z80 40 Bit floating point
 
 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+-+-------------+-+-------------------------------------------------------------+
|S|  Two's exp. |S|            Two's complement mantissa, MSB = 2^1             |
+-+-------------+-+-------------------------------------------------------------+
 
 
IEEE 754 single precision
 
 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+-+---------------+---------------------------------------------+
|S| exp. with bias|       positive mantissa, MSB = 2^-1         |
+-+---------------+---------------------------------------------+

Na ZX Spectru je použit 40bitový formát, přičemž 40 bitů bylo pravděpodobně zvoleno z toho důvodu, že všech pět univerzálních pracovních registrů má dohromady přesně tuto šířku (ale to může být jen spekulace):

 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+-+-------------+-+-------------------------------------------------------------+
|exp. with bias |S|                       mantissa                              |
+-+-------------+-+-------------------------------------------------------------+
Poznámka: jedná se o formát používaný subrutinami v ROM a taktéž BASICem. Není však problém (je to pouze hodně pracné) si navrhnout formát vlastní.

Předpokládá se, že první bit mantisy je roven jedničce (což dává smysl), proto tento první bit není uložen a na jeho místě je znaménko mantisy. Exponent je posunutý o hodnotu 128 (viz další příklady).

Navíc tento formát obsahuje výjimku pro celočíselné hodnoty v rozsahu od –65535 až +65535. Tyto hodnoty jsou uloženy takovým způsobem, že první bajt (posunutý exponent) je nulový, druhý bajt obsahuje samé nuly pro kladná čísla a samé jedničky pro čísla záporná a teprve třetí a čtvrtý bajt obsahuje 16bitovou hodnotu bez znaménka. Poslední bajt je opět nulový. Tuto výjimku si podrobněji ukážeme příště.

16. Zásobník FP hodnot na ZX Spectru, subrutina pro tisk FP hodnot

V ROM ZX Spectra je uloženo několik subrutin určených pro zpracování FP hodnot. Z těchto subrutin nás dnes budou zajímat pouze dvě:

2DE3: THE 'PRINT A FLOATING-POINT NUMBER' SUBROUTINE
This subroutine prints x, the 'last value' on the calculator stack. The print format never occupies more than 14 spaces.
 
2AB1: THE 'STK-STORE' SUBROUTINE
This subroutine passes the values held in the A, B, C, D and E
A   First byte (when entering at STK_STO or STK_STORE)
B   Fifth byte
C   Fourth byte
D   Third byte
E   Second byte
Poznámka: dejte si pozor na druhou subrutinu, protože její ústřední část začíná až na adrese 0×2AB6 a nikoli na adrese 0×2AB1. Proto v demonstračním příkladu použijeme první adresu:
PRINT_FP      equ $2DE3
STK_STORE     equ $2AB6

Subrutina STK_STORE uloží FP hodnotu předanou ve všech pěti pracovních registrech (kromě HL) na zásobník (stack) používaný při FP výpočtech. Podrobnosti si řekneme příště, protože dnes nám postačuje vědět, jaké registry a v jakém pořadí jsou použity pro předání FP hodnoty:

        ; A  první bajt
        ; B  pátý byte
        ; C  čtvrtý byte
        ; D  třetí byte
        ; E  druhý bajt
        ld   A, ...
        ld   E, ...
        ld   D, ...
        ld   C, ...
        ld   B, ...
        call STK_STORE      ; uložit FP hodnotu na zásobník

Subrutina PRINT_FP přečte hodnotu z výše zmíněného zásobníku a vytiskne ji na obrazovku. Přitom se správně rozpoznají všechny speciální případy, tj. nuly, celočíselné hodnoty –65535..+65535 atd.

        call PRINT_FP       ; vytisknout FP hodnotu uloženou na vrcholu zásobníku
new_line:
        ld   A, 0x0d        ; kód znaku pro odřádkování
        rst  0x10           ; zavolání rutiny v ROM

Můžeme si tedy otestovat, jaké FP hodnoty jsou reprezentovány následujícími sekvencemi čtyřiceti bitů. Otestování bude snadné – pětici bajtů (40 bitů) uložíme do „FP zásobníku“ subrutinou STK_STORE a následně hodnotu vytiskneme zavoláním subrutiny PRINT_FP. Této subrutině se nepředávají žádné parametry – ty jsou na „FP zásobníku“:

        ld IX, fp0             ; adresa s pěticí bajtů FP hodnoty
        call print_fp_number   ; tisk FP hodnoty na obrazovku s odřádkováním

Tisknout budeme FP hodnoty reprezentované těmito sekvencemi bitů:

;         mantisa+128  s  exponent
fp0:    db %00000000, %00000000, %00000000, %00000000, %00000000
fp1:    db %10000000, %00000000, %00000000, %00000000, %00000000
fp2:    db %10000000, %10000000, %00000000, %00000000, %00000000
fp3:    db %10000000, %01000000, %00000000, %00000000, %00000000
fp4:    db %10000001, %01000000, %00000000, %00000000, %00000000
fp5:    db %10000001, %01000000, %00000000, %00000001, %00000000
fp6:    db %10000001, %00111111, %11111111, %11111111, %00000000

Výsledek by měl vypadat následovně:

Obrázek 8: Sedm FP hodnot vypsaných na obrazovce ZX Spectra.

Proč hodnoty vypadají tak, jak vypadají:

  1. První hodnota je 16bitovým číslem se znaménkem
  2. Druhá hodnota obsahuje exponent=0 a mantisa má nastaven pouze nejvyšší bit za binární tečkou (ten se, jak víme, neukládá)
  3. Třetí hodnota je totožná, jako hodnota druhá, ovšem je nastaven znaménkový bit
  4. Čtvrtá hodnota obsahuje exponent=0 a mantisa je rovna 0.11 (binárně), tedy 0,5+0,25=0,75 dekadicky
  5. Pátá hodnota má exponent=1 (×2) a mantisu stejnou, jako hodnota předchozí, tedy 2×0,75=1,5
  6. Šestá hodnota je nepatrně vyšší, než hodnota pátá (viz v pořadí devátý bit zprava)
  7. Sedmá hodnota je naopak nepatrně menší, než hodnota pátá

Pro tisk FP hodnoty je použita tato subrutina:

print_fp_number:
        ; A  první bajt
        ; B  pátý byte
        ; C  čtvrtý byte
        ; D  třetí byte
        ; E  druhý bajt
        ld   A, (IX)
        ld   E, (IX+1)
        ld   D, (IX+2)
        ld   C, (IX+3)
        ld   B, (IX+4)
        call STK_STORE      ; uložit FP hodnotu na zásobník
        call PRINT_FP       ; vytisknout FP hodnotu uloženou na vrcholu zásobníku
new_line:
        ld   A, 0x0d        ; kód znaku pro odřádkování
        rst  0x10           ; zavolání rutiny v ROM
        ret                 ; návrat ze subrutiny

17. Úplný zdrojový kód dnešního pátého demonstračního příkladu

Úplný zdrojový kód dnešního pátého a současně i posledního demonstračního příkladu je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/83-print-fp-numbers.asm:

ENTRY_POINT   equ $8000
ROM_CLS       equ $0DAF
PRINT_FP      equ $2DE3
STK_STORE     equ $2AB6
 
 
        org ENTRY_POINT
 
start:
        call ROM_CLS           ; smazání obrazovky a otevření kanálu číslo 2 (screen)
 
        ld IX, fp0             ; adresa s pěticí bajtů FP hodnoty
        call print_fp_number   ; tisk FP hodnoty na obrazovku s odřádkováním
 
        ld IX, fp1             ; adresa s pěticí bajtů FP hodnoty
        call print_fp_number   ; tisk FP hodnoty na obrazovku s odřádkováním
 
        ld IX, fp2             ; adresa s pěticí bajtů FP hodnoty
        call print_fp_number   ; tisk FP hodnoty na obrazovku s odřádkováním
 
        ld IX, fp3             ; adresa s pěticí bajtů FP hodnoty
        call print_fp_number   ; tisk FP hodnoty na obrazovku s odřádkováním
 
        ld IX, fp4             ; adresa s pěticí bajtů FP hodnoty
        call print_fp_number   ; tisk FP hodnoty na obrazovku s odřádkováním
 
        ld IX, fp5             ; adresa s pěticí bajtů FP hodnoty
        call print_fp_number   ; tisk FP hodnoty na obrazovku s odřádkováním
 
        ld IX, fp6             ; adresa s pěticí bajtů FP hodnoty
        call print_fp_number   ; tisk FP hodnoty na obrazovku s odřádkováním
 
        ret
 
;         mantisa+128  s  exponent
fp0:    db %00000000, %00000000, %00000000, %00000000, %00000000
fp1:    db %10000000, %00000000, %00000000, %00000000, %00000000
fp2:    db %10000000, %10000000, %00000000, %00000000, %00000000
fp3:    db %10000000, %01000000, %00000000, %00000000, %00000000
fp4:    db %10000001, %01000000, %00000000, %00000000, %00000000
fp5:    db %10000001, %01000000, %00000000, %00000001, %00000000
fp6:    db %10000001, %00111111, %11111111, %11111111, %00000000
 
 
print_fp_number:
        ; A  první bajt
        ; B  pátý byte
        ; C  čtvrtý byte
        ; D  třetí byte
        ; E  druhý bajt
        ld   A, (IX)
        ld   E, (IX+1)
        ld   D, (IX+2)
        ld   C, (IX+3)
        ld   B, (IX+4)
        call STK_STORE      ; uložit FP hodnotu na zásobník
        call PRINT_FP       ; vytisknout FP hodnotu uloženou na vrcholu zásobníku
new_line:
        ld   A, 0x0d        ; kód znaku pro odřádkování
        rst  0x10           ; zavolání rutiny v ROM
        ret                 ; návrat ze subrutiny
 
 
end ENTRY_POINT

Překlad do assembleru:

root_podpora

ENTRY_POINT     EQU 8000
ROM_CLS         EQU 0DAF
PRINT_FP        EQU 2DE3
STK_STORE       EQU 2AB6
                ORG 8000
8000:           label start
8000:CDAF0D     CALL 0DAF
8003:DD213580   LD IX, 8035
8007:CD5880     CALL 8058
800A:DD213A80   LD IX, 803A
800E:CD5880     CALL 8058
8011:DD213F80   LD IX, 803F
8015:CD5880     CALL 8058
8018:DD214480   LD IX, 8044
801C:CD5880     CALL 8058
801F:DD214980   LD IX, 8049
8023:CD5880     CALL 8058
8026:DD214E80   LD IX, 804E
802A:CD5880     CALL 8058
802D:DD215380   LD IX, 8053
8031:CD5880     CALL 8058
8034:C9         RET
8035:           label fp0
8035:00000000   DEFB of 5 bytes
8039:00
803A:           label fp1
803A:80000000   DEFB of 5 bytes
803E:00
803F:           label fp2
803F:80800000   DEFB of 5 bytes
8043:00
8044:           label fp3
8044:80400000   DEFB of 5 bytes
8048:00
8049:           label fp4
8049:81400000   DEFB of 5 bytes
804D:00
804E:           label fp5
804E:81400001   DEFB of 5 bytes
8052:00
8053:           label fp6
8053:813FFFFF   DEFB of 5 bytes
8057:00
8058:           label print_fp_number
8058:DD7E00     LD A, (IX+00)
805B:DD5E01     LD E, (IX+01)
805E:DD5602     LD D, (IX+02)
8061:DD4E03     LD C, (IX+03)
8064:DD4604     LD B, (IX+04)
8067:CDB62A     CALL 2AB6
806A:CDE32D     CALL 2DE3
806D:           label new_line
806D:3E0D       LD A, 0D
806F:D7         RST 10
8070:C9         RET
8071:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8070

18. Příloha: upravený soubor Makefile pro překlad demonstračních příkladů

Výše uvedené demonstrační příklady i příklady, které již byly popsány v předchozích devíti článcích [1] [2], [3], [4], [5], [6], [7], [8], [9], je možné přeložit s využitím souboru Makefile, jehož aktuální verze vypadá následovně (pro překlad a slinkování je použit assembler Pasmo):

ASSEMBLER := pasmo
 
all: 01.tap 02.tap 03.tap 04.tap 05.tap 06.tap 07.tap 08.tap 09.tap 10.tap \
    11.tap 12.tap 13.tap 14.tap 15.tap 16.tap 17.tap 18.tap 19.tap 20.tap \
    21.tap 22.tap 23.tap 24.tap 25.tap 26.tap 27.tap 28.tap 29.tap 30.tap \
    31.tap 32.tap 33.tap 34.tap 35.tap 36.tap 37.tap 38.tap 39.tap 40.tap \
    41.tap 42.tap 43.tap 44.tap 45.tap 46.tap 47.tap 48.tap 49.tap 50.tap \
    51.tap 52.tap 53.tap 54.tap 55.tap 56.tap 57.tap 58.tap 59.tap 60.tap \
    61.tap 62.tap 63.tap 64.tap 65.tap 66.tap 67.tap 68.tap 69.tap 70.tap \
    71.tap 72.tap 73.tap 74.tap 75.tap 76.tap 77.tap 78.tap 79.tap 80.tap \
    81.tap 82.tap 83.tap
 
clean:
        rm -f *.tap
 
.PHONY: all clean
 
 
01.tap: 01-color-attribute.asm
        $(ASSEMBLER) -v -d --tap $< $@ > 01-color-attribute.lst
 
02.tap: 02-blinking-attribute.asm
        $(ASSEMBLER) -v -d --tap $< $@ > 02-blinking-attribute.lst
 
03.tap: 03-symbolic-names.asm
        $(ASSEMBLER) -v -d --tap $< $@ > 03-symbolic-names.lst
 
04.tap: 04-operators.asm
        $(ASSEMBLER) -v -d --tap $< $@ > 04-operators.lst
 
05.tap: 05-better-symbols.asm
        $(ASSEMBLER) -v -d --tap $< $@ > 05-better-symbols.lst
 
06.tap: 06-tapbas-v1.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 06-tapbas-v1.lst
 
07.tap: 07-tapbas-v2.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 07-tapbas-v2.lst
 
08.tap: 08-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 08-loop.lst
 
09.tap: 09-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 09-loop.lst
 
10.tap: 10-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 10-loop.lst
 
11.tap: 11-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 11-loop.lst
 
12.tap: 12-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 12-loop.lst
 
13.tap: 13-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 13-loop.lst
 
14.tap: 14-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 14-loop.lst
 
15.tap: 15-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 15-loop.lst
 
16.tap: 16-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 16-loop.lst
 
17.tap: 17-loop.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 17-loop.lst
 
18.tap: 18-cls.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 18-cls.lst
 
19.tap: 19-print-char-call.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 19-print-char-call.lst
 
20.tap: 20-print-char-rst.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 20-print-char-rst.lst
 
21.tap: 21-print-char.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 21-print-char.lst
 
22.tap: 22-print-all-chars.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 22-print-all-chars.lst
 
23.tap: 23-print-all-chars.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 23-print-all-chars.lst
 
24.tap: 24-change-color.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 24-change-color.lst
 
25.tap: 25-change-flash.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 25-change-flash.lst
 
26.tap: 26-print-at.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 26-print-at.lst
 
27.tap: 27-print-string.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 27-print-string.lst
 
28.tap: 28-print-string.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 28-print-string.lst
 
29.tap: 29-print-colorized-string.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 29-print-colorized-string.lst
 
30.tap: 30-print-string-ROM.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 30-print-string-ROM.lst
 
31.tap: 31-attributes.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 31-attributes.lst
 
32.tap: 32-fill-in-vram.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 32-fill-in-vram.lst
 
33.tap: 33-fill-in-vram-no-ret.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 33-fill-in-vram-no-ret.lst
 
34.tap: 34-fill-in-vram-pattern.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 34-fill-in-vram-pattern.lst
 
35.tap: 35-slow-fill-in-vram.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 35-slow-fill-in-vram.lst
 
36.tap: 36-slow-fill-in-vram-no-ret.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 36-slow-fill-in-vram-no-ret.lst
 
37.tap: 37-fill-block.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 37-fill-block.lst
 
38.tap: 38-fill-block-with-pattern.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 38-fill-block-with-pattern.lst
 
39.tap: 39-fill-block-optimized.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 39-fill-block-optimized.lst
 
40.tap: 40-draw-char.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 40-draw-char.lst
 
41.tap: 41-draw-any-char.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 41-draw-any-char.lst
 
42.tap: 42-block-anywhere.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 42-block-anywhere.lst
 
43.tap: 43-block-anywhere-rrca.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 43-block-anywhere-rrca.lst
 
44.tap: 44-better-draw-char.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 44-better-draw-char.lst
 
45.tap: 45-even-better-draw-char.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 45-even-better-draw-char.lst
 
46.tap: 46-draw-char-at.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 46-draw-char-at.lst
 
47.tap: 47-draw-char-at-unrolled.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 47-draw-char-at-unrolled.lst
 
48.tap: 48-incorrect-print-string.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 48-incorrect-print-string.lst
 
49.tap: 49-correct-print-string.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 49-correct-print-string.lst
 
50.tap: 50-ascii-table.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 50-ascii-table.lst
 
51.tap: 51-plot-block.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 51-plot-block.lst
 
52.tap: 52-plot-pixel.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 52-plot-pixel.lst
 
53.tap: 53-plot-pixel.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 53-plot-pixel.lst
 
54.tap: 54-plot-pixel-on-background.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 54-plot-pixel-on-background.lst
 
55.tap: 55-plot-pixel-on-background.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 55-plot-pixel-on-background.lst
 
56.tap: 56-inverse-ascii-table.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 56-inverse-ascii-table.lst
 
57.tap: 57-plot-pixel-on-inverse-background.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 57-plot-pixel-on-inverse-background.lst
 
58.tap: 58-plot-inverse-pixel-on-inverse-background.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 58-plot-inverse-pixel-on-inverse-background.lst
 
59.tap: 59-configurable-ascii-table.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 59-configurable-ascii-table.lst
 
60.tap: 60-plot-over.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 60-plot-over.lst
 
61.tap: 61-print-number-A.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 61-print-number-A.lst
 
62.tap: 62-print-number-B.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 62-print-number-B.lst
 
63.tap: 63-print-number-C.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 63-print-number-C.lst
 
64.tap: 64-print-number-D.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 64-print-number-D.lst
 
65.tap: 65-more-numbers-A.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 65-more-numbers-A.lst
 
66.tap: 66-more-numbers-B.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 66-more-numbers-B.lst
 
67.tap: 67-print-flags-1.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 67-print-flags-1.lst
 
68.tap: 68-print-flags-2.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 68-print-flags-2.lst
 
69.tap: 69-print-flags-3.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 69-print-flags-3.lst
 
70.tap: 70-print-flags-4.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 70-print-flags-4.lst
 
71.tap: 71-print-flags-5.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 71-print-flags-5.lst
 
72.tap: 72-print-flags-6.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 72-print-flags-6.lst
 
73.tap: 73-print-flags-7.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 73-print-flags-7.lst
 
74.tap: 74-print-hex-number.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 74-print-hex-number.lst
 
75.tap: 75-print-hex-number.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 75-print-hex-number.lst
 
76.tap: 76-print-hex-numbers.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 76-print-hex-numbers.lst
 
77.tap: 77-add-hex-numbers.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 77-add-hex-numbers.lst
 
78.tap: 78-add-bcd-numbers.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 78-add-bcd-numbers.lst
 
79.tap: 79-print-hex-digit-jmp.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 79-print-hex-digit-jmp.lst
 
80.tap: 80-print-hex-digit-overflow.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 80-print-hex-digit-overflow.lst
 
81.tap: 81-print-hex-digit-daa.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 81-print-hex-digit-daa.lst
 
82.tap: 82-print-hex-numbers-daa.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 82-print-hex-numbers-daa.lst
 
83.tap: 83-print-fp-numbers.asm
        $(ASSEMBLER) -v -d --tapbas $< $@ > 83-print-fp-numbers.lst

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

V tabulce zobrazené pod tímto odstavcem jsou uvedeny odkazy na všechny prozatím popsané demonstrační příklady určené pro překlad a spuštění na osmibitovém domácím mikropočítači ZX Spectrum (libovolný model či jeho klon), které jsou psány v assembleru mikroprocesoru Zilog Z80. Pro překlad těchto demonstračních příkladů je možné použít například assembler Pasmo (viz též úvodní článek):

# Soubor Stručný popis Adresa
1 01-color-attribute.asm modifikace jednoho barvového atributu https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/01-color-attribute.asm
2 02-blinking-attribute.asm barvový atribut s nastavením bitů pro blikání a vyšší intenzitu https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/02-blinking-attribute.asm
3 03-symbolic-names.asm symbolická jména v assembleru https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/03-symbolic-names.asm
4 04-operators.asm operátory a operace se symbolickými hodnotami https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/04-operators.asm
5 05-better-symbols.asm tradičnější symbolická jména https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/05-better-symbols.asm
6 06-tapbas-v1.asm vygenerování BASICovského loaderu (neúplný příklad) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/06-tapbas-v1.asm
7 07-tapbas-v2.asm vygenerování BASICovského loaderu (úplný příklad) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/07-tapbas-v2.asm
8 08-loop.asm jednoduchá počítaná programová smyčka: naivní varianta https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/08-loop.asm
9 09-loop.asm programová smyčka: zkrácení kódu pro vynulování použitých pracovních registrů https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/09-loop.asm
10 10-loop.asm programová smyčka: optimalizace skoku na konci smyčky (instrukce DJNZ) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/10-loop.asm
11 11-loop.asm programová smyčka: optimalizace využití pracovních registrů https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/11-loop.asm
12 12-loop.asm programová smyčka: použití pracovního registru IX https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/12-loop.asm
13 13-loop.asm programová smyčka: použití pracovního registru IY https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/13-loop.asm
14 14-loop.asm programová smyčka se šestnáctibitovým počitadlem, základní varianta https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/14-loop.asm
15 15-loop.asm programová smyčka se šestnáctibitovým počitadlem, vylepšená varianta https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/15-loop.asm
16 16-loop.asm použití relativního skoku a nikoli skoku absolutního https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/16-loop.asm
17 17-loop.asm programová smyčka: inc l namísto inc hl https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/17-loop.asm
       
18 18-cls.asm smazání obrazovky a otevření kanálu číslo 2 (screen) přes funkci v ROM https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/18-cls.asm
19 19-print-char-call.asm smazání obrazovky a výpis jednoho znaku na obrazovku přes funkci v ROM (použití instrukce CALL) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/19-print-char-call.asm
20 20-print-char-rst.asm smazání obrazovky a výpis jednoho znaku na obrazovku přes funkci v ROM (použití instrukce RST) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/20-print-char-rst.asm
21 21-print-char.asm pouze výpis jednoho znaku na obrazovku bez jejího smazání https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/21-print-char.asm
22 22-print-all-chars.asm výpis znakové sady znak po znaku (nekorektní verze příkladu) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/22-print-all-chars.asm
23 23-print-all-chars.asm výpis znakové sady znak po znaku (korektní verze příkladu) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/23-print-all-chars.asm
24 24-change-color.asm změna barvových atributů (popředí a pozadí) vypisovaných znaků https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/24-change-color.asm
25 25-change-flash.asm povolení či zákaz blikání vypisovaných znaků https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/25-change-flash.asm
26 26-print-at.asm výpis znaku či znaků na určené místo na obrazovce https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/26-print-at.asm
27 27-print-string.asm výpis celého řetězce explicitně zapsanou programovou smyčkou (základní varianta) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/27-print-string.asm
28 28-print-string.asm výpis celého řetězce explicitně zapsanou programovou smyčkou (vylepšená varianta) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/28-print-string.asm
29 29-print-colorized-string.asm výpis řetězce, který obsahuje i řídicí znaky pro změnu barvy atd. https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/29-print-colorized-string.asm
30 30-print-string-ROM.asm výpis řetězce s využitím služby/subrutiny uložené v ROM ZX Spectra https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/30-print-string-ROM.asm
       
31 31-attributes.asm modifikace atributů pro tisk řetězce subrutinou uloženou v ROM https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/31-attributes.asm
32 32-fill-in-vram.asm vyplnění celé bitmapy barvou popředí, návrat do systému https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/32-fill-in-vram.asm
33 33-fill-in-vram-no-ret.asm vyplnění celé bitmapy barvou popředí, bez návratu do systému https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/33-fill-in-vram-no-ret.asm
34 34-fill-in-vram-pattern.asm vyplnění celé bitmapy zvoleným vzorkem https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/34-fill-in-vram-pattern.asm
35 35-slow-fill-in-vram.asm pomalé vyplnění celé bitmapy, vizualizace struktury bitmapy https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/35-slow-fill-in-vram.asm
36 36-slow-fill-in-vram-no-ret.asm pomalé vyplnění celé bitmapy, vizualizace struktury bitmapy, bez návratu do systému https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/36-slow-fill-in-vram-no-ret.asm
37 37-fill-block.asm vykreslení bloku 8×8 pixelů https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/37-fill-block.asm
38 38-fill-block-with-pattern.asm vykreslení bloku 8×8 pixelů zvoleným vzorkem https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/38-fill-block-with-pattern.asm
39 39-fill-block-optimized.asm optimalizace předchozího příkladu https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/39-fill-block-optimized.asm
40 40-draw-char.asm vykreslení znaku do levého horního rohu https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/40-draw-char.asm
41 41-draw-any-char.asm podprogram pro vykreslení libovolně zvoleného znaku do levého horního rohu https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/41-draw-any-char.asm
42 42-block-anywhere.asm podprogramy pro vykreslení bloku 8×8 pixelů kamkoli na obrazovku https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/42-block-anywhere.asm
       
43 43-block-anywhere-rrca.asm podprogramy pro vykreslení bloku 8×8 pixelů kamkoli na obrazovku, vylepšená varianta https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/43-block-anywhere-rrca.asm
44 44-better-draw-char.asm vykreslení znaku v masce 8×8 pixelů, vylepšená varianta https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/44-better-draw-char.asm
45 45-even-better-draw-char.asm posun offsetu pro vykreslení dalšího znaku https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/45-even-better-draw-char.asm
46 46-draw-char-at.asm vykreslení znaku v masce 8×8 pixelů kamkoli na obrazovku https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/46-draw-char-at.asm
47 47-draw-char-at-unrolled.asm vykreslení znaku v masce 8×8 pixelů kamkoli na obrazovku https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/47-draw-char-at-unrolled.asm
48 48-incorrect-print-string.asm tisk řetězce, nekorektní varianta https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/48-incorrect-print-string.asm
49 49-correct-print-string.asm tisk řetězce, korektní varianta https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/49-correct-print-string.asm
       
50 50-ascii-table.asm tisk několika bloků ASCII tabulky https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/50-ascii-table.asm
51 51-plot-block.asm vykreslení pixelu verze 1: zápis celého bajtu na pozici pixelu https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/51-plot-block.asm
52 52-plot-pixel.asm vykreslení pixelu verze 2: korektní vykreslení jednoho pixelu, ovšem překreslení celého bajtu https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/52-plot-pixel.asm
53 53-plot-pixel.asm vykreslení pixelu verze 3: vylepšená verze předchozího demonstračního příkladu https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/53-plot-pixel.asm
54 54-plot-pixel-on-background.asm vykreslení pixelu vůči pozadí (nekorektní varianta) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/54-plot-pixel-on-background.asm
55 55-plot-pixel-on-background.asm vykreslení pixelu vůči pozadí (korektní varianta) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/55-plot-pixel-on-background.asm
       
56 56-inverse-ascii-table.asm vykreslení ASCII tabulky inverzní barvou (inkoust vs. papír) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/56-inverse-ascii-table.asm
57 57-plot-pixel-on-inverse-background.asm vykreslení pixelů barvou papíru proti inverzní ASCII tabulce https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/57-plot-pixel-on-inverse-background.asm
58 58-plot-inverse-pixel-on-inverse-background.asm vykreslení pixelů inverzní barvou proti inverzní ASCII tabulce https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm58-plot-inverse-pixel-on-inverse-background.asm/
59 59-configurable-ascii-table.asm vykreslení ASCII tabulky buď přímo inkoustem nebo inverzně https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/59-configurable-ascii-table.asm
60 60-plot-over.asm přibližná implementace příkazu PLOT OVER https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/60-plot-over.asm
       
61 61-print-number-A.asm ukázka použití podprogramu pro tisk celého čísla https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/61-print-number-A.asm
62 62-print-number-B.asm pokus o vytištění záporných čísel https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/62-print-number-B.asm
63 63-print-number-C.asm tisk maximální podporované hodnoty 9999 https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/63-print-number-C.asm
64 64-print-number-D.asm tisk vyšší než podporované hodnoty 10000 https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/64-print-number-D.asm
65 65-more-numbers-A.asm vytištění číselné řady https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/65-more-numbers-A.asm
66 66-more-numbers-B.asm kombinace tisku celočíselných hodnot s dalšími subrutinami https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/66-more-numbers-B.asm
67 67-print-flags-1.asm příznakové bity po provedení celočíselné operace 1+2 https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/67-print-flags-1.asm
68 68-print-flags-2.asm příznakové bity po provedení celočíselné operace 0+0 https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/68-print-flags-2.asm
69 69-print-flags-3.asm příznakové bity po provedení operace 255+1 https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/69-print-flags-3.asm
70 70-print-flags-4.asm příznakové bity po provedení operace 254+1 https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/70-print-flags-4.asm
71 71-print-flags-5.asm příznakové bity po provedení operace 255+255 https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/71-print-flags-5.asm
72 72-print-flags-6.asm výsledek operace 100+100, nastavení příznakových bitů https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/72-print-flags-6.asm
73 73-print-flags-7.asm výsledek operace 128+128, nastavení příznakových bitů https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/73-print-flags-7.asm
       
74 74-print-hex-number.asm tisk hexadecimálního čísla v rozsahu 0×00 až 0×ff (neoptimalizovaná varianta) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/74-print-hex-number.asm
75 75-print-hex-number.asm tisk hexadecimálního čísla v rozsahu 0×00 až 0×ff (optimalizovaná varianta) https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/75-print-hex-number.asm
76 76-print-hex-numbers.asm tisk několika hexadecimálních hodnot https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/76-print-hex-numbers.asm
77 77-add-hex-numbers.asm součet dvou osmibitových hexadecimálních hodnot s tiskem všech výsledků https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/77-add-hex-numbers.asm
78 78-add-bcd-numbers.asm součet dvou osmibitových BCD hodnot s tiskem všech výsledků https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/78-add-bcd-numbers.asm
       
79 79-print-hex-digit-jmp.asm tisk jedné hexadecimální cifry s využitím podmíněného skoku https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/79-print-hex-digit-jmp.asm
80 80-print-hex-digit-overflow.asm otestování, jaký znak je vytištěn pro hodnoty větší než 15 https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/80-print-hex-digit-overflow.asm
81 81-print-hex-digit-daa.asm tisk jedné hexadecimální cifry s využitím instrukce DAA https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/81-print-hex-digit-daa.asm
82 82-print-hex-numbers-daa.asm tisk série hexadecimálních hodnot s využitím instrukce DAA https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/82-print-hex-numbers-daa.asm
83 83-print-fp-numbers.asm tisk numerických hodnot reprezentovaných v systému plovoucí řádové tečky https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/83-print-fp-numbers.asm
       
84 Makefile Makefile pro překlad a slinkování všech demonstračních příkladů do podoby obrazu magnetické pásky https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/Makefile

20. Odkazy na Internetu

  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. The system variables
    https://worldofspectrum.or­g/ZXBasicManual/zxmanchap25­.html
  39. ZX Spectrum manual: chapter #17 Graphics
    https://worldofspectrum.or­g/ZXBasicManual/zxmanchap17­.html
  40. Why does Sinclair BASIC have two formats for storing numbers in the same structure?
    https://retrocomputing.stac­kexchange.com/questions/8834/why-does-sinclair-basic-have-two-formats-for-storing-numbers-in-the-same-structu
  41. Plovoucí řádová čárka na ZX Spectru
    https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/#k05
  42. Norma IEEE 754 a příbuzní: formáty plovoucí řádové tečky
    https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/#k05
  43. 1A1B: THE ‚REPORT AND LINE NUMBER PRINTING‘ SUBROUTINE
    https://skoolkid.github.i­o/rom/asm/1A1B.html
  44. 2DE3: THE ‚PRINT A FLOATING-POINT NUMBER‘ SUBROUTINE
    https://skoolkid.github.i­o/rom/asm/2DE3.html
  45. 5C63: STKBOT – Address of bottom of calculator stack
    https://skoolkid.github.i­o/rom/asm/5C63.html
  46. 5C65: STKEND – Address of start of spare space
    https://skoolkid.github.i­o/rom/asm/5C65.html
  47. Why does Sinclair BASIC have two formats for storing numbers in the same structure?
    https://retrocomputing.stac­kexchange.com/questions/8834/why-does-sinclair-basic-have-two-formats-for-storing-numbers-in-the-same-structu
  48. Chapter 24: The memory
    https://worldofspectrum.or­g/ZXBasicManual/zxmanchap24­.html
  49. Survey of Floating-Point Formats  
    https://mrob.com/pub/math/flo­atformats.html
  50. Convert an 8bit number to hex in z80 assembler
    https://stackoverflow.com/qu­estions/22838444/convert-an-8bit-number-to-hex-in-z80-assembler
  51. 80 MICROPROCESSOR Instruction Set Summary
    http://www.textfiles.com/pro­gramming/CARDS/z80
  52. Extended Binary Coded Decimal Interchange Code
    http://en.wikipedia.org/wiki/EBCDIC
  53. ASCII/EBCDIC Conversion Table
    http://docs.hp.com/en/32212–90008/apcs01.html
  54. EBCDIC
    http://www.hansenb.pdx.edu/DMKB/dic­t/tutorials/ebcdic.php
  55. EBCDIC tables
    http://home.mnet-online.de/wzwz.de/temp/eb­cdic/cc_en.htm
  56. The Mainframe Blog
    http://mainframe.typepad.com/blog/2006/11/my_per­sonal_mai.html
  57. Binary-coded decimal
    https://en.wikipedia.org/wiki/Binary-coded_decimal
  58. BCD
    https://cs.wikipedia.org/wiki/BCD
  59. Z80 heaven: Floating Point
    http://z80-heaven.wikidot.com/floating-point
  60. Z80, the 8-bit Number Cruncher
    http://www.andreadrian.de/ol­dcpu/Z80_number_cruncher.html
  61. Floating-point library for Z80
    https://github.com/DW0RKiN/Floating-point-Library-for-Z80
  62. z80float
    https://github.com/Zeda/z80float
  63. ZX Spectrum BASIC Programming – 2nd Edition
    https://archive.org/details/zx-spectrum-basic-programming/page/n167/mode/2up
  64. ZX Spectrum BASIC Programming – 2nd Edition
    https://archive.org/details/zx-spectrum-basic-programming/page/n169/mode/2up

Byl pro vás článek přínosný?