Aritmetické operace s hodnotami uloženými binárně i ve formátu BCD

13. 4. 2023
Doba čtení: 46 minut

Sdílet

 Autor: Depositphotos
Vyzkoušíme si, jak jsou provedeny základní aritmetické operace při sčítání celočíselných hodnot uložených v binárním tvaru i s využitím formátu BCD – přesněji řečeno ve variantě označované jako Packed BCD.

Obsah

1. Aritmetické operace s hodnotami uloženými binárně i v BCD

2. Podprogram pro tisk hexadecimální cifry i osmibitové hexadecimální hodnoty

3. Demonstrační příklad, který vytiskne několik specifických hexadecimálních hodnot na obrazovku

4. Optimalizace předchozího demonstračního příkladu

5. Rutina pro tisk tabulky s větším množstvím hexadecimálních hodnot

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

7. Součet osmibitových hodnot uložených ve standardním binárním kódu

8. Tisk tabulky se sčítanci i součty

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

10. Uložení numerických hodnot ve formátu BCD a Packed BCD

11. Podpora pro výpočty v BCD v instrukčních sadách mikroprocesorů

12. Příznak half carry a instrukce DAA

13. Vliv příznaků a hodnoty v akumulátoru na operaci DAA

14. Realizace součtu dvou numerických hodnot uložených ve formátu Packed BCD

15. Tisk tabulky se sčítanci i součty

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

17. Obsah následujícího článku

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. Aritmetické operace s hodnotami uloženými binárně i v BCD

Na předchozí článek o tvorbě aplikací pro slavný osmibitový mikropočítač ZX Spectrum v assembleru dnes navážeme. Připomeňme si, že minule jsme si nejprve ukázali, jakým způsobem lze využít rutinu pojmenovanou OUT_NUM1 a uloženou v paměti ROM pro tisk dekadických hodnot v rozsahu od 0 do 9999. Dále jsme si popsali funkce a vlastnosti pěti základních příznaků (flags) mikroprocesoru Zilog Z80 – carry, zero, add/subtract, overflow a sign.

Obrázek 1: Výpis numerických hodnot na obrazovku podprogramem uloženým v ROM. Konkrétně se jedná o minule popsanou subrutinu nazvanou OUT_NUM1.

Dnes si nejprve ukážeme, jak by mohla vypadat poměrně přímočaře naprogramovaná subrutina určená pro tisk osmibitové hexadecimální hodnoty v rozsahu od 0×00 do 0×ff. Uvidíme, že se v této subrutině použije podmíněný skok. Ovšem ve skutečnosti je možné využít určité triky a napsat tuto subrutinu i bez skoku, a to konkrétně tak, že se využije doposud nepopsaný příznak half carry a s ním související instrukce DAA. Proto si nejprve ukážeme, jak probíhá součet (nebo i rozdíl) hodnot uložených v binárním kódu i v kódu BCD (Binary-coded decimal), protože jak příznak half carry, tak i instrukce DAA původně s BCD velmi úzce souvisí (resp. přesněji řečeno souvisí s variantou, která se nazývá packed BCD).

Obrázek 2: Výsledek pokusu o vytištění záporného čísla standardní subrutinou OUT_NUM1.

2. Podprogram pro tisk hexadecimální cifry i osmibitové hexadecimální hodnoty

Ukažme si nejprve, jak by mohla vypadat prozatím dosti primitivně naprogramovaná subrutina určená pro tisk jedné šestnáctkové (hexadecimální) cifry. Tato subrutina předpokládá, že v pracovním registru A (akumulátoru) bude uložena hodnota 0×00 až 0×0f, přičemž spodní cifra 0×0 až 0×f se vytiskne k tomu určeným standardním podprogramem z ROM. V podstatě nám pouze postačuje určit, zda se má tisknout ASCII hodnota „0“ až „9“ či „A“ až „F“. Tyto hodnoty netvoří ucelenou řadu, takže musíme provést podmíněný skok:

print_hex_digit:
        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       ; ASCII kód znaku 'A', ovšem začínáme od desítky, ne od nuly
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
        ret                 ; návrat ze subrutiny
print_0_to_9:
        add  A, 48          ; ASCII kód znaku '0'
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
        ret                 ; návrat ze subrutiny

Aby se vytiskla celá osmibitová hodnota 0×00 až 0×FF, je nutné provést tisk vyšší cifry a poté cifry nižší. To lze realizovat velmi snadno s využitím bitových posunů spojených s maskováním:

print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
 
        pop  AF             ; obnovit A
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
        ret                 ; a návrat z podprogramu
Poznámka: alternativně lze maskování přesunout do subrutiny print_hex_digit, což vede ke kratšímu kódu.

Jak je to obvyklé prakticky ve všech instrukčních sadách, je možné dvojici instrukcí CALL+RET nahradit za jedinou instrukci přímého skoku. Podprogram pro tisk hexadecimální hodnoty tedy pozměníme takto:

