Ľudmila Jánošíková

Programovanie v jazyku symbolických adries

pre 32-bitové procesory Intel
Obsah Index
BCD aritmetika

V tejto kapitole budeme hovoriť o inštrukciách, ktoré vykonávajú operácie s číslami uloženými v kóde BCD (Binary Coded Decimal). Táto forma reprezentácie čísiel slúži na spracovanie čísiel v desiatkovej sústave. S kódom BCD sa vyhneme starostiam s prevodom desiatkových čísiel na binárne pri vstupných dátach a nemusíme prevádzať binárne čísla na desiatkové pri výpise výsledkov z programu. BCD aritmetika je dôležitá aj z dôvodu zachovania presnosti čísla na všetkých významových miestach (čo je potrebné napr. v účtovníctve), ktorá by sa mohla porušiť zaokrúhľovaním zlomkovej časti čísla pri prevodoch medzi číselnými sústavami.

V kóde BCD je každá číslica desiatkového čísla zobrazená v 4 bitoch ako dvojkové číslo. Rozoznávame zhustený a nezhustený formát BCD. V nezhustenom formáte je každá desiatková číslica uložená v jednom bajte (horné 4 bity sú nulové). V zhustenom formáte sú v jednom bajte uložené dve číslice. Ostatné detaily zobrazenia desiatkového čísla sa ponechávajú na programátora, hoci existujú určité konvencie, napr. 01h pred prvou číslicou znamená, že BCD číslo so znamienkom je kladné, zatiaľ čo 8h (80h v nezhustenom formáte) reprezentuje záporné číslo. Okrem toho býva zvykom vyhradiť pred zápisom čísla 1 bajt, ktorý obsahuje počet číslic BCD čísla. Napr. číslo -125 v zhustenom formáte BCD má tvar

0000 0011
1000 0001
0010 0101
3 číslice
záp.
číslo
1
2 5

Nezhustený BCD formát čísla 19 by mohol byť

00000010
00000001
00000001
00001001
2 číslice
kladné číslo
1
9

Kvôli aritmetickým operáciám je tiež vhodné dodržať konvenciu uloženia čísiel v pamäti v obrátenom poradí slabík, t.j. v nezhustenom BCD formáte je najmenej významná číslica na najnižšej adrese a ďalšie číslice potom postupne na vyšších adresách. V zhustenom kóde BCD je najmenej významná dvojica číslic na najnižšej adrese, atď.

Nevýhodou zobrazenia čísiel v BCD kóde je, že BCD reprezentácia je menej kompaktná ako binárna forma. Ďalšou nevýhodou je, že pri aritmetických operáciách s BCD číslami musíme vykonávať dodatočné úpravy, aby výsledok operácie bol správnym BCD číslom.

Pri sčítaní dvoch čísiel v kóde BCD nastávajú problémy vtedy, keď dôjde k prenosu do vyššieho rádu desiatkového čísla. Pozrime sa, ako dopadne súčet 19+24 v zhustenom formáte BCD:

19
0001 1001
24
0010 0100
3?
0011 1101

Problém je, že 1101 nepredstavuje v BCD kóde žiadnu číslicu. V 4 bitoch môžeme zakódovať 16 hodnôt, ale existuje len 10 desiatkových číslic. Ale ani v nasledujúcom príklade nedostaneme správny výsledok, hoci formát BCD zostane zachovaný:

19
0001 1001
28
0010 1000
41
0100 0001

V obidvoch prípadoch sa problém vyrieši, keď k dolným 4 bitom výsledku pripočí­tame 6:

3?
0011 1101
06
0000 0110
43
0100 0011

resp.

41
0100 0001
06
0000 0110
47
0100 0111

Podobne treba urobiť aj korekciu v horných 4 bitoch pre druhú číslicu. Ako už naznačili uvedené príklady, korekciu robíme len v prípade, že došlo k pretečeniu zo štvorice bitov predstavujúcej desiatkovú číslicu, alebo je hodnota týchto bitov väčšia ako 9. Všetky uvedené testy a úpravy nemusíme robiť ručne, ale môžeme použiť inštrukciu

daa (decimal adjust for addition)

OF SF ZF AF PF CF
? * * * * *

