標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第10回]

●宿題の答えです

今ちょっとまたいろいろ作業しなければならないことが出てきて、急に忙しくなってしまったので、ホームページの更新もつい遅れてしまいます。
書かなければならないことがいっぱいあるのですが、以前書きかけで棚上げにしてしまったことがあります。後で書くつもりでそのままにしておくと、忘れてしまいます。
そこで、今日はその宿題を片付けてしまうことにします。

7月11日にアップロードした、8080のレジスタの説明のところ([第3回])で、名探偵コナンよろしく、思わせぶりに自分だけ「そうだったのか!」と合点したままになっていました。
フラグの説明で、Z80にフラグは6個あるが8080には5個しかない。という説明をしました。本日はその説明の続きをいたします。

Z80にあって8080にないフラグとはNフラグです。
参考までにZ80のフラグ(フラグレジスタ)を下に示します。

7 6 5 4 3 2 1 0
S Z x H x P N C

(Xは使用されていない)

ビット1にあるのがNフラグです。8080ではフラグレジスタのビット1は使用されていない(らしい)のです。
ではこのNフラグはいったい何の目的でZ80に追加されたのでしょうか?
じつはNフラグはただ1つの命令、DAA(Decimal Adjust、十進補正)のためだけに用意されているのです。

●十進補正って何だ?

DAAとはどんな命令なのでしょうか?
DAAの働きを説明するためには、まず十進数と16進数の説明をしなければいけません。

私たちの世界で普通に使っているのは十進数です。
1、2、3、と数えていって9までいくと、もうその次の数がありません。そこでもう1つ進むと、9の次は0ということにします。ちょうど時計のように0から9までを円のように配置しておいたとすると9の次は0になることがわかると思います。ただ0にしたのではもとの数より小さくなってしまいます。そこで1まわりした証拠として、ひとつ上の桁に1を足して、10にします。
11、12、13と進んで19までくると、次は同じように、一番下の桁を0にして、上の桁に1を加えます。
20、21、と進んで、99になると、次はまた最下位桁を0にして上の桁に1を加えます。上の桁(十の位)も9なので、ここも0にします。そしてさらに上の桁(百の位)に1を加えて、100にします。
これが十進数(十進法)です。こんな数え方は、まわりくどく説明しなくても、みんな小学校の低学年で学んでしまいます。

しかし私たちは全てが十進法で計算しているわけではありません。さきほど時計の例をあげましたが、時間は十進ではなくて、12進または24進ですし、秒と分は60進数です。よくは知りませんが昔のお金の単位や、度量衡の単位もかなりややこしい計算法になっていたようです。

さてそこでコンピュータの世界の計算法です。
コンピュータの計算法が0と1しか使わない、2進法であることは今ではもうほとんど常識になっているようです。
念のため、0〜9を十進数と2進数で表記すると次のようになります。

0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001

2進数では0と1しかないので、すぐに上の桁に桁上がりするので桁が急激に増えていってしまいます。
ところが、この次の数からは、少し様子が変わってきます。10から20までを同じように示してみます。

10 1010
11 1011
12 1100
13 1101
14 1110
15 1111
16 10000
17 10001
18 10010
19 10011
20 10100

十進数では10から上は桁が増えて2桁になりますが、2進数ではまだしばらくは4桁のままです。16になってはじめて5桁になります。
では、もっと数が大きくなると、2進数の桁はどうなるのでしょうか?
じつは、十進数の255のとき、2進数ではちょうど8桁いっぱいになります。
十進数の255は2進数では11111111になります。
マイコンの世界でよく使う8ビットとは、2進数の8桁のことです。つまり2進数8ビット(8桁)で表現できる最大の数は十進数の255だということになります。

コンピュータの内部では計算は全て1と0だけの2進数でやっているのですが、機械はそれでよくても、私たちからすると、1と0だけでやたら桁数の多い数は実に扱いにくくて困ります。プログラムを書くときなど、当然定数を書かなければいけないときがありますが、そのようなとき、十進の100を2進数で01100100などと表現したり、読んだりするのはたまりません。
そこでこの面倒な2進数を、少しでも人間が理解しやすくために、表現の方法を工夫することが考え出されました。

●16進数

十進数に近づけるため、2進数を4桁ごとに区切って扱うことにして、十進数の0から9に対応する2進数の0000から1001は、十進数と同じく0から9で表し、十進数の10から15に対応する2進数1010から1111には、表現する数がありませんから、アルファベットのAからFをあてはめて表すことにしました。これが16進数です。
もう一度さきほどの表を今度は0〜20まで続けて示します。2進数だけではなく16進数もあわせて示します。