print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
 
        pop  AF             ; obnovit A
        and  $0f            ; zamaskovat horní čtyři bity
        jp   print_hex_digit; vytisknout hexa číslici
                            ; a návrat z podprogramu

3. Demonstrační příklad, který vytiskne několik specifických hexadecimálních hodnot na obrazovku

Výše uvedenou rutinu několikrát zavoláme a získáme na tak obrazovce několik hexadecimálních hodnot:

Obrázek 3: Několik hexadecimálních hodnot vytištěných na obrazovku ZX Spectra.

Úplný zdrojový kód dnešního prvního demonstračního příkladu vypadá následovně:

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   A, 0x00        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0x01        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0x09        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0x99        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0x0a        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0xa0        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0xba        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0xff        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ret                 ; návrat z programu do BASICu
 
print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
 
        pop  AF             ; obnovit A
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
        ret                 ; a návrat z podprogramu
 
print_hex_digit:
        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       ; ASCII kód znaku 'A', ovšem začínáme od desítky, ne od nuly
        rst  0x10           ; zavolání rutiny v ROM pro tisk jednoho znaku
        ret                 ; návrat ze subrutiny
print_0_to_9:
        add  A, 48          ; ASCII kód znaku '0'
        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 podívejme se ještě na způsob překladu tohoto zdrojového kódu z assembleru 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:3E00       LD A, 00
8005:CD4480     CALL 8044
8008:CD6180     CALL 8061
800B:3E01       LD A, 01
800D:CD4480     CALL 8044
8010:CD6180     CALL 8061
8013:3E09       LD A, 09
8015:CD4480     CALL 8044
8018:CD6180     CALL 8061
801B:3E99       LD A, 99
801D:CD4480     CALL 8044
8020:CD6180     CALL 8061
8023:3E0A       LD A, 0A
8025:CD4480     CALL 8044
8028:CD6180     CALL 8061
802B:3EA0       LD A, A0
802D:CD4480     CALL 8044
8030:CD6180     CALL 8061
8033:3EBA       LD A, BA
8035:CD4480     CALL 8044
8038:CD6180     CALL 8061
803B:3EFF       LD A, FF
803D:CD4480     CALL 8044
8040:CD6180     CALL 8061
8043:C9         RET
8044:           label print_hex_number
8044:F5         PUSH AF
8045:0F         RRCA
8046:0F         RRCA
8047:0F         RRCA
8048:0F         RRCA
8049:E60F       AND 0F
804B:CD5580     CALL 8055
804E:F1         POP AF
804F:E60F       AND 0F
8051:CD5580     CALL 8055
8054:C9         RET
8055:           label print_hex_digit
8055:FE0A       CP 0A
8057:3804       JR C, 805D
8059:C637       ADD A, 37
805B:D7         RST 10
805C:C9         RET
805D:           label print_0_to_9
805D:C630       ADD A, 30
805F:D7         RST 10
8060:C9         RET
8061:           label new_line
8061:3E0D       LD A, 0D
8063:D7         RST 10
8064:C9         RET
8065:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8064

4. Optimalizace předchozího demonstračního příkladu

Podprogram pro tisk jedné hexadecimální číslice lze dále zjednodušit tak, že pro hodnoty „A“ až „F“ nejprve provedeme mezivýpočet a poté se budeme tvářit, že se jedná o hodnotu „0“ až „9“:

print_hex_digit:
        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
        ret                 ; návrat ze subrutiny

Výsledkem by měla být naprosto stejná obrazovka:

Obrázek 4: Několik hexadecimálních hodnot vytištěných na obrazovku ZX Spectra (totožné s předchozím screenshotem).

Již ve druhé kapitole jsme si řekli, že kód je možné velmi snadno (i když jen nepatrně) optimalizovat tak, že se dvojice za sebou jdoucích instrukcí CALL+RET nahradí za přímý skok. Výsledkem bude tento kód:

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   A, 0x00        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0x01        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0x09        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0x99        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0x0a        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0xa0        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0xba        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ld   A, 0xff        ; vytisknout hexa hodnotu s přechodem na nový řádek
        call print_hex_number
        call new_line
 
        ret                 ; návrat z programu do BASICu
 
print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
 
        pop  AF             ; obnovit A
        and  $0f            ; zamaskovat horní čtyři bity
        jp print_hex_digit  ; vytisknout hexa číslici a návrat z podprogramu
 
print_hex_digit:
        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
        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

Ještě si pro úplnost uveďme symbolicky popsanou podobu výsledného strojového kódu, který je o tři bajty kratší než kód originální:

ENTRY_POINT     EQU 8000
ROM_CLS         EQU 0DAF
OUT_NUM_1       EQU 1A1B
                ORG 8000
