ワンボードマイコンをつくろう!(パソコンの原点はここから始まった)
TK80ソフトコンパチブル!8080、Z80マシン語からBASICまでこれ1台でこなせます
当記事は2009年11月から「TTLでCPUをつくろう!」というタイトルの もとにほとんど毎日連載をしてきたものを再編集したものです。 2011.6.30
前へ
次へ
目次へ戻る
ホームページトップへ戻る
☆ND80ZVでBASICを。
とんでもない機能追加を思いついてしまいました。
ND80ZVでBASICを実行できるようにしようというのです。
それも浮動小数点演算ができて、三角関数や対数計算までできる本物のBASICです!

[第49回]

●いよいよデバッグ開始です

前回は、ND80ZVにとりあえず仮実装したZ80BASICで、簡単なテストプログラムがうまく動作するのを確認した後で、システムプログラムの実装アドレスを組替えて整理したところ、なんとせっかく動いていたテストプログラムが全く動かなくなってしまいました、というところまでを書きました。

幸いベースになっているZ80マシン語モニタプログラムはうまく動いているようですので、その機能を使ってデバッグを進めていくことができます。
Z80マシン語モニタプログラムとはND80ZVのROMに入っております、ND80Zモニタプログラムのことではありません。
もちろんTK80モニタプログラムのことでもありません。

●ND80ZVのモニタプログラム

ND80ZVのモニタROMにはもともと3種類のモニタプログラムが入っています。



アドレス0000にあるTK80モニタプログラムは、NECのTK80に搭載されておりましたモニタプログラムと一部を除いて同じものです。
ND80ZVでTK80と同じ動作をさせるときに使います。

その上のアドレス0400にもTK80モニタプログラムがあります。
このモニタプログラムはアドレスが0000からのTK80モニタプログラムを0400からスタートするように平行移動させるとともに、RAMワークエリアをFFXX番地に移動しています。

もともとのTK80モニタプログラムはワークエリアが83XXにあります。
TK80の時代にはRAMは非常に高価でしたので、TK80にはRAMをわずかしかのせてありませんでした。
その当時のTK80のメモリマップでは83xxというアドレスはRAMの終わりだったのですが、ND80ZVではRAMは8000〜FFFFの32KBもあります。

この場合は83xxはRAMアドレスの始めに近い部分になりますから、そこにシステムのワークエリアが置かれていると、RAMの途中がシステムに占有されてしまうことになって都合が悪いことになります。
そこでアドレス0400〜に置いたモニタプログラムは0000〜のTK80モニタプログラムと全く同じ動作をするのですが、システムワークエリアは83xxではなくて、RAMの一番後ろのFFxxにしてあります。

その後ろの0800〜にあるND80Zモニタプログラムは、TK80モニタプログラムを発展させた当社オリジナルのモニタプログラムです。
TK80モニタプログラムは8080CPUで動くことを前提にしたプログラムですが(もちろんZ80CPUでも問題なく動作します)、ND80Zモニタプログラムは最初からZ80CPUのためのプログラムです。
でもせっかく同じROMの中にTK80モニタプログラムがあるのですから、キー入力ルーチンとか7セグメントLED表示ルーチンやSAVE、LOADルーチンは、0400に置かれたTK80モニタ(2)のルーチンをCALLしています。

●BASICシステムにもマシン語モニタが入っています

今回デバッグをしております、Z80BASICシステムプログラムは、ND80ZV試作基板に増設しました増設RAMの1000〜にロードしています。
主たる機能はもちろんBASICですけれど、ここにもマシン語モニタの機能が含まれているのです。
たとえば前回少し説明をしましたdm(ダンプメモリ)の機能もそのひとつです。
これはND80Zモニタをリモートプログラムで操作したときの/dと同じ機能です。

なんだか重複していてムダなようですけれど、そのようにして重複してでも置くだけの価値があるのです。
ND80Zモニタの機能はリモートプログラムから操作しても、基本的にND80ZVの16進キーボードからリモートでデータを入力する、という操作から離れることはできません。
もちろんND80ZV単独でキー操作するのに比べれば、リモートプログラムでDOS/Vパソコンに接続して、そこから操作したほうが、より利便性があります。