0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 1111 F
16 10000 10
17 10001 11
18 10010 12
19 10011 13
20 10100 14

このように16進数で表記すると、1と0ばかりだった2進数に比べてぐっと十進数に近づいて見やすくなります。
といってもこれはあくまで人間が理解しやすくなるように考え出された表記法で、コンピュータ内部では4桁ごとに区切って、16進で計算しているわけではありません。
コンピュータ内部では2進数で計算されているのですが、しかし2進数を16進数で表記しても十進数で表記しても、計算の結果がかわるわけではありません。10+10=20になるのは、2進数でも16進数でも同じです。

●ここからがやっと本題のDAAのおはなし

さてながながと説明してきましたが、ここからがやっとお話の本筋になります。
その前にまず、本当に2進でも16進でも10+10=20になるかどうか、確認してみることにします。
2進数の足し算のルールは10進数と同じです。ただ1と0しかありませんから、どの桁でも、0+0=0、0+1=1はできますが、1+1=2というわけにはいきませんので、上位桁に繰り上がりして、1+1=10になります。
このルールを頭に入れたうえで2進数の足し算をしてみます。

  1010
  1010(+
 10100

となってちゃんと十進の20と同じになります。16進ではどうやるのか、というと、16進は人間が理解しやすくするためだけの表記法ですから、16進数表記での筆算はやりません(やる気になればやれないことはないですけれど、頭の中が混乱して間違えてしまいます)。
私は上でしたような2進数の筆算を、4桁ごとに区切った形で行います。
たとえば、27+46=73の計算は、

  1 1011    …1B(十進の27)  
 10 1110(+  …2E(十進の46)
100 1001    …49(十進の73)

というようにします。
しかしながら、十進数になれた私たちからみると、2進数を16進数で表現してみても、この計算方法ひとつをとってみても、今一つもどかしい感じがして、なじめません。

●BCD数

実は、16進数に近い表記法でBCD(2進化十進)というものがあります。こちらは16進数と同じように2進数を4桁ごとに区切って表記するのですが、16進数と違い、10から上をAからFで表すのではなく、素直に上の桁を使ってしまいます。
BCD(2進化十進)表記でもう一度さきほどの0から20を示してみます。

0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1 0000 10
11 1 0001 11
12 1 0010 12
13 1 0011 13
14 1 0100 14
15 1 0101 15
16 1 0110 16
17 1 0111 17
18 1 1000 18
19 1 1001 19
20 10 0000 20

これはもう十進数と同じです。こちらの方がうんとわかりやすいですね。
この表記法は8080が生まれるずっと以前からTTLの世界ではごく普通に使われていたものです。代表的な例として、7447(BCD to 7segment decoder)とか7442(BCD to DECIMAL decoder)などがあります。10進カウンタ(7490)も出力がBCDで得られます。
これは単なる想像なのですが、まだ16進数になじみの薄かった当時の人たちのうちに、扱いづらい16進数を嫌い、なんとか十進数と同じBCD数で計算をしたい、と考える人たちが少なからずいたのではないでしょうか。
そういう人たちは、さきほどの27+46=73の計算も

  10 0111    …BCDの27  
 100 0110(+  …BCDの46
 111 0011    …BCDの73

という風にしたい(いや、そうあってほしい、いやいや、ぜひともそうあるべきだ)、と考えたに違いありません。
もちろん、BCD数は本当の2進数ではありませんから、コンピュータで計算した結果は決して上のようにはなりません。
参考までに上の計算をBCD数ではなくて2進数だとして計算した結果を示します(コンピュータは当然BCDなんて知ったことではない、とばかりに普通の2進数として計算をします)。


  10 0111    …16進数の27(十進では39になる) 
 100 0110(+  …16進数の46(十進では70になる)
 110 1101    …16進数の6D(十進では109になる)

という結果になります。

いや、それじゃあ、困る!なんとか、してほしい!

そこで、このようにして普通に計算して得られる6D(110 1101)を、なんとかしてBCD数同士の計算だった場合の73(111 0011)に直してしまおう、というのが、DAA命令なのです。

どうしたら、そんな、器用な、まるで手品のような換算ができるのでしょうか?

まだ、Nフラグの説明まで行きつけませんが、Nフラグについては、しばらく横に置いておくことにして、次回はこの不思議なDAA変換の謎にせまってみたいと思います(何かの番組のナレーションみたいですね)。
2008.7.17upload

前へ
次へ
ホームページトップへ戻る