Inštrukcia daa vykonáva korekciu výsledku sčítania dvoch čísiel v zhustenom formáte BCD v registri AL podľa nasledujúcich pravidiel:

  • ak v dolných 4 bitoch registra AL je číslo > 9 alebo AF = 1, pripočíta sa 6 k obsahu registra AL a AF := 1, inak AF := 0;
  • ak v horných 4 bitoch registra AL je číslo > 9 alebo CF = 1, pripočíta sa 60h k obsahu registra AL a CF := 1, inak CF := 0.

V nasledujúcom príklade si ukážeme, ako by prebiehal súčet dvoch viacmiestnych BCD čísiel v zhustenom formáte. Pre jednoduchosť predpokladajme, že obidve čísla majú rovnaký počet číslic.

Úloha

Sčítajte dve BCD čísla v zhustenom formáte uložené v dátovom segmente. Offset prvého čísla je v registri ESI, offset druhého čísla v EDI. V registri ECX je počet bajtov, ktoré čísla zaberajú. Čísla sú uložené v obrátenom poradí slabík. Výsledok uložte na miesto druhého sčítanca, teda od offsetu EDI.

Riešenie

Čísla budeme sčítavať po bajtoch. Ku každému bajtu pripočítame prípadný prenos z predchádzajúceho rádu (použijeme inštrukciu adc) a po sčítaní vykonáme korekciu výsledku pomocou daa. Pretože na začiatku sčítania je prenos nulový, vynulujeme na začiatku programovej sekvencie príznakový bit CF.

       clc ; CF := 0
Sucet: mov al,[edi]
       adc al,[esi]
       daa
       mov [edi],al
       inc edi
       inc esi
       loop Sucet
; ulož posledný prenos
       mov al,0
       adc al,0
       mov [edi],al

Korekciu výsledku sčítania dvoch BCD čísiel v nezhustenom formáte v registri AL vykonáva inštrukcia

aaa (ASCII adjust for addition)

OF SF ZF AF PF CF
? ? ? * ? *

Ak obsah registra AL > 9 alebo AF = 1, pripočíta sa 0F6h k registru AX a príznaky AF a CF sa nastavia na 1. V opačnom prípade sa príznaky AF a CF vynulujú.

Úloha

Sčítajte dve dvojmiestne čísla BCD v nezhustenom formáte uložené v registroch AX a BX a výsledok uložte do registra DX.

Riešenie

Súčet vypočítame v dvoch krokoch: najprv sčítame nižšie slabiky (AL a BL), pričom prenos sa pripočíta k obsahu registra AH. V druhom kroku sčítame vyššie slabiky (AH a BH). Pred korekciou výsledku vynulujeme register AH a prípadný prenos z vyššieho rádu potom zostane v AH.

        add al,bl
        aaa
        mov dl,al
        mov al,ah
        add al,bh
        mov ah,0
        aaa
        mov dh,al

Korekciu výsledku odčítania dvoch BCD čísiel v zhustenom formáte v registri AL vykonáva inštrukcia

das (decimal adjust for subtraction)

OF SF ZF AF PF CF
? * * * * *

podľa nasledujúcich pravidiel:

  • ak v dolných 4 bitoch registra AL je číslo > 9 alebo AF = 1, odčíta sa 6 od obsahu registra AL a AF := 1, inak AF := 0;
  • ak v horných 4 bitoch registra AL je číslo > 9 alebo CF = 1, odčíta sa 60h od obsahu registra AL a CF := 1, inak CF := 0.

Korekciu výsledku odčítania dvoch BCD čísiel v zhustenom formáte v registri AL vykonáva inštrukcia

aas (ASCII adjust for subtraction)

OF SF ZF AF PF CF
? ? ? * ? *

Ak obsah registra AL > 9 alebo AF = 1, odčíta sa 0F6h od registra AX a príznaky AF a CF sa nastavia na 1. V opačnom prípade sa príznaky AF a CF vynulujú.

Operácie násobenia a delenia možno vykonávať len s BCD číslami v nezhustenom formáte.

Inštrukcia

aam (ASCII adjust for multiplication)

OF SF ZF AF PF CF
? * * ? * ?

vykonáva korekciu výsledku násobenia dvoch BCD čísiel v nezhustenom formáte v registri AX. Číslo v registri AX upraví tak, aby významnejšia číslica výsledku bola v registri AH a menej významná číslica v AL.