ですけれど、せっかくDOS/Vパソコンと接続して、DOS/Vのキーボードやディスプレイ表示を使うことを前提に考えた場合には、できるならば、もっとより使い易いものにしたいなあ、ということになります。
そこでND80Zモニタプログラムと機能的に重複するものであっても、より使い易いマシン語モニタの機能ということで、Z80BASICシステムにも搭載しているのです。

Z80BASICシステムプログラムには、そのほかにも便利なマシン語コマンドがたくさん用意されています。
そのうちのいくつかの機能は、これからデバッグを進めていく間に使うことになると思います。

●デバッグの続きです

さてそこで、前回のデバッグの続きです。
簡単なプログラムですが、BASICシステムプログラムを仮アドレスに配置してテストしたときにはちゃんと動作してくれたものが、BASICシステムプログラムを正規のアドレスにアレンジして配置した後にもう一度同じテストプログラムを実行しましたら、どういうわけか、うんともすんとも言ってくれません。
前に正しく動作したときにはちゃんと計算結果が画面に表示されたのですが、今回は画面には何も表示されないでハングアップしてしまいます。

そこで何が起きているのかを確認するために、前回は整数型変数A%、B%、C%のアドレスをdmコマンドで表示させてみたのです。

ついでですが、このZ80BASICでは変数名としてA%やB%しか使えないかといいますと、そんなことはありません。
メモリサイズの制約から変数名としての長さの制限や、予約語は使えないなどの制約はありますが、たとえばABC%とかあるいは、RINGOでもMIKANでもTOKYOでもOSAKAでも変数名として使うことができます。
それらの変数はプログラムに書かれた順に、BASICの変数格納エリアに格納されていきますから、機械語の感覚で、その変数がメモリ上で実際に配置されているアドレスを知ることは、不可能ではありませんがちょっと面倒な作業を必要とします。

しかしマシン語のサブルーチンをBASICプログラムからCALLして使うときなどに、BASICとマシン語のサブルーチンとの間で値を受け渡ししたいということが出てきます。
そのような場合を想定して、A%〜Z%の26個の整数型変数はメモリアドレスの固定エリアに最初から置かれているのです。
そのアドレスが、前回説明しましたようにA%がF440、F441で、B%がF442、F443、そしてC%がF444、F445なのです。

ハングアップしたあとで、一旦強制終了してから、もう一度Z80BASICを起動させて、dmコマンドでそのA%〜C%の値を確認してみたところ、ちゃんと計算通りの結果になっていることが確認されましたから、少なくとも行番号10から30までは正しく実行されたはず、というつもりだったのですが。

それは早とちりというもので、そうはいきませんでしょう。
A%〜C%の値は、その前に正しく実行されたときに、その値になったはずで、ですからその値を確認してみたところで、今回はのっけからハングアップしてしまって全く実行されなかった、という可能性を除外することはできません。
そういうことに気がつきましたので、そこのところをわかるようにして再度試してみることにいたしました。

岡目八目で、はたからみておりますと、もどかしいと言いますか、ばかなことをやっているなあとお思いでしょうけれど、そこはそれ、Z80BASICシステムプログラムの使用例を紹介するという側面もありますので、ナマのままの、実況中継であります。


今度は、やはりZ80BASICシステムプログラムのマシン語モニタに含まれておりますcmコマンドを使って、A%〜C%の領域を0クリアしました。

●CMコマンド

cmコマンドはチェンジメモリ(Change Memory)コマンドです。
アドレスを指定すると、そのアドレスを表示して、その後ろにそのアドレスのメモリ内容を表示してキー入力待ちになります。
ここを書き換えたいときは、16進数2桁をキーから入力すると、その入力した値が書き込まれて、次のアドレスとその内容が表示されます。
リモートプログラムの[WR+]とよく似ていますが、[WR+]と違って、値を入力後に[Enter]を入力する必要はありません。
16進数2桁を入力するだけで、その値が書き込まれて次のアドレスに進みます。