8000:           label start
8000:CDAF0D     CALL 0DAF
8003:3E00       LD A, 00
8005:CD4480     CALL 8044
8008:CD5E80     CALL 805E
800B:3E01       LD A, 01
800D:CD4480     CALL 8044
8010:CD5E80     CALL 805E
8013:3E09       LD A, 09
8015:CD4480     CALL 8044
8018:CD5E80     CALL 805E
801B:3E99       LD A, 99
801D:CD4480     CALL 8044
8020:CD5E80     CALL 805E
8023:3E0A       LD A, 0A
8025:CD4480     CALL 8044
8028:CD5E80     CALL 805E
802B:3EA0       LD A, A0
802D:CD4480     CALL 8044
8030:CD5E80     CALL 805E
8033:3EBA       LD A, BA
8035:CD4480     CALL 8044
8038:CD5E80     CALL 805E
803B:3EFF       LD A, FF
803D:CD4480     CALL 8044
8040:CD5E80     CALL 805E
8043:C9         RET
8044:           label print_hex_number
8044:F5         PUSH AF
8045:0F         RRCA
8046:0F         RRCA
8047:0F         RRCA
8048:0F         RRCA
8049:E60F       AND 0F
804B:CD5480     CALL 8054
804E:F1         POP AF
804F:E60F       AND 0F
8051:C35480     JP 8054
8054:           label print_hex_digit
8054:FE0A       CP 0A
8056:3802       JR C, 805A
8058:C607       ADD A, 07
805A:           label print_0_to_9
805A:C630       ADD A, 30
805C:D7         RST 10
805D:C9         RET
805E:           label new_line
805E:3E0D       LD A, 0D
8060:D7         RST 10
8061:C9         RET
8062:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8061

5. Rutina pro tisk tabulky s větším množstvím hexadecimálních hodnot

Předchozí kód neustále opakoval stejnou sekvenci instrukcí:

  1. Načtení hodnoty, která se má vypsat
  2. Zavolání subrutiny pro tisk této hodnoty
  3. Zavolání subrutiny pro odřádkování

Ve chvíli, kdy se tiskne větší množství hodnot, může být výhodnější si nejprve připravit pole se všemi hodnotami. Pole je buď nutné ukončit nějakou předem známou hodnotou (v našem případě nulou), nebo si pamatovat délku pole popř. adresu posledního prvku. My použijeme první způsob, tedy vložení „zarážky“ na konec pole:

numbers:
        db 0x01, 0x09, 0x99, 0xa0, 0xa0, 0xba, 0xff, 0x00

Dále lze vytvořit programovou smyčku, která postupně načítá hodnoty z pole a tiskne je. Pokud se dojde na koncový prvek, je subrutina ukončena instrukcí RET (což je v našem případě podmíněná instrukce testující příznak zero):

        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

Obrázek 5: Opět si zobrazíme sloupeček s několika hexadecimálními hodnotami. Nyní nelze kvůli logice programové smyčky vytisknout nulovou hodnotu, ovšem úprava je jednoduchá a ponechám ji na váženém čtenáři.

Poznámka: jak uvidíme dále, lze smyčku zapsat i jinak, konkrétně takovým způsobem, že se použije počitadlo.

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

Úplný zdrojový kód demonstračního příkladu popsaného v předchozí kapitole je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/76-print-hex-numbers.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, 0x09, 0x99, 0xa0, 0xa0, 0xba, 0xff, 0x00
 
print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
 
        pop  AF             ; obnovit A
        and  $0f            ; zamaskovat horní čtyři bity
        jp print_hex_digit  ; vytisknout hexa číslici a návrat z podprogramu
 
print_hex_digit:
        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
        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

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: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:CD1B80     CALL 801B
800D:CD3580     CALL 8035
8010:C30680     JP 8006
8013:           label numbers
8013:010999A0   DEFB of 8 bytes
8017:A0BAFF00
801B:           label print_hex_number
801B:F5         PUSH AF
801C:0F         RRCA
801D:0F         RRCA
801E:0F         RRCA
801F:0F         RRCA
8020:E60F       AND 0F
8022:CD2B80     CALL 802B
8025:F1         POP AF
8026:E60F       AND 0F
8028:C32B80     JP 802B
802B:           label print_hex_digit
802B:FE0A       CP 0A
802D:3802       JR C, 8031
802F:C607       ADD A, 07
8031:           label print_0_to_9
8031:C630       ADD A, 30
8033:D7         RST 10
8034:C9         RET
8035:           label new_line
8035:3E0D       LD A, 0D
8037:D7         RST 10
8038:C9         RET
8039:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8038

7. Součet osmibitových hodnot uložených ve standardním binárním kódu

V dalším kroku vytvoříme program, který na obrazovku ZX Spectra vytiskne výsledky součtu vždy dvou osmibitových hodnot, které jsou uloženy ve standardním binárním formátu. Jedná se tedy o celočíselné hodnoty v rozsahu 0 až 255. Jak jednotlivé sčítance, tak i výsledek součtu jsou vypsány v hexadecimálním tvaru (proč tomu tak je, uvidíme v kapitole o BCD):

Obrázek 6: V každém sloupci jsou vždy nejprve uvedeny oba sčítance a pod nimi i výsledek součtu.

