Amsterdam Compiler Kit を6800向けに改造する (1) スタック構造
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になる。
ディスカッション
コメント一覧
まだ、コメントがありません