標準TTLだけ(!)でCPUをつくろう!(組立てキットです!)
(ホントは74HC、CMOSなんだけど…)
[第536回]
●デバッグ作業の続きです
前回は、PRINT命令の動作をデバッグするために、まずアドレス2397にブレイクポイントをセットして、テストプログラムを実行したところ、ちゃんとブレイクしました、というところまで書きました。
アドレス2397はPRINT命令のエントリポイントです。
前回はBASICプログラムの構造についても簡単に説明をいたしました。
テストプログラムはアドレス8004から80D5にあります。
最初のPRINT文が書かれている、行番号40は、8068から始まります。
>dm 8000,80d5 8000 41 55 54 4F 00 80 FF DF 10 80 0C 00 B3 53 44 94 AUTO...゚....ウSD. 8010 40 F4 41 25 00 00 00 85 0A 00 02 00 42 F4 42 25 @.A%........B.B% 8020 00 00 00 85 14 00 02 00 44 F4 43 25 00 00 00 85 ........D.C%.... 8030 1E 00 02 00 F0 DF 41 00 00 00 00 85 50 00 04 00 .....゚A.....P... 8040 0A 00 08 F2 0C 00 9A FA 7B 00 0D 0B 14 00 08 F2 ........{....... 8050 18 00 9A FA C8 01 0D 0B 1E 00 0C F2 24 00 9A F2 ....ネ.......$... 8060 0C 00 2B F2 18 00 0D 0F 28 00 1F 81 22 61 25 3D ..+.....(..."a%= 8070 22 3B F2 0C 00 2C 22 62 25 3D 22 3B F2 18 00 2C ";...,"b%=";..., 8080 22 63 25 3D 22 3B F2 24 00 0D 22 32 00 0D 84 F2 "c%=";.$.."2....
前回、PRINT命令は中間コードの81に置き換えられる、という説明をしました。
コード81はアドレス806Bにあります。
そこまでを確認していただいたところで、アドレス2397でブレイクしたときの、下の画面を見てください。
DEレジスタの値が806Cになっています。
DEレジスタにはユーザープログラムのアドレスが保持されています。
ユーザープログラムアドレス806Bのコード81まで実行が進んで、そこで、PRINT命令に分岐してBASICプログラムアドレスの2397でブレイクしたのですが、そのときDEレジスタはコード81の次のアドレス806Cになっています。
コード81はすでに「読み終わった」ところなので、ユーザープログラムのポインタの役目をしているDEレジスタはその次のアドレスに進んでいるのです。
説明の中で2種類の「アドレス」が出てきます。
ブレイクポインタを設定しながらBASICシステムプログラムの動作を追いかけるときの「アドレス」は、CPUが実行する命令アドレスのことでPC(プログラムカウンタ)に入る値です。
下の画面では左側に表示されているZ80BASICプログラムのアドレスです。
もうひとつの「アドレス」はDEレジスタに入れられて、Z80BASICによって解読されながら実行されていくユーザープログラムのアドレスです。
こちらも「プログラム」と表現しますが、Z80BASICから見ると、「データ」に過ぎません。
PRINT命令の動作をデバッグする作業は、Z80BASICの実行をトレースしながら、ユーザープログラムが解読され実行されていくところを確認する作業です。
ということで、ここから先はJP命令などの分岐点にブレイクポイントをセットして、少しずつ実行させながら、PRINT命令の動作をチェックしていきます。
2397でPRINTルーチンにエントリした後の最初の分岐は23A1のJP NZ命令です。
下の画面で、
bp 23a1
でブレイクポイントをセットしたあと、
rtコマンドを入力しています。
rtコマンドはブレイクしたアドレスからデバッグ中のプログラムに戻って続きを実行するコマンドです。
rtはreturnを意味しています。
すると23a1でブレイクしました。
この直前のCP命令でAレジスタの値が23であるかどうかを比較しています。
Aレジスタにはさらにその前のCALL PRNTEによって、次のユーザープログラムのコード(DEレジスタに入っているアドレス806Cのデータ)22が読み込まれています。
コード22はPRINT文の文字列データの開始を示す、”(ダブルクォーテーション)です。
いよいよここから、文字列データの検出と表示の動作に入ります。
ブレイクポイントを設定しないでテストプログラムを実行させたときは、画面に何も表示しないでハングアップしてしまったわけですから、これからあとの動作を追及していけば、その原因がわかるはずです。
アドレス23a1はJP NZ命令ですが、ここでブレイクしたときは、まだJP NZ命令は実行されていません。
実行される直前にブレイクしています。
そこで右のフラグの詳細を見てみますと、Zフラグは立っていません。
Aレジスタの値は22で、それを23と比較したのですから、当然不一致です。
CP命令では一致したときはZフラグが立ちますが、不一致のときはZフラグはオフになります。
Not Zeroですから、ここでrtコマンドを実行することによって、ブレイクして中断されていたBASICプログラムに戻って、JP NZ,PRINT0が実行されると、その結果は、PRINT0にジャンプすることになります。
PRINT0はアドレス23A8です。
ここにジャンプすることは間違いありませんから、すると、その次の分岐はアドレス23ADのJR NZ,PRN33です。
そこで、
bp 23ad
と入力したあと、
rt
を実行します。
ここでもAレジスタの値と5Cとを比較しています。
不一致ですから、次はPRN33にジャンプすることになります。
すると、その次の分岐はアドレス23C2のJP C,PRNT4ですから、ここにブレイクポイントを設定して、またリターンします。
bp 23c2
rt
です。
すると、今度はレジスタの値が変化しました。
DEレジスタが8071になっています。
もう一度ユーザープログラムのダンプリストを見てみましょう。
8060 0C 00 2B F2 18 00 0D 0F 28 00 1F 81 22 61 25 3D ..+.....(..."a%= 8070 22 3B F2 0C 00 2C 22 62 25 3D 22 3B F2 18 00 2C ";...,"b%=";...,
さきほどまでのDEレジスタの値は806Cでした。
PRINT文の最初の文字列データの始まりを示す ” の位置にありました。
今回のDEレジスタの値は8071です。
文字列データの終わりの ” の次のアドレスです。
ということは、文字列データの”a%=”のところを読み取ったわけです。
HLレジスタを見てみますと、806Dになっています。
文字列の最初の文字 a のあるアドレスです。
そしてCレジスタには文字列 a%= の文字数を示す03が入っています。
実はこの直前に実行されたCALL LETS2は、文字列の処理ルーチンなのです。
LETS2の実行によって、文字列が検出されなかったときはCフラグがセットされます。
今回は文字列が検出されましたから、Cフラグはセットされません。
アドレス23C2のJP C,PRNT4はパスされますから、その次のアドレスに進みます。
すると次の分岐はアドレス23CBのJP NZ,PRN34ですから、今度はここにブレイクポイントをセットしてリターンします。
bp 23cb
rt
です。
あれえ?
a
が表示されたあと、ブレイクしました。
ADISPはAレジスタの値を文字コードとみなして、それを画面に表示するルーチンです。
プログラムを見ますと、HLレジスタで示すメモリの値をAレジスタに入れたあとでADISPをCALLしています。
そのあとHLを+1して、Cレジスタを−1しています。
Cレジスタが0になるまで、その動作が繰り返し実行されます。
ブレイクポイントを設定しないで普通に実行させたときは、何も表示しないでハングアップしてしまったのに、ステップ動作をさせて追及してみましたら、画面にポロリ、と文字 a が表示されてしまいました。
プログラムのデバッグをしていますと、こういう信じられない現象に遭遇して悩まされることがよくあります。
さてさてこれはいったいどうしたことでしょう。
むむ。さてはまた、たぬきかあるいはきつねのいたずらか?
2010.6.28upload
前へ
次へ
ホームページトップへ戻る