V nasledujúcom príklade ukážeme, ako možno násobiť viacmiestne BCD čísla.

Úloha

Vynásobte BCD číslo uložené v obrátenom poradí slabík od adresy ESI jednomiestnym BCD číslom v registri DL. Obidve čísla sú v nezhustenom formáte. Počet číslic prvého čísla je v ECX. Výsledok uložte od adresy EDI.

Riešenie

Spomeňte si, ako násobíte jedným číslom ručne bez kalkulačky: vynásobíte jednu číslicu prvého čísla, pripočítate prenos z predchádzajúceho rádu, menej významnú číslicu zapíšete do výsledku a významnejšiu si zapamätáte ako prenos do ďalšieho rádu.

V nasledujúcej ukážke programu si prenos do ďalšieho rádu zapamätáme v nasledujúcom bajte výsledku. Pretože na začiatku násobenia je prenos nulový, vynulujeme na začiatku programu prvý bajt výsledku. Po vynásobení jednej číslice prvého čísla obsahom registra DL vykonáme korekciu výsledku násobenia pomocou aam. Pripočítame prenos z predchádzajúceho rádu a urobíme korekciu po sčítaní. Až teraz je výsledok násobenia danej číslice (v registri AX) korektný. Obsah registra AL predstavuje číslicu výsledku, obsah AH prenos do vyššieho rádu.

        mov byte ptr [edi],0
Dalsia_cislica:
        mov al,[esi]
        mul dl
        aam
        add al,[edi]; pripočítaj prenos z predchádzajúceho rádu
        aaa
        mov [edi],al; odlož číslicu výsledku
        inc edi
        mov [edi],ah; odlož prenos
        inc esi
        loop Dalsia_cislica

Operácia delenia v kóde BCD si vyžaduje úpravu pred delením. Inštrukcia

aad (ASCII adjust for division)

OF SF ZF AF PF CF
? * * ? * ?

prevedie dve BCD číslice v nezhustenom formáte v registri AX na dvojkové číslo (vynásobí obsah AH desiatimi a pripočíta AL). Nasledujúca inštrukcia div potom nechá výsledok delenia v nezhustenom BCD formáte v registri AL a zvyšok po delení v nezhustenom BCD formáte v registri AH.

Úloha

Vydeľte BCD číslo uložené v obrátenom poradí slabík od adresy ESI jednomiestnym BCD číslom v registri BL. Obidve čísla sú v nezhustenom formáte. Počet číslic prvého čísla je v ECX. Výsledok uložte od adresy EDI.

Riešenie

Spomeňte si, ako delíte viacmiestne čísla na papieri: zvyšok po delení v danom ráde sa prenáša do nasledujúceho rádu – stáva sa významnejšou číslicou delenca v nasledujúcom ráde. Pretože inštrukcia div ponechá zvyšok po delení v registri AH, stačí do AL skopírovať ďalšiu číslicu prvého čísla a máme v AX delenec v ďalšom ráde. Ten treba upraviť pomocou aad a môžeme deliť. Ešte si treba uvedomiť, že delenie začína od najvyššieho rádu, preto na začiatku programu musíme registre ESI a EDI upraviť tak, aby ukazovali na najvyšší bajt delenca, resp. výsledku.

        add esi,ecx
        dec esi
        add edi,ecx
        dec edi
        mov ah,0
Dalsia_cislica:
        mov al,[esi]
        aad
        div bl
        mov [edi],al ; odlož číslicu výsledku
        dec edi
        dec esi
        loop Dalsia_cislica; zvyšok po delení zostane v AH

Hore

Načo je vám jazyk symbolických adries?

Architektúra moderných procesorov

Registre

Spôsoby adresovania

Premenné a návestia

Symbolické konštanty

Inštrukčný súbor

Inštrukčný súbor

Segmentové direktívy

Moduly

Služby operačného systému MS-DOS

Služby operačného systému Windows 95/98/NT/XP

Systémová úroveň vstupu a výstupu

Prostriedky pre prípravu programu

Reťazcové inštrukcie | Reálna aritmetika
Vydala Žilinská univerzita v Žiline, 2000. ISBN 80-7100-723-4.
Otázky a pripomienky môžete poslať autorke.
Naposledy upravené 19.10.2015.