Na dalším obrázku je ukázáno, jak se mají číst zobrazené hodnoty. V červeném rámečku je vypsána operace 1+2=3 (stejné jak v decimálním tak i hexadecimálním tvaru). V modrém rámečku je vypsána operace 6+9=15, což v hexadecimálním tvaru odpovídá hodnotě 0×0f:

Obrázek 7: Základní aritmetika: 1+2=3 a 6+9=15 (0×0f).

8. Tisk tabulky se sčítanci i součty

Podívejme se nyní na způsob zobrazení „tabulek“ se součty. Nejdříve je nutné deklarovat dvojici polí. Pole array1 obsahuje první sčítance, pole array2 sčítance druhé:

array1:
        db 0x00, 0x01, 0x05, 0x09, 0x09, 0x09, 0x10, 0x10, 0xa0, 0xfe, 0xff
 
array2:
        db 0x01, 0x02, 0x05, 0x01, 0x06, 0x07, 0x09, 0x10, 0x01, 0x01, 0x01

Dále si vytvoříme podprogram, který vytiskne celou řadu hodnot. Počet těchto hodnot je uložen v pracovním registru B a adresa pole s hodnotami v registrovém páru HL. Povšimněte si, že počet hodnot je zvolen takovým způsobem, aby se za poslední hodnotou již netiskla mezera: 11×3=33 (celkem je k dispozici 32 textových sloupců):

; podprogram pro tisk sekvence numerických hodnot v hexadecimálním formátu
; vstupy:
;         HL - ukazatel na pole s hodnotami
;         B - počet hodnot (délka pole)
print_numbers:
next_item:
        ld   A, (HL)           ; načíst hodnotu z pole
        inc  HL                ; přechod na další prvek pole
        call print_hex_number  ; vytisknout hexa hodnotu
        ld   A, B
        cp   1                 ; za poslední hodnotou už nechceme tisknout mezeru
        jr   Z, skip           ; přeskočení tisku mezery u poslední hodnoty
        call space
skip:
        djnz next_item         ; zpracování dalšího prvku pole
        ret

A konečně poslední sekvence instrukcí vypočítá a následně vytiskne součet hodnot, a to bez nutnosti jejich uložení do třetího pole:

        ld   DE, array1        ; adresa pole s první řadou sčítanců
        ld   HL, array2        ; adresa pole se druhou řadou sčítanců
        ld   B, 11
next_add:
        ld   A, (DE)           ; načíst první sčítanec z pole
        add  A, (HL)           ; načíst druhý sčítanec z pole
        inc  DE                ; posun ukazatele na další sčítanec
        inc  HL                ; dtto
        call print_hex_number  ; vytisknout hexa hodnotu výsledku
        call space
        djnz next_add          ; kontrola počtu zobrazených výsledků
        ret

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 popsaného v předchozích dvou kapitolách je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/77-add-hex-numbers.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, array1        ; vytištění 11 hodnot z pole array1 v hexadecimálním formátu
        ld   B, 11
        call print_numbers
 
        ld   HL, array2        ; vytištění 11 hodnot z pole array2 v hexadecimálním formátu
        ld   B, 11
        call print_numbers
 
        call new_line          ; odřádkování
        call new_line          ; (chyba v ROM subrutině nebo užitečná vlastnost?)
 
        ld   DE, array1        ; adresa pole s první řadou sčítanců
        ld   HL, array2        ; adresa pole se druhou řadou sčítanců
        ld   B, 11
next_add:
        ld   A, (DE)           ; načíst první sčítanec z pole
        add  A, (HL)           ; načíst druhý sčítanec z pole
        inc  DE                ; posun ukazatele na další sčítanec
        inc  HL                ; dtto
        call print_hex_number  ; vytisknout hexa hodnotu výsledku
        call space
        djnz next_add          ; kontrola počtu zobrazených výsledků
        ret
 
; podprogram pro tisk sekvence numerických hodnot v hexadecimálním formátu
; vstupy:
;         HL - ukazatel na pole s hodnotami
;         B - počet hodnot (délka pole)
print_numbers:
next_item:
        ld   A, (HL)           ; načíst hodnotu z pole
        inc  HL                ; přechod na další prvek pole
        call print_hex_number  ; vytisknout hexa hodnotu
        ld   A, B
        cp   1                 ; za poslední hodnotou už nechceme tisknout mezeru
        jr   Z, skip           ; přeskočení tisku mezery u poslední hodnoty
        call space
skip:
        djnz next_item         ; zpracování dalšího prvku pole
        ret
 
array1:
        db 0x00, 0x01, 0x05, 0x09, 0x09, 0x09, 0x10, 0x10, 0xa0, 0xfe, 0xff
 
array2:
        db 0x01, 0x02, 0x05, 0x01, 0x06, 0x07, 0x09, 0x10, 0x01, 0x01, 0x01
 
 
print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
 
        pop  AF             ; obnovit A
        and  $0f            ; zamaskovat horní čtyři bity
        jp print_hex_digit  ; vytisknout hexa číslici a návrat z podprogramu
 
