BCD変換ルーチン
BCDとは"Binary
Coded
Decimal"のことで、日本語では"2進化10進数"という意味に相当します。これは、10進法で記述した場合の各桁の数を、桁ごとに4bitの2進表記で記述したものです。例えば、10進法で表記した「1985」という数は、BCD表記では、「0001 1001 1000 0101」と書けます。
BCD変換は、2進のデータを扱い、かつ10進で数字などを表示する場合には必ず必要になります。PICマイコンを使用した電子工作にも、しばしば登場します。BCD変換の方法としては、
多少複雑なロジック回路を用いて、ハード的・静的(入力が決まると、直ちに出力も決まる)に変換を行う。
ソフトウェアを用い、テーブルを参照して変換する。
ソフトウェアで動くBCD形式のカウンタで、変換元の数を数え上げていく。
などの方法があります。
の方法は最も高速で、桁数が少ない場合や、巨大なPGAなどを使用していてリソースに余裕がある場合には有効です。ただし、桁数が増えると、物凄い複雑なものになります。これはCADの力に頼らなければ作れません。
の方法は、数ステップで処理でき、原理が単純で、関数で対応させることが困難なデータ変換を行うこともできます。ただし、桁数が増えるとテーブルが巨大なものになります。
の方法は、処理に時間がかかり、処理ルーチンがやや複雑ですが、低機能なマイコンにも実装できます。また、桁数が増えても処理ルーチンの複雑さはほとんど変わりません。
以下ではの方法について説明します。
この方法では、ソフトウェアで動くBCD形式のカウンタで、変換元の数を数え上げていくことでBCD変換を行います。これは、2進カウンタの袋から10進カウンタの袋へ、ひとつずつ玉を移動するような作業です。
いま例として、B<SUB>n B<SUB>n-1・・・ B<SUB>2 B<SUB>1 B<SUB>0と表記される2進数があるとすると、この2進数は、
B<SUB>n×2<SUP>n + B<SUB>n-1×2<SUP>n-1 + B<SUB>n-2×2<SUP>n-2
+ ・・・ + B<SUB>2×2<SUP>2 + B<SUB>1×2<SUP>1
+ B<SUB>0 という「量」です。
この式は、 ((...( B<SUB>n×2 + B<SUB>n-1) ×2 + B<SUB>n-2)
×2 + ・・・ + B<SUB>3) ×2 + B<SUB>2) ×2 + B<SUB>1) ×2 +
B<SUB>0 という風に変形できます。
この式は 「2倍して次の桁を加算・・・」という処理の繰り返しそのものです。これならマイコンの基本命令のみで処理できます。
10進カウンタの最下位に2進カウンタの最上位の数を転送 → 10進カウンタの数を2倍 というような処理を繰り返します。
2倍するという処理は、レジスタのデータを1bit左シフトすることで実現します。詳しくは後述しますが、レジスタをうまく並べれば、1bit左シフト命令のみで、「2倍して次の桁を加算」という処理を一度に行うことができます。
さて、BCD形式のレジスタについても、1bit左シフトすれば2倍する効果が得られますが、2倍することで10以上の数になるようならば、繰り上がりの処理を行わなければなりません。この「10以上なら、ひとつ上の桁に繰り上がりを発生する」ような処理を、10進補正処理と呼びます。
困ったことに、10進補正処理にかけるデータというのは、ひとつ前のループ回の「2を乗じて次の桁を加算」という処理で生成された数ですから、0〜19までの範囲を取り得ます。これは、4bitのレジスタでは表現しきれません。10進補正処理を行うためには、合計5bitのレジスタを読み、合計5bitのレジスタを操作する必要があります。この方法では、複数のレジスタやキャリーにまたがった演算が多く発生してしまい、処理が複雑になってしまいます。そこで、「2倍→次の桁を加算→10進補正」という順番を少し変え、「10進補正→2倍→次の桁を加算」という順番にし、「2倍したときに繰り上がりが生じる予定の数」に、「2倍したときに10進補正の効果が得られるような処理」を施すことにします。
具体的には、
0
=0000 → 0000(そのまま), 2倍すると0000 0000(BCD表記で0) になる。
1 =0001
→ 0001(そのまま), 2倍すると0000 0010(BCD表記で2) になる。
2 =0010
→ 0010(そのまま), 2倍すると0000 0100(BCD表記で4) になる。
3 =0011
→ 0011(そのまま), 2倍すると0000 0110(BCD表記で6) になる。
4 =0100
→ 0100(そのまま), 2倍すると0000 1000(BCD表記で8) になる。
5 =0101
→ 1000(3を加算), 2倍すると 0001 0000(BCD表記で10) になる。
6 =0110
→ 1001(3を加算), 2倍すると 0001 0010(BCD表記で12) になる。
7 =0111
→ 1010(3を加算), 2倍すると 0001 0100(BCD表記で14) になる。
8 =1000
→ 1011(3を加算), 2倍すると 0001 0110(BCD表記で16)になる。
9 =1001
→ 1100(3を加算), 2倍すると 0001 1000(BCD表記で16)になる。
このように、「BCD部分の値が5以上ならば3を加算する」という処理を行うことで、2倍した後に10進補正の効果が得られます。
こうすれば、合計4bitのレジスタを読み、合計4bitのレジスタを操作するだけで済むので、処理が少し楽になり、さらには1bit左シフト操作で、繰り上がりのビットをそのままひとつ上の桁の最下位ビットに入れることが可能になります。
さて、もっと具体的な手順を見てみましょう。
1.レジスタ確保
とても具体的な話になってしまいますが、まずはレジスタ(メモリ)を確保します。
変換前の2進数が入るレジスタと、BCD変換された10進数が入るレジスタを確保します。
いま例として、B<SUB>8</SUB>B<SUB>7</SUB>B<SUB>6</SUB>B<SUB>5</SUB>B<SUB>4</SUB>B<SUB>3</SUB>B<SUB>2</SUB>B<SUB>1</SUB>B<SUB>0
と表記される8桁の2進数があるとします。
この2進数は、最高で 2<SUP>8-1 = 255 の大きさですので、10進に変換したときは最高で3桁の数になります。
BCD表記では、10進ひと桁あたり4bitを要しますので3桁の10進数を表すためには合計12bitのレジスタが必要です。
つまり、このようになります。
BCD部分 | 2進部分 | ||||||||||||||||||
100の桁 | 10の桁 | 1の桁 | |||||||||||||||||
8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
D<SUB>2 | D<SUB>2 | D<SUB>2 | D<SUB>2 | D<SUB>1 | D<SUB>1 | D<SUB>1 | D<SUB>1 | D<SUB>1 | D<SUB>1 | D<SUB>1 | D<SUB>1 | B<SUB>7 | B<SUB>6 | B<SUB>5 | B<SUB>4 | B<SUB>3 | B<SUB>2 | B<SUB>1 | B<SUB>0 |
プロセッサによって一度に扱えるレジスタの長さは8bitだったり16bitだったりしますが、上のように連続して並んでいると考えると、とても理解しやすくなります。
2.変換開始
確保したレジスタのBCD部分はあらかじめゼロクリアしておき、2進部分には変換したい2進数をコピーします。
つまり、このようになります。
BCD部分 | 2進部分 | ||||||||||||||||||
100の桁 | 10の桁 | 1の桁 | |||||||||||||||||
8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | B<SUB>7 | B<SUB>6 | B<SUB>5 | B<SUB>4 | B<SUB>3 | B<SUB>2 | B<SUB>1 | B<SUB>0 |
3.10進補正処理
10進補正処理を行います。これは、BCD部分の各桁について、「値が5以上ならば3を加算する」という処理です。
値が5以上かどうかの判定の為に、まずBCD部分に3を加算します。この後に4bit目(8)が1になっているようならば、5以上の数です。このとき10進補正処理をしなければなりませんが、今の3を加算する作業で10進補正処理も済んでいます。このまま次のステップに進みます。
BCD部分に3を加算しても、4bit目(8)が0である場合は、5未満の数です。3を減算して元に戻し、次のステップに進みます。
これで、2倍した後に10進補正の効果が得られます。
ただし、ループ初期では、まだゼロが多いので何もしていないのと同じです。
4.全bit左シフト
レジスタの全bitを左シフトします。プロセッサによって一度に扱えるレジスタの長さは8bitだったり16bitだったりしますが、この場合は、キャリーごと左シフトする命令を下の桁から順に適用することで処理できます。(PICマイコンの場合はRLF命令など)
つまり、このようになります。
BCD部分 | 2進部分 | ||||||||||||||||||
100の桁 | 10の桁 | 1の桁 | |||||||||||||||||
8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | B<SUB>7 | B<SUB>6 | B<SUB>5 | B<SUB>4 | B<SUB>3 | B<SUB>2 | B<SUB>1 | B<SUB>0 | X |
この作業で、2倍するという処理と、BCD部分の最下位に2進部分の最上位の数を転送するという作業を同時にしてしまったことになります。
2進部分の最下位ビットはX(バツ)です。このビットはこれから先の処理に一切関与しないので、気にする必要はありません。
5.再び10進補正処理 まだゼロが多いので何もしていないのと同じです。
6.再び全bit左シフト
7.再び10進補正処理 まだゼロが多いので何もしていないのと同じです。
8.再び全bit左シフト
現段階ではこのようになっています。
BCD部分 | 2進部分 | ||||||||||||||||||
100の桁 | 10の桁 | 1の桁 | |||||||||||||||||
8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | B<SUB>7 | B<SUB>6 | B<SUB>5 | B<SUB>4 | B<SUB>3 | B<SUB>2 | B<SUB>1 | B<SUB>0 | X | X | X |
9.再び10進補正処理 ようやくBCD部分の1の桁の数が5以上になる可能性がでてきました。
........中略.......
このような処理を合計8回繰り返します。すると
BCD部分 | 2進部分 | ||||||||||||||||||
100の桁 | 10の桁 | 1の桁 | |||||||||||||||||
8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 8 | 4 | 2 | 1 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
D | D | D | D | D | D | D | D | D | D | D | D | X | X | X | X | X | X | X | X |
このように、2進部分が256倍されて、かつ10進形式に変換されて、BCD部分のレジスタに収まることになります。
2進部分の桁数が増えても、繰り返しの回数が増えるだけで、まったく同じような作業で処理できます。
数学も含めた人類の英知全てが技術に生きているのを感じますね(^
^;