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

●DAAのテストプログラムです

少し間があいてしまいましたが、いよいよ最後、12本目のテストプログラムです。
今回はDAAのテストです。
「最後」と書きましたが、実はDAA以外にもテストプログラムとしては、まだテストをしていない命令があります。
IN、OUT、XTHL、RST、EI、DIです。

IN命令とOUT命令は、232C受信プログラムの中で、PICと交信するところでしっかり使っていますし、別にテストするためには、外部に回路が必要ですから、これは今回のテストプログラムからは除外しました。
XTHLはやや特殊な命令ですし、これもTK80モニタプログラムの中で使っていますから、あらためてテストすることもないと思います。
RSTとEI、DIは[第245回]で、割込みのテストとして動作確認をしていますので、これも今回のテストプログラムからは除外、です。

ということで、DAAのテストプログラムが最後のプログラムになりました。
最後だから、というわけでもありませんが、ちょいと気張って、DAAの対象になる全ての加算結果をテストしてみることにしました。

DAA命令については、この連載のはじめのころにかなりしつこく説明をしています。
また[第223回]から数回にわたって回路を中心にした説明をしています。

DAA(Decimal Adjust Accumlator)はその直前に行われた8ビットの加算が十進数の加算であるとみなして(加算は全て2進数として行われます)、結果を十進数の計算結果に補正する命令です。
たとえば2進数(16進数)の計算、56+78=CEを受けて、十進数での計算56+78=134の結果の8ビット34およびキャリーに補正します。

当然DAAの対象になる加算は十進数同士の加算でなければなりません。
つまり99+99〜00+00の範囲の加算ということになります。
今回は、その計算を全てやってしまおう、というわけです。

●DAAのテストプログラムの考え方

DAAのテストプログラムの考え方です。
十進数の加算を1回行うごとにDAAを実行します。そしてその結果が正しいかどうかを、これもプログラムでチェックします。
今回は比較データは作りません(そんなことをしようとしたら、大変なことになります)。
先にZ80互換のCPUボード、ZBKボードで実行して、プログラムが正しく機能することを確認してから、「作るCPU」ボードでも実行することにします。

加算は、まず99+99から開始します。
次は、99+98です。
その次は99+97…というように計算をしていって、最後は99+00の計算をします。
その次は、98+98から開始します。最後は98+00の計算です。
そしてその次は、97+97から開始します。
このように計算をしていって、最後の最後は00+00で終わります。

全部で何通りの計算をすることになるでしょうか?
最初は99+99〜99+00で100通りです。
次は98+98〜98+00で99通りです。
その次は97+97〜97+00で98通りです。
最後は00+00で1通りです。
つまり100+99+98+…+1の答えを求めることになります。

その答えを得るための計算方法は小学校で習いました。
その考え方は、はて、どのように説明してもらったんでしたっけ?
公式だけ教えてもらったように思います。
最初の数と最後の数を足すと101になって、その内側の数も足すと101になって…、なんて風に解法だけ教えてもらったような…。

これ、台形の面積の計算と同じなんですよね。

ううむ。そういえば最近はまた復活したそうですけれど、一時期、小学校で台形の面積を教えなかったとか…。

んなもの、こうやって教えれば、それほど難しいことではないと、思うのですけれどねぇ。


ま、それは置いといて、計算をしてみますと、なんと5050通り!
それだけ全部の場合を計算させてみましょう、というテストプログラムです。

●DAAのテストプログラムのリストです


2009/6/17  16:29  TEST12.TXT
END=418A
              ;;; MYCPU80 TEST12
              ;;; DAA
              ;;; 09/6/17
              ;
                ORG $4100
              ;
                STCK=$5000
                ERCNTR=$5001
                TCNTR=$5002
                REENT=$1033;for ZBK
              ;
4100 210050     LXI H,STCK
4103 F9         SPHL
4104 210000     LXI H,$0000
4107 E5         PUSH H
4108 F1         POP PSW;CLEAR FLAG REGISTER;   POP PSW
              ;
