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

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



[第49回]


●PRTNUM,PRNLN

今回はPRTNUMとPRNLNの説明です。
PRTNUMはスタックを利用していて、その使い方がちょっと理解しにくいかもしれません。
説明するのもなかなかに難しいです。





まずはPRTNUMの説明です。
最初にBに0を入れていますがこれは前回説明した最上位ビットをトグルすることとは無関係です。
数値が負の時に’−’を表示するためのSW兼バッファとして使っています。
次のCHKSGNで正数だったときはPN1にジャンプします。
負数のときはBに’−’の文字コード2Dを入れ、桁表示カウンタCを−1します。

PN1:からが16進数を10進数に変換するプログラムです。
PN1:はその前準備です。
最初のPUSH DでDEを保存します。
このときDEにはおそらく行位置のアドレスが入っているはずです。
それを壊さないようにスタックに保存しておきます。
次の
LXI D,0AH
でDEに000A(=10)を入れます。
同時に次の
PUSH D
でバッファの終わりのマークとしても利用しています。
そのあともう一度DCR Cで桁カウンタを−1した後、PUSH Bで’−’の文字コードと表示桁数カウンタを保存します。
ここでDCR Cを実行しているのは次の理由からです。
PN2:以下のところで10進数に1桁変換するごとにDCR C(プログラム中ではDCR L)を実行しますが最後の桁を算出したときだけDCRを実行しないでループを抜け出てしまうため、それを補うために前もって1回余分にDCR Cを実行しているのです。

PN2:でCALL DIVIDEを実行します。
ここからが16進数を10進数に変換するプログラム本体です。
16進数から10進数への変換は、HLにある16進数をDE(000A、つまり10)で割ることで行なわれます。
例で示します。
HLに3039Hが入っているとしてそれを10進数に変換してみます。
3039Hは10進数では12345です。
1)3039/000Aを計算して商と余りを求めます。
商は04D2で余りは0005になります。
2)04D2/000Aを計算します。
商は007Bで余りは0004になります。
3)007B/000Aを計算します。
商は000Cで余りは0003になります。
4)000C/000Aを計算します。
商は0001で余りは0002になります。
5)0001/000Aを計算します。
商は0000で余りは0001です(計算終了)。
余りを逆に見ていくと1、2、3、4、5となって10進数に変換されていることがわかります。

PN2:からのプログラムでは上で説明した演算をスタックを利用して行なっています。
CALL DIVIDEの結果、商はBCに入り、余りはHLに入ります。
BC(商)が0になったら計算終了です(PN3にジャンプします)。
結果が0ではないときはXTHLを実行します。
HL(余り)をスタックトップに置いて、代わりにスタックに保存したBCの値(’−’コードと桁数カウンタ値)をHLに入れます。
そのあとLを−1してPUSH Hを実行します。
ここでLはもとはCレジスタに入れていた表示桁数カウンタです。

ここまでの実行によってスタックには最初の000Aが置かれ、次に最初の余りが置かれて、最後に’−’コードと桁数カウンタ値が、その順に置かれます。
そしてDIVIDEの結果の商(BCの値)をHLに入れてPN2に戻って繰り返します。
繰り返し実行した結果BC(商)が0になって計算を終了した時点でスタックトップには’−’コードと桁数カウンタ値が置かれ、そこから変換された10進数の上位桁から下位桁の順に各桁の値(桁によって0000〜0009のいずれかの値)が並び最後にエンドマークとして000Aが置かれることになります。

PN3:からは10進数の表示を行ないます。
最初にPOP Bで’−’の文字コードと表示桁数カウンタをBCに戻します。
05B5 PN4:DCR Cから05BC JMP PN4で数値の前の空白(スペース)を表示します。
Cには最初は#で指定する表示桁数(初期値は6)が入っていて、桁ごとに10進数に変換するたびに−1されます。
負数だったときには’−’を表示するためにその分も−1されています。
PN4:でDCR Cを実行した結果マイナスならば数値の前に置く空白(スペース)はないことになりますからループを抜け出ます。
マイナスになるまでの間スペース(文字コード20)を表示します。
RST 2は1文字表示ルーチンです。

PN5:では’−’を表示します。
Bには負数の場合’−’の文字コード2Dが入っています。
正数の場合には先頭で設定した0のままです。
MOV A,B
ORA A
の結果0でないならば2Dが入っていることになりますからCNZ 10Hを実行します。
0010番地のサブルーチンはRST 2です。
ここでRST 2を使おうとすると
JZ LABEL
RST 2
LABEL:
と書くことになりますから1バイト余分にメモリを消費してしまいます。
うーん。
実に徹底していますなあ。

その次の
05C6 MOV E,L
は何をやっているのでしょう?
実はこのときHLには最上位桁の値が入っているのです。
PN2:以下のプログラムで最後に
05A9 JZ PN3
で10進数への変換を終えたとき、HLには最後の余り(つまり最上位桁の数)が残ったままなのです。
それ以外の桁の数値は順次スタックに入れられています。
そこでスタックには入れられないで最後に残っていた最上位桁の値をここでEレジスタに渡して、PN6:以下のところで10進数として表示するようにしています。

で。
PN6:以下の処理なのですが。
POP Dでスタックから1桁の値をDE(必要なのはEの値)に入れた後、エンドマークの0Aかどうかをチェックします。
0Aならば初めにPUSH Dで保存したDEをPOP Dでもとに戻してリターンします。
0Aでなければ0〜9の値を30〜39の文字コードにしたうえでRST 2を実行します。
そしてPN6に戻って繰り返します。
上の説明からするとPOP Dの位置がおかしいように思えますが、ここは実に巧妙なプログラムです。
なかなかこんな見事なプログラムは書けません。
大抵はごちゃごちゃしたプログラムになってしまいます。
最上位桁の表示とエンドマークのチェックと最後の処理としてのPOP Dをまとめてさらりと書いてしまっています。
感服!
です。

次はPRTLNです。
PRTLNは1行のプログラム行を表示するサブルーチンです。
特に難しいところはありません。
行の先頭の2バイトの行番号をHLに入れてPRTNUMをCALLし、続いてプログラム行の文字列をPRTSTGで表示します。

復活!TINY BASIC[第49回]
2020.7.27upload

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