print_hex_digit:
        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
        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
 
space:
        ld   A, 32          ; kód mezery
        rst  0x10           ; zavolání rutiny v ROM
        ret                 ; návrat ze subrutiny
 
 
end ENTRY_POINT

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

ENTRY_POINT     EQU 8000
ROM_CLS         EQU 0DAF
OUT_NUM_1       EQU 1A1B
                ORG 8000
8000:           label start
8000:CDAF0D     CALL 0DAF
8003:213E80     LD HL, 803E
8006:060B       LD B, 0B
8008:CD2E80     CALL 802E
800B:214980     LD HL, 8049
800E:060B       LD B, 0B
8010:CD2E80     CALL 802E
8013:CD6E80     CALL 806E
8016:CD6E80     CALL 806E
8019:113E80     LD DE, 803E
801C:214980     LD HL, 8049
801F:060B       LD B, 0B
8021:           label next_add
8021:1A         LD A, (DE)
8022:86         ADD A, (HL)
8023:13         INC DE
8024:23         INC HL
8025:CD5480     CALL 8054
8028:CD7280     CALL 8072
802B:10F4       DJNZ 8021
802D:C9         RET
802E:           label print_numbers
802E:           label next_item
802E:7E         LD A, (HL)
802F:23         INC HL
8030:CD5480     CALL 8054
8033:78         LD A, B
8034:FE01       CP 01
8036:2803       JR Z, 803B
8038:CD7280     CALL 8072
803B:           label skip
803B:10F1       DJNZ 802E
803D:C9         RET
803E:           label array1
803E:00010509   DEFB of 11 bytes
8042:09091010
8046:A0FEFF
8049:           label array2
8049:01020501   DEFB of 11 bytes
804D:06070910
8051:010101
8054:           label print_hex_number
8054:F5         PUSH AF
8055:0F         RRCA
8056:0F         RRCA
8057:0F         RRCA
8058:0F         RRCA
8059:E60F       AND 0F
805B:CD6480     CALL 8064
805E:F1         POP AF
805F:E60F       AND 0F
8061:C36480     JP 8064
8064:           label print_hex_digit
8064:FE0A       CP 0A
8066:3802       JR C, 806A
8068:C607       ADD A, 07
806A:           label print_0_to_9
806A:C6e0       ADD A, 30
806C:D7         RST 10
806D:C9         RET
806E:           label new_line
806E:3E0D       LD A, 0D
8070:D7         RST 10
8071:C9         RET
8072:           label space
8072:3E20       LD A, 20
8074:D7         RST 10
8075:C9         RET
8076:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8075

10. Uložení numerických hodnot ve formátu BCD a Packed BCD

Ve druhé části dnešního článku se budeme zabývat uložením numerických hodnot ve formátu BCD; konkrétně nás bude zajímat varianta nazývaná Packed BCD. Zkratkou BCD neboli Binary-coded decimal se označuje skupina kódování numerických hodnot založená na jednoduchém principu – každá desítková číslice je uložena ve čtyřech bitech. Tyto čtyři bity jsou buď uloženy v samostatném bajtu (co bajt, to jediná číslice) nebo jsou vždy „dvojice čtveřic“ bitů uloženy do jediného bajtu. Druhý zmíněný formát se nazývá Packed BCD a v naprosté většině případů se jedná o variantu big endian (a to nezávisle na procesorové architektuře).

V případě, že je nutné pracovat například s hodnotami v rozsahu 0 až 99999, je nutné pro takové BCD hodnoty alokovat pro každou číslici čtyři bity a celkem je tedy minimálně nutné použít 5×4=20 bitů. Naproti tomu v případě použití binárního kódování je zapotřebí pouze sedmnácti bitů, protože 217=131072 > 99999.

BCD se v nejjednodušším případě používá pro uložení celých čísel, a to buď bez znaménka (jednotlivé cifry, jak již bylo naznačeno výše) nebo se znaménkem (potom je znaménko typicky uloženo ve vlastním bitu, nebo se navíc používá dvojkový doplněk, to ovšem méně často). Ovšem můžeme se setkat i s takzvaným FX formátem (fixed point), v němž je desetinná tečka umístěna za předem známou cifrou a je neměnná – počet cifer před desetinnou tečkou a za ní je stále stejný. To znamená, že se v takovém případě nemusí ukládat exponent, který u FP hodnot přímo či nepřímo určoval pozici řádové čárky v uloženém číslu.

Poznámka: i když by se mohlo zdát, že je BCD dnes již překonaný, není tomu tak, protože se používá ve specifických oblastech, v nichž záleží například na každém haléři (centu) atd.

11. Podpora pro výpočty v BCD v instrukčních sadách mikroprocesorů