4109 320150     STA ERCNTR
410C 220250     SHLD TCNTR
              ;
410F 0609       MVI B,09
4111 48         MOV C,B
4112 78       LOOP1:MOV A,B
4113 07         RLC
4114 07         RLC
4115 07         RLC
4116 07         RLC
4117 B1         ORA C
4118 67         MOV H,A
4119 50         MOV D,B
411A 59         MOV E,C
411B 7A       LOOP2:MOV A,D
411C 07         RLC
411D 07         RLC
411E 07         RLC
411F 07         RLC
4120 B3         ORA E
4121 6F         MOV L,A
4122 84         ADD H
4123 27         DAA
              ;compare
4124 E5         PUSH H SAVE "A"
4125 F5         PUSH PSW;SAVE DAA DATA & FLAG
4126 79         MOV A,C
4127 83         ADD E
4128 FE0A       CPI 0A
412A DA2F41     JC JP1
412D D60A       SUI 0A
412F F5       JP1:PUSH PSW;SAVE LOW 4BITS
4130 3F         CMC
4131 78         MOV A,B
4132 8A         ADC D
4133 FE0A       CPI 0A
4135 DA3A41     JC JP2
4138 D60A       SUI 0A
413A 3F       JP2:CMC
413B F5         PUSH PSW
413C E1         POP H;SAVE CHECK FLAG to L
413D 07         RLC
413E 07         RLC
413F 07         RLC
4140 07         RLC
4141 67         MOV H,A;HIGH 4BITS
4142 F1         POP PSW;LOW4BITS
4143 B4         ORA H
4144 67         MOV H,A;CHECK DATA to H
4145 F1         POP PSW
4146 F5         PUSH PSW;SAVE FLAG
4147 BC         CMP H
4148 C28441     JNZ ERR
414B F1         POP PSW
414C F5         PUSH PSW;SAVE DATA
414D 7D         MOV A,L
414E DA5941     JC CFON
              ;CFOFF
4151 E601       ANI 01
4153 C28441     JNZ ERR
4156 C35E41     JMP NEXT
4159 E601     CFON:ANI 01
415B CA8441     JZ ERR
415E F1       NEXT:POP PSW
415F E1         POP H
4160 E5       NEXT2:PUSH H
4161 210250     LXI H,TCNTR
4164 34         INR M
4165 C26A41     JNZ NEXT3
4168 23         INX H
4169 34         INR M
416A E1       NEXT3:POP H
416B 1D         DCR E
416C F21B41     JP LOOP2
416F 1E09       MVI E,09
4171 15         DCR D
4172 F21B41     JP LOOP2
4175 0D         DCR C
4176 F21241     JP LOOP1
4179 0E09       MVI C,09
417B 05         DCR B
417C F21241     JP LOOP1
417F 210000     LXI H,$0000
4182 E5         PUSH H
4183 76         HLT
              ;
4184 210150   ERR:LXI H,ERCNTR
4187 34         INR M
4188 C36041     JMP NEXT2
              ;
CFON         =4159  ERCNTR       =5001  ERR          =4184  
JP1          =412F  JP2          =413A  LOOP1        =4112  
LOOP2        =411B  NEXT         =415E  NEXT2        =4160  
NEXT3        =416A  REENT        =1033  STCK         =5000  
TCNTR        =5002  

●DAAの結果をプログラムで確認する

内容が内容だけに、結構難しいプログラムです。
キモはDAAの結果を判断する部分です。
どうやれば、十進補正が正しく行われたかどうかをプログラムで確認することができるでしょうか?

その答えです。
DAAを使う計算とは別に、同じ計算を十進補正を使わないで、十進数で計算をして比べてみればよいのです。
だって、それができないから、DAA命令があるんじゃないのぉ?

いえいえ。DAAなど使わなくても、十進数の加算はちゃんとできるのです。ただちょいと面倒ですけれど。

