エピ11.2 メモリの使い方が分ってきました - 変数領域を特定!
***
エピ11.2 メモリの使い方が分ってきました - 変数領域を特定!
***
エピ11.1にて初期化処理を詳しく見たところ、特に "NEW" 命令と "CLR" 命令の処理の違いから [4634h] 以降には変数の領域があるのではないかという考察をしました。
それでスキマ時間にコツコツと調べました。A=0 や A$="A" を入力してメモリにどう反映されるかを追跡したのです。
その結果を発表します!
1 [4634h]: 関数定義(DEF FN)
2 [4636h]: ストリング変数(2次元配列)
3 [4638h]: ストリング変数(1次元配列)
4 [463Ah]: ストリング変数
5 [463Ch]: 数値変数(2次元配列)
6 [463Eh]: 数値変数(1次元配列)
7 [4640h]: 数値変数
8 [4642h]: 文字ワーク(文字列の作業用)
9 [4644h]: 数値ワーク(数値の作業用)
*
最初に調べたときには 4636h と 463Ch の2つは見つからなかったけど、突き止めましたよ。ホメてー
***
例えば、AA$="ABC" を入力すると、
[463Ah] = 480Eh : 4141 4142430D 0000
[463Ch] = 4816h : 0000
となります。[463Ah] 番地の値は初期値と同じ 480Eh で、[463Ch] の値が 4810h から 4816h へと6増えます。
更に BB$="DEF" を入力すると、
[463Ah] = 480Eh : 4141 4142430D 4242 4445460D 0000
[463Ch] = 481Ch : 0000
となります。文字列は [4642h] に格納されてから [463Ah] に転送されるようです。
変数名は2バイト(4141, 4242)、文字列は可変長で 0Dh で終端(4142430D, 4445460D)。最後の 0000h は領域の終端かな。
変数名が1文字の場合、A$ なら 4120 となる。3文字以上の場合は、ABC$ なら 4142 となって3文字目以降は変数領域に反映されない。プログラムリストには3文字目以降も存在するが、変数としては2文字までで識別する。
それから、ストリング変数が追加されると、4)の 188Fh のルーチンで [463Ch] から [4644h] の値を更新します。そのときの BC は挿入したバイト数、DE は挿入したストリング変数の番地です。
188Fh のルーチンは2回コールされていて、1回目は変数名2バイトと終端1バイトの計3バイトを挿入したとき。2回目は文字列("ABC")を挿入したときです。
ここまで解読して、188Fh のルーチンの謎が解けましたよ。このルーチンはプログラムの挿入だけではなくて、変数の挿入でも使えるのです。
*
色々観察して、[463Ah] はストリング変数の領域、[4642h] は文字列の作業用領域(文字ワーク)と推定しました。
*
ストリング変数を参照するときは変数名で探索するのだよね。ポインタや索引的なものが見当たらないけど? 変数名の順番でソートしている様子もない。真魚なー
文字列は可変長だから、次の変数名を調べるには文字列を終端まで調べるのかな。ストリング変数が複数あって最後の変数を参照するときには、ストリング変数の領域を最初から最後まで調べるのかなー
文字列の長さに制限ができますが「長さ+文字列」で表現する方法もあって、これだと探索がマシになります。文字列の最大が 255 文字で良いなら長さは1バイトに納まるので、終端を付ける方法とサイズは同じ。
変数の参照はプログラムの実行速度に影響するから考慮すべきと思うのです。それともBASICではストリング変数の使用頻度は低いの? と言うか(4文字以下の)短い文字列は数値として処理するのが良いのかな。数値配列とストリング変数では、どちらが速いのだろう?
*
数値の場合は。A=0:B=1:C=-1 と入力すると、
[4640h] = 4814h : 4120 8000000000
4220 C100000080
4320 4100000080 0000
[4642h] = 482Bh : 0000
となります。数値は [4644h] を経由しました。
変数名は2バイト(4120, 4220, 4320)で、数値は5バイトです。
80 00 00 00 00 = 0
C1 00 00 00 80 = 1
41 00 00 00 80 = -1
これは指数部の付いた実数、...浮動小数点数ですね。
幾つか数値を入れて観察した感じでは、
・最初のバイトのビット7(MSB)が数値の符号(1:正, 0:負)
・最初のバイトのビット6〜ビット0が指数部
・ビット6が指数の符号
・ビット5〜0が指数
・残りの4バイトが仮数で、バイト順はリトルエンディアン
でした。リトルエンディアンは、最初のバイトが最小桁になるという順番です(...リトル・インディアンではないよ笑)。
計算式にすると、
符号 x 仮数 / 0x100000000 x 2^(符号 x 指数)
です。例えば、
C2 A1 DA 0F C9
だと、C2 = 1 1 000010 だから、数値の符号は1、指数の符号は1、指数は2。仮数は4バイトを逆順にして C90FDAA1h。計算式に代入すると、
1.0 * 0xC90FDAA1 / 0x100000000 * 2**(1.0 * 2)
= 3.14159265...
になるよ。みんなも計算してみて!
*
SP5030は整数と実数を区別しないのです。N-BASICやF-BASICには整数型・単精度型・倍精度型の区別があるのに。
整数のみを扱うプログラム(ゲームだと多いよね)では最初に DEFINT A-Z すると高速化できてメモリも節約できて良かったのよ。
PC8001やFM7、MSXの時代の話だけど。今ではね、...。
*
配列(添字付き変数)の場合は。
1次元配列(の数値変数)は、DIM A(2) と入力すると、
[463Eh] = 4812h : 4120 0200 8000000000 8000000000 8000000000 0000
[4640h] = 4827h : 0000
となります。変数名・添字・要素ですね(この添字は配列の宣言時の添字)。要素は数値で、(添字+1)個ある。DIM A(2) だと A(0), A(1), A(2) だから3個。数値の初期値は0です。
2次元配列(のストリング変数)は、DIM A$(2,1) と入力すると、
[4636h] = 480Ah : 4120 02 01 0D 0D 0D 0D 0D 0D 0000
[4638h] = 4816h : 0000
となります。変数名・添字1・添字2で、要素は文字列が、...(添字1+1)*(添字2+1)個です。DIM A$(2,1) なら終端だけの文字列が (2+1)*(1+1) = 6 個できます。添字が1バイトなのが1次元配列と違いますね。添字は最大 255 なので1バイトで良いのです。
2次元配列の要素の順番は、
A$(0,0)="A":A$(1,0)="B":A$(2,0)="C"
A$(0,1)="D":A$(1,1)="E":A$(2,1)="F"
と入力すると、
[4636h] = 480A: 4120 02 01 410D 420D 430D 440D 450D 460D 0000
[4638h] = 481C: 0000
となります。DIM A$(i,j), A$(I,J) とすると I+J*(i+1) ですね。
配列の添字は値を評価するときに小数点以下を切捨てるようです。...これ、ちょっと心配。
*
関数定義(DEF FN)は、名前と引数が各1バイトで、命令文(数式)は可変長で、0Dh で終端でした。
*
余談になりますが。"DEF FN" 命令文は
10 DEF FNA(X)=X+1
20 PRINT FNA(1)
のような使い方をします。'=' の右側は数式です。
でもね。キーワードの中に20hが入っている斬新さ。だけどこのスペースは無くてもよくて、
10 DE FF NA(X)=X+1
20 PRINT F N A(1)
のようにデタラメに見えてもエラーなく動く奇妙さ。変数名に FN は使えないだろうと予測はできるけれど、仕様に明記されていない(よね?)曖昧さ。ブチブチする。
あとね。FNA(1) で呼び出すと、まず変数 X を 23D2h で処理して、X の値をDEFFN領域に退避します。それから X=1 と代入して数式を評価します。最後に X の値を元に戻します。変数 X が使用宣言されていないと初期値0で数値変数領域に残るのは注意かな。
***
**
*
数値の作業用の領域(数値ワーク)[4644h] ですけど。
A=(1+2*(3+4))/(5+6)
と入力すると、1, 2, 3, 4 と順に [4644h] 番地に転送されて、3+4 = 7、2*7 = 14, 1+14 = 15 と計算が進み、次は 5+6 = 11、そして 15/11 = 1.364 となります。
それは良いのですが、数値が転送される毎に [4644h] は5増加するのです。4 まで転送されたときは
[4644h]-15 = 4818h : 1.000
[4644h]-10 = 481Dh : 2.000
[4644h]- 5 = 4822h : 3.000
----------------------------
[4644h]+ 0 = 4827h : 4.000
となります。最初に転送された 1 は 4818h 番地で、2 は 481Dh 番地に入っています。そして、3+4 を計算すると [4644h] は5減少して、
[4644h]-10 = 4818h : 1.000
[4644h]- 5 = 481Dh : 2.000
----------------------------
[4644h]+ 0 = 4822h : 7.000 (=3+4)
このように 7 が 4822h 番地に入ります。HP電卓かFORTH言語が連想されますね。
*
[4642h] 〜 [4644h]-1 の範囲は文字ワークと考えていたのですが。
[4644h] が増加することで、[4642h] 〜 [4644h]-1 の範囲に数値が入ることに注意ですね。一時的なことですし、文字列は壊していないですけど。
文字ワークを参照するときは、文字列の終端(0Dh)と文字ワークの終端(0000h)をチェックしましょう。
途中で変数が増えて領域がシフトしたら混乱しそう、...。それに数式の処理中に文字列の処理がある場合はどうなるんだろう?
A=(1+2*(LEN(B$+C$)+4))/(5+6)
LEN() は文字列の長さを求める関数です。
***
**
*
SP5030のメモリの使い方が分ってきました。いわゆる解像度が上がった感じです?
実装から分かったことを整理します。主に自分のためですけど。だらだら書いたので、後で書き直すと思います。
*
・4806h 番地以降がプログラムの領域。プログラムは 4806h 番地からに記憶する。
・プログラムの終端は 0000h。「次文の番地」が 0000h ならプログラムの最後であることを意味する。
・プログラムの領域の後に変数の領域と作業用の領域が続く。
・変数の領域は、関数定義・2次元配列ストリング変数・1次元配列ストリング変数・ストリング変数・2次元配列数値変数・1次元配列数値変数・数値変数の7つ。[4634h] 〜 [4640h] の7個によって管理する。なお、4806h 〜 [4634h]-1 がプログラムの領域。
・作業用の領域は、文字ワークと数値ワークの2つ。[4642h] と [4644h] の2つで管理する。[4642h] 〜 [4644h]-1 が文字ワーク(数式の処理中は例外あり)。数値ワークは [4644h] 以降。
・各領域の初期値は 0000h(領域は最小2バイト)。この 0000h は領域の終端である。ただし数値ワークには終端は無い。
*
・変数名は2バイト固定(関数定義は1バイト)。1文字の変数は2バイト目を 20h で埋める。2文字より1文字の変数名の方が速い。
・新規の変数は末尾に追加する。変数名でソートしない。文字列の初期値は 0Dh で、数値の初期値は 8000000000 である。
・変数を参照する場合は変数名で検索する。検索して 0000h に到達したらその変数は無いことが分かる。使用頻度の高い変数を先に宣言をすると検索が効率化。
・変数名は領域が異なれば同じ名前を使える。数値変数 A とストリング変数 A$ は別の変数。1次元配列 A(1) と二次元配列 A(1,1) も別の変数となる。
*
・数値は5バイトの浮動小数点数。うち4バイトが仮数。指数は6ビット。
・数値変数の検索は、[4640h] から開始して、最初の2バイトを変数名と比較して、不一致なら「7」を加算すると次の変数になる(変数名が2バイト、数値が5バイト)。
・1次元配列数値変数の場合、[463Eh] から開始して、「4+要素数*5」を加算すると次の変数になる(変数名が2バイト、添字が2バイト、数値が5バイト)。
・宣言時の添字がiのとき、要素数は「i+1」になる。添字は 0〜255 の範囲。
・見つかった場合、参照時の添字がIのとき「4+I*5」を加算すると数値の番地になる。
・2次元配列数値変数の場合、「4+要素数*5」を加算すると次の変数になる。添字は各1バイト。
・宣言時の添字がi,jのとき、要素数は「(i+1)*(j+1)」になる。
・見つかった場合の数値の番地は、参照時の添字がI,Jのとき「4+(I+J*(i+1))*5」になる。
*
・ストリング変数は、[463Ah] から開始して、終端 0Dh を探して、その後が次の変数。
・1次元配列ストリング変数の場合は、...大変だよー
・2次元配列ストリング変数の場合は、...大変だよー 大変だよー
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.3.17 エピ11が加筆により長くなったので分割
2026.3.20 微加筆と微推敲
2026.3.22 エピタイ修正
2026.4.1 微推敲
2026.4.19 微推敲