入力ミスに気が付いて前のアドレスに戻りたいときは[←]キーを押します。
また書き換える必要がなくてそのアドレスをスキップするときは[→]キーか[Space]キーを押します。
cmを終了するときは[Enter]キーを押すか、[Ctrl]と[B]を同時に押します。

cmコマンドでA%〜C%の領域(F440〜F445)が0クリアされたことを見ていただくために、もう一度dmコマンドでその範囲を表示しています。
そのあとで、runコマンドでもう一度テストプログラムを実行させました。
上の画面はそこでハングアップしてしまったところです。

このあとまた強制終了して、再度Z80BASICシステムを起動してから、A%〜C%の値がどうなっているか確認したのですけれど、うっかりしてしまったらしく、そのところの画面コピーがみあたりません。

ええ。そういうときに、ログファイルがお役に立ちますですよ。

logfile nd80zlog\06230602.txt open

ND80ZVに接続しました
send[read+]
0001 0000 - zzentry
1000 00C3 - send goto zentry
>dm f440,f44f
F440  0B 00 C8 01 43 02 FF EF FB 3B B5 7E 75 A2 3E 3A  ..ネ.C....;オ~u「>:
>bp 2397
>run

>bp d
2397
>cm 2397
2397 FF-
>bp 0
>cm 2397
2397 3E-

確かに、F440〜F445は正しい値に書き換わっています。
これでテストプログラムの行番号10〜30が正しく実行されたことは確認できました。

●ブレイクポイント

その次のbp 2397は何をやっているのでしょう?
その下のrunコマンドの入力からは画面コピーが残っておりましたので、そちらで説明をいたします。



画面の左側(上部)はBASICシステムプログラムのアセンブルリストです。
行番号10〜30が正しく実行されていることが確認できましたので、ひょっとすると、その次のPRINT文でハングアップしているのかも、と思って、今度はPRINT文の動作を確認しようとしているところです。

いよいよマシン語プログラムのデバッグです。
ここからは通常はお見せすることのない、舞台裏をちょいと公開することになります。
BASICのPRINT命令はアドレス2397にあります。

で、さきほどのbp 2397です。
bpはブレイクポイントを設定するコマンドです。
TK80モニタプログラムにもブレイクポイントの機能がありました。
しかしその機能は、AUTO/STEPスイッチをSTEP側にしたうえで、プログラムを実行させ、1命令ごとに割り込みを発生させてシステムルーチンに戻り、そこでブレイクアドレスとの比較を行いながら、ブレイクアドレスまでを実行していくという機能でした。
1命令ごとに割り込み処理を行いますから、ブレイクするまでに通常の実行時間の数十倍の時間がかかってしまいます。

このZ80BASICのマシン語モニタプログラムのbp機能はAUTO/STEPスイッチは使いません。
AUTOのままで実行します。
それじゃどうしてブレイクできるのか?
ちょいと不思議に思われるかもしれません。
それにつきましてはのちほど種明かしをいたします。

さてアドレスの2397にブレイクポイントを設定したうえで、runコマンドを実行しますと。
あれ、ブレイクしないで、しかもハングアップもしないで終了して、>が表示されてしまいました。

さては、bpの設定を間違えてしまったか?
それにしてもなぜ、今度はハングアップしなかったのか?
いきなり頭のなかには疑問が渦巻きます。
脳みそが低酸素状態になってしまいます。

bp d
を入力しています。
bpにdパラメータをつけて入力すると、ブレイクポイントが設定されているアドレスを表示します。
むむ。間違いなく、2397にブレイクポイントが設定されています。
ということは、ブレイクポイントが実行されなかったことを意味します。
ブレイクポイントでブレイクすると、その時点で設定されていたブレイクアドレスはクリアされて、ブレイクポイントが設定されていない状態にクリアされることになっているからです。
むむ。なぜブレイクしなかったのか?
次にcmコマンドでアドレス2397の値を確認しています。
FFになっています。

