共立出版の『マイクロコンピュータのプログラミング』に掲載された東大版Palo Alto Tiny BASICについて調査しています。アセンブラで書かれたプログラムの先頭では、以下のようにメモリ配置が定義されています。
; MEMORY MAP
ITOP EQU 0000H
ISIZE EQU 0800H
LTOP EQU 1000H
VTOP EQU 5000H
LBUF EQU VTOP+0037H ;0037H?
LBFSZ EQU 80
MSTK EQU LBUF+LBFSZ
TMSTK EQU MSTK+30 ;30?
STACK EQU 6000H
本文では「図3 メモリマップ」として説明されています。VTOPとLBUFの間は「変数エリア」になっています。Tiny BASICですから、プログラムで使える変数は「A」~「Z」と配列「@」の27個に制限されています。また変数に格納できるのは16ビット整数だけです。つまり27種類×2バイト=54バイトあれば良い計算になります。これは、16進数ならば0036Hです。
ところがLBUFは、「VTOP+0037H」として定義されています。何故「0036H」ではないのでしょうか。プログラムを解析してみると、ロジックの都合ではないかと思われます。
LBUFからLBFSZ(=80)は、「行バッファ」です。キー入力される文字列を格納する領域です。この処理は、次のようになっています。
EDITR: MVI A,'>'
CALL GETL
PUSH D
LXI D,LBUF
CALL GINT
CALL SKPBL
MOV A,H
ORA L
POP B
JZ KWCPR
INSRT: DCX D
MOV A,H
STAX D
DCX D
MOV A,L
STAX D
GETLルーチンで入力された文字列は、先頭に数字があるかをGINTルーチンで判断しています。つまり「10 PRINT A」のように入力すると、先頭の数字をバイナリに変換し、数字文字列と置き換えます。また空白も取り除くので、入力された行は、「<0A><00>PRINT A」としてメモリ上に置かれることになります。数字文字列をバイナリに変換した結果を格納しているのが、INSRTで始まる処理です。
このようなロジックになっているので、現代的な書き方であるインデントを使用としても、全て取り除かれてしまいます。「10 PRINT A」のように空白を入れても、内部構造は「<0A><00>PRINT A」になります。さらに当時の厳しいメモリ事情の為なのか、Tiny Trekのプログラムでわかるように、意味がとおるなら空白文字を省くことができます。つまり「10PRINT A」としてもエラーではありません。
ここで考えなければならないのは、数字文字列をバイナリに変換して格納している点です。バイナリは16bitなので、2バイト必要です。数字文字列が「10」であったり、例え一桁であったとしても「1 PRINT A」のように空白が入っていれば、2文字あるので、16bitのバイナリを格納することができます。ところが「1PRINT A」のような記述を認めているので、数字文字列が1文字しかない場合、16bitのバイナリを格納するには領域が不足してしまうのです。プログラムでは例外処理がありません。どうしているかというと、LBUFの直前にある「変数エリア」に踏み込んでしまうのです。これが「0036H」ではなくて「0037H」になっている理由と思われます。
試しに「LBUF EQU VTOP+0036H」としてアセンブルしたプログラムで実験してみました。変数「Z」に何か値を代入しておいて、「1PRINT Z」を入力すると、変数「Z」の値が変わってしまいました。やはり変数「Z」の領域を壊してしまうのです。
こうならないようにするため、あえて「LBUF EQU VTOP+0037H」として1バイト分ずらして領域を確保しているのですが、極めて技巧的です。
0 件のコメント:
コメントを投稿