Výpočty s číselnými hodnotami uloženými v kódu BCD se ve výpočetní technice používají již od první generace (elektronkových) mainframů, které společnost IBM vyráběla v padesátých letech minulého století. Dnes se s BCD setkáme například při programování mikrořadičů, protože některé sedmisegmentové LCD, čipy s hodinami reálného času či malé numerické klávesnice kód BCD používají. Ostatně i z tohoto důvodu výpočty v BCD podporuje většina mikrořadičů, s výjimkou některých řad osmibitových mikrořadičů PIC. Čipy podporující výpočty v BCD můžeme rozdělit do tří kategorií:

  1. Registry přímo obsahují BCD (typicky dvě číslice 00–99) a ALU taktéž produkuje výsledek v BCD. Typickým příkladem je slavný osmibitový mikroprocesor MOS 6502, který lze přepnout do „dekadického režimu“ instrukcí SED (Set Decimal Mode) či se přepnout zpátky instrukcí CLD (Clear Decimal Mode).
  2. ALU sice provádí výpočty v binárním kódu, ale po provedení výpočtu je možné explicitně provést úpravu výsledku na BCD (na základě nastavených příznaků přenosu a polovičního přenosu). Typickým příkladem jsou čipy Intel 8048, Intel 8051, Motorola 6809, Motorola 68HC11 atd. atd. s instrukcí DAA či DA A používanou po operaci součtu a rozdílu. Podobná je instrukce A6AAC u čtyřbitových mikrořadičů TMS-1000. S tímto konceptem se blíže seznámíme v dalších kapitolách.
  3. ALU provádí většinu operací v binárním režimu, ovšem některé instrukce ji mohou přepnout do režimu BCD. Příkladem je Motorola 68000 s instrukcemi ABCD (Add Binary Coded Decimal), NBCD (Negate Binary Coded Decimal) a SBCD (Subtract Binary Coded Decimal).

Obrázek 8: Jedna z typických nasazení čtyřbitových mikrořadičů – nejjednodušší kalkulačky .

Některé mikroprocesory obsahují větší množství instrukcí pro úpravu výsledku a/nebo vstupních operandů. Příkladem může být čip 8086 (s instrukční sadou udržovanou dodnes), podporující jak „packed BCD“ (dvě cifry v bajtu), tak i „unpacked BCD“ (jediná cifra v bajtu). V instrukční sadě nalezneme hned šest relevantních instrukcí:

Operace Packed BCD Unpacked BCD
součet DAA AAA
rozdíl DAS AAS
součin × AAM
podíl × AAD

BCD používá i matematický koprocesor x86 (konkrétně čip 8087 a kvůli zpětné kompatibilitě i dnešní CPU+FPU), který dokáže do osmdesáti bitového slova uložit 18 BCD cifer + znaménko (necelé dva bajty se přitom nevyužijí).

Poznámka: S kódem BCD poměrně úzce souvisí i kódování znaků EBCDIC neboli Extended Binary Coded Decimal Interchange Code. Firma IBM toto kódování vytvořila v letech 1963–1964 v návaznosti na šestibitový kód využívaný u děrných štítků a použila ho u počítačů System/360. Vzhledem k velké popularitě těchto počítačů se kódování EBCDIC i přes jeho mnohé nevýhody (především v porovnání s kódováním ASCII) rozšířilo i na další počítače a jejich operační systémy, například OS/390, z/OS, OS/400 i některé počítačové systémy firem HP či Unisys.

Obrázek 9: Segmentem, v němž se BCD používá dodnes, jsou kalkulačky.

12. Příznak half carry a instrukce DAA

V souvislosti s formátem Packed BCD je nutné, aby mikroprocesor při provádění aritmetických operací dokázal výsledky upravit takovým způsobem, aby výsledkem byla opět hodnota s formátem Packed BCD. V praxi to v tom nejjednodušším případě znamená, že k výsledné cifře je nutné při přetečení přes 4 bity připočíst hodnotu 6. A vzhledem k tomu, že v bajtu jsou uloženy dvě BCD cifry, znamená to:

  • Je nutné detekovat přetečení/přenos z bitů 0–3 do bitu 4 (dolní BCD cifra)
  • Je nutné detekovat přetečení/přenos z bitu 7 (horní BCD cifra)

Výsledná hodnota se tedy bude měnit následovně:

  • +0×00 pokud nedošlo k přetečení
  • +0×06 pokud došlo k prvnímu typu přetečení
  • +0×60 pokud došlo ke druhém typu přetečení
  • +0×66 pokud došlo k obou přetečením
Poznámka: ve skutečnosti je situace složitější u odečítání, kdy je nutné provést úpravu přidáním doplňku šestky nebo 66 – viz též další kapitolu.

O výše uvedené úpravy výsledku se stará instrukce DAA (Decimal Add Adjust). V mnoha ohledech se jedná o nejsložitější instrukci mikroprocesoru Zilog Z80, neboť její chování závisí na mnoha vstupních hodnotách.

13. Vliv příznaků a hodnoty v akumulátoru na operaci DAA