これがZ80BASICシステムのマシン語モニタのブレイク機能の種明かしです。
何回も説明しておりますように、マシン語コードのFFはRST7命令です。
8080やZ80がこのコードを実行すると、アドレス0038番地からのプログラムを実行します(ジャンプではなくてコールします)。
0038はROMのアドレスですが、ND80ZVのモニタROMの0038番地には、RAMアドレスへのジャンプ命令が書いてありますから、そのジャンプ先のアドレスを書き換えることで、任意の処理プログラムを実行させることができます。

TK80モニタプログラムではそのRAMアドレスにステップ処理ルーチンへのジャンプ命令が書いてあって、AUTO/STEPスイッチをSTEP側にしてユーザープログラムを実行させることで、1命令ごとに強制的に割り込みを発生させて、ステップ処理プログラムを実行させるようになっています。

一方、Z80BASICシステムのマシン語モニタのブレイク処理も動作としては、TK80と似ていますが、ハードウェアの割り込みを使わないで、「割り込み」処理ルーチンを起動させているところが大きな違いなのです。

そんな手品のようなことができるのか?
そこが種明かしです。
多くの手品と同じく種明かしをすればなあんだそんなことか、ということになります。
種明かしをすれば簡単なことで、bpを設定したいアドレスの命令をFFコードで書き換えておくだけなのです。
この場合2バイト以上の長さの命令に対しては、その第1バイトのみをFFで置き換えます。第2バイト以下のアドレスを指定すると正しく実行されず、場合によってはプログラムが暴走することにもなります。
このときFFで置き換える前の命令コードはシステムのワークエリアに保存しておいて、ブレイクしたあとにもとのアドレスに戻します。

そのように準備をしておいて、プログラムを実行します。
TK80モニタのようにステップスイッチをONにしているわけではありませんから、プログラムは普通の速度で実行されます。
そしてFFコードのところまでくると、そこで0038からのブレイク処理ルーチンが実行されるのです。
なんだか、爆弾ゲームみたいですねえ。
普通に実行していて、え、爆弾ではない、FFコードにあたってしまうと、ドカーン、ではなくて、ブレイクします。

ブレイクしたときには、FFに置き換えられていた、もともとそのアドレスに置かれていた命令はまだ実行されていません。
つまり、bpで指定したアドレスまで来ると、もともとそこに置かれている命令を実行する直前にブレイクすることになります。
この機能を利用するためには、プログラムがRAM上にある必要があります(理由は、おわかりですよね)。

今回Z80BASICシステムプログラムをデバッグするのに、手間をかけてRAMを増設した理由のひとつはここにあったのです。

うむむ。
うまく考えたものだ、と思いませんか。
あ。もちろん、私が考え出したテクニックではありません。
いつのころからか、普通に使われている、周知のテクニックです。

さて、上の画面では、そのあと、
bp 0
を実行しています。
0(ゼロ)パラメータをつけたbpコマンドを実行すると、今設定されているブレイクポイントが解除されます。
FFが置かれていたところに、元通りの命令コードが戻されます。

その結果を皆様に見ていただくために、cmコマンドでもう一度アドレス2397の値を確認しています。
左のアセンブルリストの通り、FFで書き換えられていたのが、もとの3Eコードに戻っています。

その後、
dm 8000,80d5
を実行しています。
ユーザーのBASICプログラムは8004から後ろに置かれます。
ブレイクもされずに、またハングアップもしないで、テストプログラムが終了してしまった(ように見えた)ので、さては何かのアクシデントでテストプログラムが蒸発してしまったか?と思って確認してみたのです。

こんなの見ても全くわからないよお。

ええ。
ですけれど私にはわかります。

でもでも、蒸発してしまっておりましたのはテストプログラムではなくて私の脳みそのほうでありました。
なんともおばか、なことをやっておりました。
CPUをつくろう!第534回(2010.6.25upload)を再編集

ワンボードマイコンをつくろう![第49回]
2011.6.30upload

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