最初に例として挙げた56+78の計算について説明をしてみます。
DAAを使う通常の加算では、そのままたとえば
MVI A,56
ADI 78
DAA
というように計算しますが、十進数での計算では、まず上位4ビットと下位4ビットを分離してそれぞれレジスタに格納します。
B=05、C=06、D=07、E=08というようにします。
そしてまず下位の加算を行います。
MOV A,C
ADD E
のようにします。
加算の結果をLに入れておきます。
加算の結果が0〜9のときはそのまま上位桁の加算に進みます。
加算の結果が10(0A)以上のときは、10(0A)を引いた残りをLに入れた上で、C(キャリー)フラグをセットします。

上位桁の加算は、
MOV A,B
ADC D
のように、下位桁からのキャリーを含めて加算します。
加算の結果をHに保存します。
下位桁のときと同じように、加算の結果が0〜9のときは次のステップに進みます。
加算の結果が10(0A)以上のときは、10(0A)を引いた残りをHに入れた上で、C(キャリー)フラグをセットします。
このキャリーフラグは、このあとの計算で破壊されないように、スタックに保存しておきます。

次のステップです。
上位4ビットと下位4ビットを別々に計算しましたが、最後にそれを1つの数にまとめます。
MOV A,H
RLC
RLC
RLC
RLC
ORA L
これでAレジスタには十進数での加算結果が入れられたことになります。
この値と、別に2進数で加算後にDAAで十進補正した値とを比較します。
それとともに上位桁へのC(キャリー)フラグの有無も比較します。

比較の結果が不一致ならばエラーカウンタを+1します。
またエラーの有無にかかわらず、1回の計算が済むごとにテストカウンタを+1します。
このテストプログラムではレジスタを全部使ってしまいますので、カウンタに利用できるレジスタは残っていません。
ですからメモリを使います。
エラーカウンタはアドレス5001です。
テストカウンタは5002、5003の2バイトです。

●プログラムの簡単な説明です

最初はBC=0909からスタートします。
DEはBCと同じ値を入れます。
HレジスタにはBCの値を1バイトに圧縮した値(99)を入れます。
LレジスタにはDEの値を1バイトに圧縮した値(99)を入れます。
そしてH+Lを計算してからDAAを実行します。
以上がアドレス4123までのプログラムです。

4124〜4144でC+EとB+Dを別々に計算したあと結果を1バイトに合成してHレジスタにいれています。
Lレジスタにはフラグを保存します。

4145〜415Dで結果の比較を行います。

415E〜4169でカウンタ(TCNTR)を+1します。

416A以下ではD、E、B、Cの値を−1します。
JP命令は、Z80のニーモニックではJMPのことなので、つい勘違いをしてしまいそうになります。
JPはJUMP IF PLUSです。

最初にEレジスタを−1します。
Eレジスタを−1した結果が≧0ならばそのままLOOP2に戻ります。
Eレジスタを−1した結果が負ならばEレジスタには9を入れて、Dレジスタを−1します。

Dレジスタを−1した結果が≧0ならばLOOP2に戻ります。
Dレジスタを−1した結果が負ならば、Cレジスタを−1します。

Cレジスタを−1した結果が≧0ならばそのままLOOP1に戻ります。
Cレジスタを−1した結果が負ならばCレジスタには9を入れて、Bレジスタを−1します。

Bレジスタを−1した結果が≧0ならばLOOP1に戻ります。
Bレジスタを−1した結果が負ならば、全計算終了です。

●テストプログラムの実行結果をUSB経由でパソコンに送りました



先に実行したTEST11の結果も表示されています。
TEST12の結果は、5001〜5003の3バイトの内容です。
5001はエラーカウンタです。ここは00になっていますから、エラーは0です。
5001〜5002は計算を行った回数です。
2バイトの16進数です。
5002が下位バイトで値はBA、5003が上位バイトで、値は13です。
16進数の13BAを十進数に直すと、5050になります。
計算回数は5050回で、エラーは0という結果になりました。

ところで、このちょいとすごいテストプログラムを実行するのにどのくらいの時間がかかったでしょうか?

なんとわずかに約1秒半程度、でした。
なかなかに、大したものじゃありませんか、ねぇ。
2009.7.8upload

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