エピ6.2 メインループを解読しましょう - プログラムの記憶
***
エピ6.2 メインループを解読しましょう - プログラムの記憶
***
入力された文(4400h〜 に格納された文字列)を処理するルーチンと、文番号付きの文をプログラムとして記憶するルーチンについて解読します。エピ6.1の続きです。
***
入力文を処理するルーチンは 13C9h と 143D の2つあります。キーワードを中間コードに変換する処理なのですが。
13C9h は 4400h〜 が入力で 4459h〜 が出力です。
143Dh は 4459h〜 が入力で 4400h〜 が出力です。13C9h とは逆です。
つまり、
4400h ⇒(13C9h)⇒ 4459h
||
4400h ⇐(143Dh)⇐ 4459h
となってます。2段階の処理なのでしょうか。順番に見ていきますね。
*
13C9h の処理です。詳細は補足編で細かく解説することにして、ここでは概略を記します。
1)16E9h のルーチンで 4400h から調べて、数字なら10進数-16進数変換をして 4457h〜4458h に格納します。これが文番号になります。最初の文字が数字か20hでなければ文番号は 0000h にします。
最初と途中と最後の20hは 162Eh のルーチンにてスキップします。つまり、「 12 3 4 A=0」なら「1234 A=0」と扱います。それから「10A=0」は「10 A=0」です。
不思議な感じですね。他の言語なら容赦なくエラーにしそうです。BASICだから初心者に優しいのか。
2)残りは 0Dh で終端するまで1文字毎に 4459h〜 にコピーします。
20h や "(", ")", FFh ならコピーを続けます。キーワードに含まないのでそのままコピーです。FFh は、まぁキーワードに無いから良いのか。
22hなら次の 22h までコピーします。文字列ですからね。キーワードと同じ文字列でも中間コードには変換しないでコピーです。
3)コピーした(文字列を除く)文字が 3Fh ('?') だったら中間コードの 85h (PRINT) に変換します。
それ以外の場合(A-Z や 0-9, 他の記号)は、中間コードのリストを参照して、一致したら(コピーした文字を)中間コードに変換します。その際、162Eh のルーチンをコールして 20h をスキップします。
80h〜FEh の場合は SYNTAX ERROR です。エラー表示して 124Dh に行きます。FFh をエラーにしないのは不思議かも。
中間コードと一致しない場合は、2)に行って文字のコピーを続けます。
4)中間コード A8h (CURSOR) は F2h に変更します。F2h とは?
中間コードが 80h (REM) か 81h (DATA) の場合は、次の ":" か終端(0Dh)までコピーします。"REM" はコメント文なので文字列と同じくそのままコピーするのです。"DATA" も(22h で挟まずに)文字列が入りますからそのままコピーです。
5)コピーした文字数をカウントしていて、256文字を超えたら DATA ERROR を表示をしてメインループの 124Dh に行きます。
でもね。4400h〜 に格納できる文字列は最大で80文字なのでエラーにならないはずです。どうしてかな。
:
(これが概略?)
:
気になったのは、中間コードになるなら途中のスペースはスキップするし、前後に他の文字が付いていても構わず変換することです。
例えば("IF" の中間コードは 88h で、"THEN" は ADh です)、
"I FA=0 T H E NJ=1" ⇒ "{88h}A=0 {ADh}J=1"
となります。一見して正常な文には見えませんがエラーにならないのです。それから("ON" は 90h、"OR" は BAh、"TO" は AEh)、
"DIF = 0" ⇒ "D{88h} = 0"
"ONE = 1" ⇒ "{90h}E = 1"
"ORG = 2" ⇒ "{BAh}G = 2"
"TOP = 3" ⇒ "{AEh}P = 3"
などとなって、これらは実行時に SYNTAX ERROR になります。特に "ORG" がエラーになるとは予想できないですね。だって "OR" がキーワードとは非公開ですから。
変数名にキーワードを含むことはできない。...、このことはSP5030の仕様を確認したら明記されていたので仕様通りなのですけど。...、知らなかったです。
逆にこれはキーワードの前後にはスペースは不要であることも意味していて、例えば、
"IF A=0 THEN J=1"
ならば、
"IFA=0THENJ=1"
と入力しても良いのです。スペースを3つも省略できます。
...。だからと言ってそんなことをするのかと思いつつ、雑誌I/Oを見てみたら。「地底最大の作戦」(1980年7月号)のプログラムリストが、
120 FORX=0TO700:POKEV+X+200,109:NEXT
となっていました。ギチギチです。
他の記事でも幾つか同様なリストが見つかったので(スペース入りのリストもありますけど)当時の人には常識だったようです。...。orz。
メモリの節約になりますし、実行速度もアップするのかな。後で調べよう。
*
余談になりますが。
「地底最大の作戦」の記事にはゲームの操作方法だけではなくて、基礎の戦術や高等戦術(!)が解説されていて興味深いです。プログラムリストは2ページに収まるサイズなのに深いです。
作者は誰?...、と検索してみたら著名な方です。...、同姓同名の別人ではなさそうなので御本人でしょう。
この記事を投稿した当時は大学生かな。凄いな。
*
更に余談になりますが。
上記の2)や3)での FFh の扱いについてですけど。謎が解けました。
MZ80では FFh は"π"(パイ)という文字のアスキーコードで、SP5030では円周率(3.14159265...)を表す定数なのでした。ABC や 123 と同じくプログラムで使える文字なのです。
プログラム中で普通に、22hで括らずに、
X = COS(T*π/180)
などと使えるのです。変数名には使えないけど。不思議な感じ。
*
次は 143Dh の処理です。でもね。13C9h の処理で中間コードへの変換は完結しているので、143Dh では何をしているのかなと不思議です。調べてみると 13C9h の逆をしているのでした。
つまり、
1)4457h〜4458h 番地の値(文番号)を 16F1h のルーチンで10進数にして、4400h〜 にコピー。文番号の前後には20hを入れます。文番号なしの場合は0なので、20h,30h,20h となります。
2)4459h から読み出して、中間コードは元のキーワードの文字に戻して、...、22h はペアの 22h までコピーして、F2h は A8h に変更して、"REM" と "DATA" は、":" か終端までコピーする。
3)コピーした文字が80文字を超えたらエラーです。80文字とは...、出力先のサイズを超えないための制限かな。...、だとすると 4459h〜 のワークエリアは256文字分を確保しているのかな。
それでさ。4400h〜 を更新して何をしたいのでしょう。文番号と中間コードでスペースが削除されるだけなのだけど。更新した 4400h〜 はどこかで参照されるのかな? たのしみデスね。
***
**
*
最後はプログラムを記憶する処理です。
処理に入る前にプログラムを記憶するメモリについて整理しましょう。
メモリは 4806h からSP - 100 までです。そのサイズは(RAM48Kのシステムで、初期化処理の時点でなら)34680 バイトでした。ここにプログラムが記憶されます。
プログラムの1つの文は、次のようになっています。
番地(2バイト),文番号(2バイト),命令文(可変長、0Dh で終端)
番地は次文へのポインタです。文番号と命令文は(3)の 13C9h 番地のルーチンで処理したものです。
ポインタがあってリスト構造なのですが、文番号の順番で並んでいて、文と文の間に隙間はなくて...、ごく単純な構造です。
例えば、次のプログラムの場合、
10 A=123
20 B=456
30 C=789
メモリは("=" の中間コードは B6h です)、
4806 : 10 48 0A 00 41 B6 31 32 33 0D
4810 : 1A 48 14 00 42 B6 34 35 36 0D
481A : 24 48 1E 00 43 B6 37 38 39 0D
4824 : 00 00 00 00 ....
となります。最初の文は、番地 = 4810h, 文番号 = 000Ah, 命令文 = "A", {B6h}, "123", 0Dh です。
*
プログラムをメモリに記憶する処理は、削除と挿入の2つで行います。
新しく入力された文と同じ文番号のものがなければ単に(適切な位置に)挿入するだけです。
同じ文番号の文があれば、それを削除して、新しい文を挿入します。文番号だけで文が空の場合は削除だけです。
簡単ですけどね。
例えば、... 34K バイトの巨大なプログラムの最初の文を「10 A=1」から「10 A=2」に修正する場合。1バイトの変更だけで、31h を 32h にするだけでよいはずですが。
削除で 34K バイトを8バイト前に移動して、挿入で元の場所に戻すのですよ。移動したら次文への番地がズレるので修正が必要です。先頭の文から最後の文まで全部の番地を修正します。削除で修正して、挿入でも修正します。
少しだけ条件判断を追加すれば処理を減らすことは可能でしょう。削除する文と挿入する文のサイズを確認するとかさ。
SP5030の開発者は、ここでは処理時間よりコードサイスを優先すべきと判断したのです。多分。
*
では、129Fh からの処理です。
1)ワークエリアの一部を更新します。
・1973h をコールして、4577h を 00h にする。4577h は "READ" 命令で読み出したデータ数で、00h にするのは "RESTORE" 命令を実行するのと同じです。
・13BCh をコールして、4565h を 00h にする。プログラムを修正するので 00h にします。
2)削除の処理です。
入力した文の文番号は 4457h〜4458h と HL に入っています。文は 4459h〜 にあります。
・179Bh のルーチンをコールして、 [462Eh] に HL を格納し、HL と同じ文番号の文を探す
・文番号が同じ文が有れば削除します
・17DCh をコールして、削除する文のバイト数を求めて、BC に格納
・175Eh をコールして、削除する文を削除(=後ろを前に詰める)
・1652h をコールして、BC = -BC とする
・188Fh をコールして、[4634h]〜[4645h] に BC を加算
・17D4h をコールして、番地を修正(BC を加算する)
・入力した文が 0Dh だけの場合、処理終了でメインループの(2)に行く
3)挿入の処理です。
挿入するデータは、4455h〜4456h の番地、4457h〜4458h の文番号、4459〜 の命令文です。
・179Eh をコールして、文番号が [462Eh] 以上となる最初の文を探す
・4455h〜4456h 番地に挿入先の番地をセット
・17DCh をコールして、挿入する文のバイト数を求めて、BC に格納
・178Dh をコールして、挿入先以降を BC バイト後ろに転送、挿入元から挿入先に転送
・188Fh をコールして、[4634h]〜[4645h] に BC を加算
・17D4h をコールして、番地を修正(BC を加算する)
・処理終了で(2)に行く
削除では BC の符号を反転して加算しますので、減算するのです。削除なので値は小さくなる。挿入では BC をそのまま加算です。
***
**
*
命令文に文番号を付けて入力するとプログラムとしてメモリに記憶される様子が見えて来ました。
今回までに解読したのは 1200h 〜 1628h の 1064 バイトです。SP5030のコードは 1200h 〜 413Fh の 12096 バイトありますから約11%です。まだまだですね。
けれど重要なコードばかりです。SP5030のことが分かり始めた感じがします。いわゆる、ひとつの実感?
*
次回はプログラムをRUNして見たいです。あ、でも、その前に直接実行命令文が実行される仕組みを解読しないとですね。
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.3.22 エピタイ修正
2026.4.2 エピタイ修正、...アレ?
2026.4.15 微推敲




