Amsterdam Compiler Kit を6800向けに改造する (1) スタック構造

BASICMASTER, 昔のパソコン

Amsterdam Compiler Kit というポータブルで多言語のコンパイラがある。

私は30年以上前に MINIX 2.0 で使っていた。PC/AT互換機(80286)で草の根BBSのホストプログラムの構築に使っていた。懐かしいなあ。

ack は 6502やcpmやpdp-11などで動くのだが、残念ながら6800用では動作しないようだ。

6800や6809用のアセンブラは存在するようなので、cg (code generator)とライブラリを作れば動きそうな気がする。とりあえずやってみる。


スタックフレーム

ソースプログラムは仮想マシン(スタックマシン)EMのコードにコンパイルされ、その後、CPUに応じたコードに変換される。

変換を行うプログラムは cg, ncg の2種類あるようだ。cg はテーブル参照で機械的にコードを置き換えるCode Generator。とりあえずこれを使うことにする。

引数は右から積まれているようだ(要確認)。引数やローカル変数はフレームポインタ相対でアクセスする。演算途中の情報はスタックに持つ。

ackではフレームポインタを Local Base(LB) と呼んでいる。


変数・引数アクセス(Z80)

Z80では、FPとしてIYを使っている。ローカル変数アクセス(LOL)での変換テーブルは下記の通りだ。

最初のlolはオフセットが小さい場合だ。sfit($1,8)が条件である。その場合は、IY相対でアクセスする。

その他の場合は、アドレス計算が発生する。

まずallocate(HL_REG,REG)で使用レジスタを宣言する。HL_REGが%[a]、REGが[%b]になる。%bはDE,BCのどれかになる。仮にBCだとしてどうなるか見てみる。

push/popで IYをBCにコピーし、HLにオフセット$1を入れる。ADD HL,BCを行うことで、 HL←IY+オフセット になる。BCに(HL)を読み込む。結果はBCで返す(最後の%[b])。


lol sfit($1,8)  | |             allocate(REG)
                                move({INDEXED,LB,$1} , %[a.2] )
                                move({INDEXED,LB,$1+1} , %[a.1] ) | %[a] | |
lol | |				allocate(HL_REG,REG)
				"push iy"
				"pop %[b]"
				move({IMMEDIATE,$1} , %[a] )
				"add hl,%[b]"
				"ld %[b.2],(hl)"
				"inc hl"
				"ld %[b.1],(hl)"
				erase(HL)
				erase(%[a])	| %[b] | |


変数・引数アクセス(6502)

6502の場合は、ゼロページにソフトウェアスタックポインタを持っている。LBもゼロページだが、それ以外にLBlというポインタを持つ。

6502の場合はIXもIYも8bitレジスタなので、直接スタックアクセスが行えない。そこでゼロページに16bitのポインタを置いて、そこからのオフセットをIYに設定してアクセスを行う。

このとき IYで指定するオフセットは 0〜255 の正の値なので、負方向に存在するローカル変数へのアクセスが行えない。

そこで、あらかじめ負方向に240ずらしたポインタ LBl を置き、そこから正方向へのオフセットとしてアクセスを行う。

アクセスが行える範囲は 0〜255 なので、結局 -240〜+15までの領域がアクセスできることになる。引数が1つ2バイトなら6個使えるので通常の利用では問題にならないだろう(戻りアドレス2バイトが含まれるので引数は6個)。

LBは関数プロローグで保存されるが、LBl は保存されない。 LBl は関数から戻るときにLBから再計算される。



変数へのアクセスはオフセットが範囲内のときと、範囲外で異なる。範囲内かどうかはIN($1)で判断している。


#define em_bsize        2       /* must be equal to EM_BSIZE */
#define ND      !defined($1)
#define D       defined($1)
#define BASE    240
#define MIN     (0-BASE)
#define MAX     (254-em_bsize-BASE)
#define IN(x)   (x>=MIN && x<=MAX)
#define IND(x)  (x>=MIN && x<=(MAX-2))

lol IN($1)      | |
                allocate(R16)
                "ldy #BASE+$1"
                "lda (LBl),y"
                "tax"
                "iny"
                "lda (LBl),y"
                                        | %[a] | |
lol     | |
                allocate(R16)
                "lda #[$1].h"
                "ldx #[$1].l"
                "jsr Lol"
                                        | %[a] | |


EM_BSIZE

6502やZ80では、フレームポインタをメモリに積むので、引数・リターンアドレスとローカル変数の間に2バイトの領域が必要になる。

このサイズはCPUやスタックフレームの構成方法によって異なるので、そのサイズをコンパイラに伝えるための変数が存在する。それが EM_BSIZE だ。

例えば、6502ではローカル変数アクセスに LBlポインタを使っているが、これは関数呼び出しのときは保存されず、復帰の際に呼び出された関数側で再計算している。

もし、LBl を関数呼び出し時に保存するなら(たぶん6800ではその方が良い)、EM_BSIZE が4になる。


リンク