V následující tabulce je ukázáno, jaká hodnota se přičte k výsledku poslední „sčítací“ operace typu ADD, ADC a INC na základě příznaků C a H a taktéž na základě hodnoty uložené v akumulátoru. Povšimněte si, že se skutečně provádí přičtení konstant 00, 6, 60 nebo 66:

C před DAA Horní cifra H před DAA Dolní cifra Přičítaná hodnota C po DAA
0 0–9 0 0–9 00 0
0 0–8 0 A-F 06 0
0 0–9 1 0–3 06 0
0 A-F 0 0–9 60 1
0 9-F 0 A-F 66 1
0 A-F 1 0–3 66 1
1 0–2 0 0–9 60 1
1 0–2 0 A-F 66 1
1 0–3 1 0–3 66 1
Poznámka: úprava hodnoty tedy může nastat i tehdy, pokud je H rovno nule, ovšem jedna z cifer má hodnotu A-F (což není BCD).

Druhá tabulka platí pro „odečítací“ operace typu SUB, SBC, DEC a kupodivu taktéž NEG:

C před DAA Horní cifra H před DAA Dolní cifra Přičítaná hodnota C po DAA
0 0–9 0 0–9 00 0
0 0–8 1 6-F FA 0
1 7-F 0 0–9 A0 1
1 6-F 1 6-F 9A 1
Poznámka: vzpomenete si, jak Z80 pozná, kterou tabulku použít?

14. Realizace součtu dvou numerických hodnot uložených ve formátu Packed BCD

Součet dvou numerických hodnot reprezentovaných ve formátu Packed BCD je triviální – pouze musíme za instrukci součtu doplnit instrukci DAA, která výsledek součtu opraví tak, aby se opět jednalo o BCD číslice:

ld   A, (DE)           ; načíst první sčítanec z pole
add  A, (HL)           ; načíst druhý sčítanec z pole
daa                    ; provést decimální korekci

Tuto trojici instrukcí použijeme v dnešním posledním demonstračním příkladu.

15. Tisk tabulky se sčítanci i součty

Předchozí demonstrační příklad, který vytisknul tabulku s výsledky součtů binárně uložených hodnot můžeme triviálním způsobem upravit tak, aby se výpočty prováděly s BCD hodnotami, přičemž výsledkem bude pochopitelně opět BCD hodnota. Nejprve si necháme opět vytisknout hodnoty sčítanců (vždy dva sčítance použité v jedné operaci pod sebou):

start:
        call ROM_CLS           ; smazání obrazovky a otevření kanálu číslo 2 (screen)
 
        ld   HL, array1        ; vytištění 11 hodnot z pole array1 v hexadecimálním formátu
        ld   B, 11
        call print_numbers
 
        ld   HL, array2        ; vytištění 11 hodnot z pole array2 v hexadecimálním formátu
        ld   B, 11
        call print_numbers

Vložíme prázdný řádek (ovšem popravdě řečeno mi není jasné, proč se příslušná subrutina musí volat dvakrát, pokud je kurzor za koncem řádku):

        call new_line          ; odřádkování
        call new_line          ; (chyba v ROM subrutině nebo užitečná vlastnost?)

A dále vytiskneme výsledek součtu prováděného s BCD hodnotami. Povšimněte si, že po operaci ADD je vložena instrukce DAA, která výsledek opraví do korektního BCD tvaru (pokud je to ovšem možné):

        ld   DE, array1        ; adresa pole s první řadou sčítanců
        ld   HL, array2        ; adresa pole se druhou řadou sčítanců
        ld   B, 11
next_add:
        ld   A, (DE)           ; načíst první sčítanec z pole
        add  A, (HL)           ; načíst druhý sčítanec z pole
        daa                    ; provést decimální korekci
        inc  DE                ; posun ukazatele na další sčítanec
        inc  HL                ; dtto
        call print_hex_number  ; vytisknout hexa hodnotu výsledku
        call space
        djnz next_add          ; kontrola počtu zobrazených výsledků
        ret

A takto by měla vypadat obrazovka po dokončení běhu programu:

Obrázek 10: Tabulka se součty dvojic hodnot uložených ve formátu Packed BCD.

Výsledky můžeme rozdělit do tří kategorií:

  1. Korektní výsledek pro korektní vstup (modrý obdélník)
  2. Nekorektní výsledek pro nekorektní vstup (červený obdélník)
  3. Přetečení výsledku pro hodnotu přesahující 99

Obrázek 11: Modře jsou vybarveny výpočty se skutečnými hodnotami Packed BCD, červeně pak výsledky, které nejsou korektní kvůli odlišnému vstupu.

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

Úplný zdrojový kód demonstračního příkladu popsaného v předchozích dvou kapitolách je dostupný na adrese https://github.com/tisnik/8bit-fame/blob/master/Speccy-asm/78-add-bcd-numbers.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, array1        ; vytištění 11 hodnot z pole array1 v hexadecimálním formátu
        ld   B, 11
        call print_numbers
 
        ld   HL, array2        ; vytištění 11 hodnot z pole array2 v hexadecimálním formátu
        ld   B, 11
        call print_numbers
 
        call new_line          ; odřádkování
        call new_line          ; (chyba v ROM subrutině nebo užitečná vlastnost?)
 
        ld   DE, array1        ; adresa pole s první řadou sčítanců
        ld   HL, array2        ; adresa pole se druhou řadou sčítanců
        ld   B, 11
