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

[新連載]復活!TINY BASIC
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
すべてはここからはじまりました。
中日電工も。
40年前を振り返りつつ新連載です。
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜



[第20回]


●コマンドの解読

前回はコマンドテーブルについて説明しました。
コマンドテーブルはコマンド(命令)の文字列とその命令の処理を行なうプログラムの先頭アドレスとがペアになった形で列挙されています。
今回はそのテーブルを使うことで、入力された文字列(またはTEXTエリアの各行の命令文字列)を解読してその命令の処理ルーチンにジャンプするところまでのプログラムについて説明をします。
コマンドテーブルは前回お見せしましたが説明の参考にするために再掲します。





「コマンドテーブル」にはもう少し続きがあるのですが、その続きの部分は上のコマンドテーブルとは少し扱いが異なるテーブルですので、今は割愛します。
コマンドテーブルが終った後ろにあるのがコマンド解読プログラムです。



DIRECT:はLXI H,TAB1−1から始まっていることでもわかりますように、入力バッファの文字列をダイレクトコマンドとみなしてテーブル検索し一致したらそのコマンドの処理ルーチンにジャンプするプログラムです。
HLレジスタにはテーブルの先頭アドレスの1バイト前のアドレスが入れられます。
ダイレクトコマンドテーブルの先頭アドレスは06AEですがLXI H,TAB1−1のマシン語コード部分を見ると21AD06になっています。
DIRECT:はその下のEXEC:に続きますが、EXEC:にはコマンドテーブルをHLレジスタに入れる部分がありません。
EXEC:はTEXTエリアのプログラム行の命令を解読するときに実行されるプログラムです。
処理の内容に従ってテーブルを選択できるように、あらかじめ特定のテーブルのアドレスをHLレジスタに入れた後にEXEC:にジャンプしてきます。

ということでコマンド(命令)を解読するルーチンの本体はこのEXEC:以下のところです。
上で書きましたようにテーブルの1バイト前のアドレスがHLレジスタに入れられて解読が開始されます。
DEレジスタには解読の対象になる入力バッファかTEXTエリア内の文字列の先頭アドレスが入れられています。
RST 5はブランク(スペース、20)をスキップするサブルーチンです([第12回]参照)。
PUSH Dで現在の文字列位置アドレスを一時保存します。
そのあとの073DのEX1:からJZ EX1までのところが対象文字列をテーブルの文字列と照合している部分です。
(DE)と(HL)との照合です。
[注記]以下の説明では(DE)はDEレジスタで示されるメモリアドレスにある値、(HL)はHLレジスタで示されるメモリアドレスにある値の意味で使います。
実際のプログラムの中では(DE)はLDAX Dを実行したあとのAレジスタの中身になります。
また(HL)は8080ニーモニックではMで示されます。

さてそれで、説明の続きです。
(DE)と(HL)を1バイトずつ順に照合します。
一致したらDEもHLも+1して照合を続けます。
途中の073FにCPI 2EHがあります。
(DE)の文字列に’.’(ピリオド)がみつかったらループを抜けてEX3にジャンプします。
EX3:はそこで照合を打ち切って命令の処理ルーチンにジャンプする処理の入口です。
TINY BASICでは省略形が使えます。
ピリオドを使ってたとえば’PRINT’の代わりに’P.’とか’PR.’とかのように書いても’PRINT’と認識されて正しく実行されます。
それを可能にしているのがこのCPI 2EHです。

(DE)と(HL)が不一致の場合には2つのことが考えられます。
その判定をしているのが0749のMVI A,07FHから0752のJNC EX2の部分です。
ここで前回書きましたアドレスの上位バイトの最上位ビットを’1’にしていることが利いてきます。
074CのCMP Mの結果キャリーが立っていたら、A<(HL)なので(HL)は80以上の値ということになります。
そこはテーブル文字列の終わりですから、対象とする文字列とテーブルの文字列が最後まで一致していることになります。
その場合にはEX5:にジャンプします。
EX5:は命令を解読した結果得られたその命令のジャンプ先にジャンプするための処理ルーチンの入口です。

074CのCMP Mの結果キャリーが立たなかったら、(HL)<=7Fなのでまだテーブル文字列が残っていることになります。
つまり照合の結果は不一致だったことになります。
その場合には0750のEX2:から0752のJNC EX2までが実行されます。
ここでテーブル文字列の残りの部分をスキップしています。
そして(HL)>7Fが見つかったらそこがテーブル文字列の終わり+1のアドレスですから、次の0755でINX Hでアドレスをもう1バイト+1して次のテーブル文字列のアドレスの1バイト前の位置にHLを進めた後、073Cで保存しておいたDEをもとにもどしてからEX0に戻って次のテーブル文字列との照合を続けます。

さて。
処理の最終作業です。
EX3:は途中で’.’がみつかったためにそこで照合を打ち切ってここにジャンプしてきました。
照合の途中なのですが早々と「一致」の判断をしたためにまだHLレジスタはテーブル文字列の途中のアドレスのままです。
そこでEX3:から075EのJNC EX4までを繰り返し実行してテーブル文字列の残りの部分をスキップします。
ここは0750から0752でやっていることと同じです。
スキップした結果HLにはジャンプ先アドレスの上位バイトの位置が入ります。
その次の0761のEX5:は文字列が完全に一致したときのジャンプ先にもなっています。
ここではその2バイトのデータの上位バイトの最上位ビットを’0’に戻して正しいジャンプ先アドレスに直してからそれをHLレジスタに入れています。
最後に0767でPOP PSWを実行しています。
これはよく忘れてしまうところです。
これを忘れてしまうとプログラムが怪しげな動きをしたり意味不明の暴走をしたりしてしまいます。
プログラムの流れをよく見てみますと、073CのPUSH Dがそのままになっていることがわかります。
PUSHしたものは必ずPOPして終らなければなりません。
それはわかったとして、しかしPOP Dではなくて、なぜにPOP PSW?
実はここ(0767)が実行された時点でDEには入力バッファ(またはTEXTエリア)の対象文字列(命令の文字列)の次のアドレスが入っています。
たとえば
PRINT A ならば
    ↑ここのアドレスです。
(ブラウザにより↑の位置がずれてしまいます。上の説明の「次のアドレス」はPRINTの後ろのスペースのアドレスです)
POP Dを実行するとそのアドレス情報が失われてしまい、このあとの処理ができなくなってしまいます。
POP PSWはそれを避けるためのダミー命令です(ここまで説明しました命令解読処理の結果としてはAレジスタの値もフラグの値も捨ててしまっても構いません)。
最後にPCHLで目的の処理ルーチンにジャンプします。

ところでテーブルの最後の’STOP’まで照合を進めても不一致だった場合にはどうなるでしょうか?
実はその場合にはテーブルの一番最後0700に置かれた831Dが示すアドレス031Dにジャンプします。
どうしてそうなるのか、上の説明をよく読んでいただいて、プログラムの流れを追っていけばそうなることが理解できるはずです。
ここは次回までの宿題ということにいたしましょう。

復活!TINY BASIC[第20回]
2020.6.13upload

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