この記事について
Linuxでx86第9回.今回はBCDです.
多くの高級言語プログラマにとってBCDは馴染がないかかもしれません.アセンブラの機能はCで反映されるていたり名残があったりするのですが、BCDは全く残っていません.intやlong型で事足りることが多いからです.どうしても、CでBCDを使いたい場合はdecNumberなどのライブラリを持ってくる必要があります.
BCDとは?
Binary Coded Decimalの略で、日本語では二進化十進数と呼ばれます.
コンピュータは基本的には数字を二進数に変換して処理します.
しかし、私達が普段扱っている数字は十進数です.この違いにより時々、不都合が生じることがあります.
例えば0.1+0.9を二進数で足し算すると二進数の正解では0.9999999999999999999999999999999999999....となり、極小さな誤差がでます.これを何万回も繰り返したらどうなるでしょうか.はじめは小さな誤差でも無視できない誤差になっていきます.BCDでは二進化が原因で起きる誤差をなくすことができます.会計など極端な正確さを求められる演算で採用されることがあります.
BCDにはUnpacked BCDとPacked BCDのに種類があります.Unpacked BCDは一バイトにひと桁のデータを保存します.
たとえば1234をUnpacked BCDで表すと、
01 02 03 04H
で表されます.
Packed BCDは1ニブル(4bit)にひと桁を保存します.
1234をPacked BCDで表すと、
1234H
で表せます.
BCDの足し算
Unpacked BCDの足し算の例です
xor ah, ah
mov al, '6' ; '6' = 36H
add al, '7' ; '7' = 37H
aaa
or al, 30H
or ah, 30H
mov al, '6'
によりalは36Hにセットされます.次の行のadd al, '7'
によりalは6DHにセットされます.これはまずい答えですね.
BCD的には13を意味する3133Hとなって欲しいところ.
そこでaaa
ニーモニックを使います.
aaa
は次の2つのことをします.
-
alの最下位4bitが9Hより大きい、またはAFがセットされていたら、alに6をたし、ahに1を足します.この際、CFとAFがセットされます.
-
alの上位4ビットは0にセットされます.
つまり、aaa
ニーモニックによりalは03Hになりahは01Hになります.このあとor al, 30H
と、or ah, 30H
によりalとahの上位4ビットは3hにセットされ、alはasciiコードの'3'、ahはaciiコードの'1'になります.
Packed BCDの例を示します.
mov al, 71H
add al, 43H
daa
この計算はPacked BCDの足し算、71+43を実行します.
daa
ニーモニックは、Packed BCDのaaaと同様に足し算によって崩れてしまったBCDを整形します.
daa
は下の2つのことを行います.
- alの最下位4ビットが9より大きい、またはAFがセットされているとき、alに6を足しAFをセットします.
- alの上位4ビットが9より大きい、またはCFがセットされているとき、60HをALに足しCFをセットします.
daa
によりahは01H、alは14Hになります.
BCDの引き算
Unpacked BCDの引き算は足し算の時と同様にsubしたあとで、aas
を用いて整形します.
xor ah, ah
mov al, '3'
sub al, '9'
aas
or al, 30H
aas
は以下の2つのことをします.
- alの最下位4ビットが9より大きいまたはAFがセットしている場合、alから6、ahから1を引いてCFとAFをセットします.
- alの上位4ビットをクリアします.
上の例では最終的にalが34hになります.
Packed BCDの引き算の整形にはdas
を使います.
mov al, 71h
sub al, 43h
das
das
は2つのことを行います.
- alの最下位4ビットが9より大きい、またはAFがセットされていたらalから6を引いてAFをセットします.
- alの上位4ビットが9より大きいまたはCFがセットされている場合は、alから60Hを引いてCFをセットします.
上の例ではalは最終的に28Hとなります.
掛け算・割り算
Unpacked BCDの掛け算の結果の整形にはaam
を使います.
mov al, 3
mov bl, 9
mul bl
aam
or ax, 3030h
Unpacked BCDの割り算の結果の整形にはaad
を使います.
mov ax, 0207h
mov bl, 05h
aad
div bl
aad
はaxレジスタのdiv
が使える二進数の形に変換します.演算の前に呼び出す必要があります.
デモコード
今回出てきたニーモニックの使い方を説明するデモコードです.
section .bss
section .data
section .text
global main
main:
enter 0,0
nop
.bcd_add:
xor ax, ax
mov al, '7'
add al, '8'
AAA
or al, 30H
nop
.bcd_sub:
xor ax, ax
mov al, '7'
sub al, '9'
aas
or al, 30H
nop
.bcd_muldiv:
mov al, '3'
mov bl, '9'
and al, 0FH
and bl, 0FH
mul bl
aam
or al, 30H
nop
mov ax, '45'
and ax, 0F0FH
mov bl, '5'
and bl, 0FH
aad
div bl
or ax, 3030H
nop
.final:
mov eax, 0
leave
ret
64bitはBCDをサポートしていません.32bitのみで使用可能です.
レジスタの本数、長さが大幅に上がったため先述したような問題は無視できるものになってきています.
将来的にはBCDは失われたテクノロジーになっていくでしょう.
参考文献
- NASM development team site:
http://www.nasm.us/ - GDB: The GNU Project Debugger site:
https://www.gnu.org/software/gdb/ - Assembly Language Step-by-Step: Programming with Linux:
http://www.amazon.co.jp/Assembly-Language-Step---Step-Programming/dp/0470497025/ref=sr_1_1?ie=UTF8&qid=1446704607&sr=8-1&keywords=assembly+language+step+by+step - Stack Analysis with GDB:
http://resources.infosecinstitute.com/stack-analysis-with-gdb/
↓↓過去の投稿もよろしくね↓↓
-
Linuxでx86アセンブラ(条件分岐編)
http://qiita.com/MoriokaReimen/items/66b8c0e3b2ef1d2993bb -
Linuxでx86アセンブラ(マクロ編)
http://qiita.com/MoriokaReimen/items/45abbb6c2938abd505ff -
Linuxでx86アセンブラ(スタック編)
http://qiita.com/MoriokaReimen/items/31a8f8ce4a46ba0f3590 -
Linuxでx86アセンブラ(基本的なコードの構造編)
http://qiita.com/MoriokaReimen/items/b320e6cc82c8873a602f -
Linuxでx86アセンブラ(論理演算編)
http://qiita.com/MoriokaReimen/items/bf863585616ad0a0a969 -
Linuxでx86アセンブラ(四則演算編)
http://qiita.com/MoriokaReimen/items/4853587dcb9eb96fab62 -
Linuxでx86アセンブラ(Cとの連携編)
http://qiita.com/MoriokaReimen/items/590a4ddb3de15bfacb4b -
Linuxでx86アセンブラ(道具編)
http://qiita.com/MoriokaReimen/items/b316a68d76c1eafa18f8 -
tmuxの使い方
http://qiita.com/MoriokaReimen/items/d7d5b158cb412dd65a60
↓↓コメントをいただけたら励みになります↓↓