7
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Linuxでx86アセンブラ(BCD編)

この記事について

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つのことをします.

  1. alの最下位4bitが9Hより大きい、またはAFがセットされていたら、alに6をたし、ahに1を足します.この際、CFとAFがセットされます.

  2. 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つのことを行います.

  1. alの最下位4ビットが9より大きい、またはAFがセットされているとき、alに6を足しAFをセットします.
  2. 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つのことをします.

  1. alの最下位4ビットが9より大きいまたはAFがセットしている場合、alから6、ahから1を引いてCFとAFをセットします.
  2. alの上位4ビットをクリアします.

上の例では最終的にalが34hになります.

Packed BCDの引き算の整形にはdas を使います.

mov al, 71h
sub al, 43h
das

das は2つのことを行います.

  1. alの最下位4ビットが9より大きい、またはAFがセットされていたらalから6を引いてAFをセットします.
  2. 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 が使える二進数の形に変換します.演算の前に呼び出す必要があります.

デモコード

今回出てきたニーモニックの使い方を説明するデモコードです.

BCD.asm
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は失われたテクノロジーになっていくでしょう.

参考文献

↓↓:bowtie:過去の投稿もよろしくね:bowtie:↓↓

↓↓:bowtie:コメントをいただけたら励みになります:bowtie:↓↓

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
7
Help us understand the problem. What are the problem?