next_add:
        ld   A, (DE)           ; načíst první sčítanec z pole
        add  A, (HL)           ; načíst druhý sčítanec z pole
        daa                    ; provést decimální korekci
        inc  DE                ; posun ukazatele na další sčítanec
        inc  HL                ; dtto
        call print_hex_number  ; vytisknout hexa hodnotu výsledku
        call space
        djnz next_add          ; kontrola počtu zobrazených výsledků
        ret
 
; podprogram pro tisk sekvence numerických hodnot v hexadecimálním formátu
; vstupy:
;         HL - ukazatel na pole s hodnotami
;         B - počet hodnot (délka pole)
print_numbers:
next_item:
        ld   A, (HL)           ; načíst hodnotu z pole
        inc  HL                ; přechod na další prvek pole
        call print_hex_number  ; vytisknout hexa hodnotu
        ld   A, B
        cp   1                 ; za poslední hodnotou už nechceme tisknout mezeru
        jr   Z, skip           ; přeskočení tisku mezery u poslední hodnoty
        call space
skip:
        djnz next_item         ; zpracování dalšího prvku pole
        ret
 
array1:
        db 0x00, 0x01, 0x05, 0x09, 0x09, 0x09, 0x10, 0x10, 0xa0, 0xfe, 0xff
 
array2:
        db 0x01, 0x02, 0x05, 0x01, 0x06, 0x07, 0x09, 0x10, 0x01, 0x01, 0x01
 
 
print_hex_number:
        push AF             ; uschovat A pro pozdější využití
        rrca                ; rotace o čtyři bity doprava
        rrca
        rrca
        rrca
        and  $0f            ; zamaskovat horní čtyři bity
        call print_hex_digit; vytisknout hexa číslici
 
        pop  AF             ; obnovit A
        and  $0f            ; zamaskovat horní čtyři bity
        jp print_hex_digit  ; vytisknout hexa číslici a návrat z podprogramu
 
print_hex_digit:
        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
        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
 
space:
        ld   A, 32          ; kód mezery
        rst  0x10           ; zavolání rutiny v ROM
        ret                 ; návrat ze subrutiny
 
 
end ENTRY_POINT

Pro úplnost si ukažme způsob překladu tohoto demonstračního příkladu 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:213F80     LD HL, 803F
8006:060B       LD B, 0B
8008:CD2F80     CALL 802F
800B:214A80     LD HL, 804A
800E:060B       LD B, 0B
8010:CD2F80     CALL 802F
8013:CD6F80     CALL 806F
8016:CD6F80     CALL 806F
8019:113F80     LD DE, 803F
801C:214A80     LD HL, 804A
801F:060B       LD B, 0B
8021:           label next_add
8021:1A         LD A, (DE)
8022:86         ADD A, (HL)
8023:27         DAA
8024:13         INC DE
8025:23         INC HL
8026:CD5580     CALL 8055
8029:CD7380     CALL 8073
802C:10F3       DJNZ 8021
802E:C9         RET
802F:           label print_numbers
802F:           label next_item
802F:7E         LD A, (HL)
8030:23         INC HL
8031:CD5580     CALL 8055
8034:78         LD A, B
8035:FE01       CP 01
8037:2803       JR Z, 803C
8039:CD7380     CALL 8073
803C:           label skip
803C:10F1       DJNZ 802F
803E:C9         RET
803F:           label array1
803F:00010509   DEFB of 11 bytes
8043:09091010
8047:A0FEFF
804A:           label array2
804A:01020501   DEFB of 11 bytes
804E:06070910
8052:010101
8055:           label print_hex_number
8055:F5         PUSH AF
8056:0F         RRCA
8057:0F         RRCA
8058:0F         RRCA
8059:0F         RRCA
805A:E60F       AND 0F
805C:CD6580     CALL 8065
805F:F1         POP AF
8060:E60F       AND 0F
8062:C36580     JP 8065
8065:           label print_hex_digit
8065:FE0A       CP 0A
8067:3802       JR C, 806B
8069:C607       ADD A, 07
806B:           label print_0_to_9
806B:C630       ADD A, 30
806D:D7         RST 10
806E:C9         RET
806F:           label new_line
806F:3E0D       LD A, 0D
8071:D7         RST 10
8072:C9         RET
8073:           label space
8073:3E20       LD A, 20
8075:D7         RST 10
8076:C9         RET
8077:           END 8000
Emiting TAP basic loader
Emiting TAP from 8000 to 8076

17. Obsah následujícího článku

V navazující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).

bitcoin školení listopad 24

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 osmi článcích [1] [2], [3], [4], [5], [6], [7], [8], 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
 
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

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 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

Autor článku

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