標準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
前へ
次へ
ホームページトップへ戻る