chibicc compiler を6800向けに改造する (5) アドレッシングと最適化

2025/04/03BASICMASTER, 昔のパソコン

MC6800向けにコンパイラを書いていて困るのは、Acc A,B と IX レジスタ と SP が別世界の住人であることだ。

Acc A,B で演算した結果をIXやSPに移動するには、ゼロページを経由するしかない。12cycles, 6bytes必要だ。遅い。

スタック経由でも渡せるが、その場合はさらに時間がかかる。


	STAB	@tmp+1
	STAA	@tmp
	LDX	@tmp


MC6809やMC6803なら悩まなくて済む

MC6809はTFR命令でレジスタ間の移動は自由にできるし、LEAX命令で加算も可能だ。

もちろん、最適化を極めれば苦労はあるのだが、そこそこの労力でそれなりのコードが生成できる。MC6800とは大違いである。

MC6809用のコンパイラである acwjも、アドレス生成には苦労しているが、MC6800だともっと大変。


MC6800用のコード生成に必要なもの

Acc A,B と IX レジスタが別世界の住人であることを意識したコード生成が必要である。

例えば、このようなコードだ。変数に値を代入するのだが、それぞれ違うコードを作る必要がある。


int	g;
int	h[10];

void func(int x)
{
	int	a;
	int	b[10];

	g = 0;
	g = 複雑な式;
	h[0] = 0;
	h[0] = 複雑な式;
	h[x] = 0;
	h[x] = 複雑な式;

	a = 0;
	a = 複雑な式;
	b[0] = 0;
	b[0] = 複雑な式;
	b[x] = 0;
	b[x] = 複雑な式;
}


右辺がIXレジスタを使わない場合

0ならCLR命令が使えるし、定数ならLDAB #定数下位バイト / LDAA #定数上位バイト で良い。面倒なので、以下 LDD と書くことにする。

左辺がアドレス計算を伴う式であっても、結果がIX+offsetで返されれば次のコードで生成できる。


	(左辺のアドレスを生成してIXに入れる)	
	LDD #定数 (あるいはIXを使わない演算)
	STD off,X

配列でないローカル変数や、添え字が定数であるローカル配列は上記パターンで良い。グローバル変数も同様。

IXレジスタだけを使って redeference できる場合も同様である。


左辺がAcc A,Bを使わない場合

A = expr; や A[const] = expr; のような代入は、左辺のアドレスを求めるのにAcc A,Bを使わなくて済む。

このような場合は以下のようにコードを生成する。


	右辺を計算する。IXを壊して良い。結果はAcc A,B
	Xに左辺のアドレスを入れる
	STD off,X

左辺がグローバル変数であれば、絶対アドレスで STD adrs+off になるが、大差はない。

左辺と右辺で壊れるレジスタをあらかじめ知っておく必要がある

どのレジスタが保存されて、どのレジスタが保存されないかを、オブジェクトコードを生成する前に知る必要がある。

オブジェクトを生成しながら調べると、AccABやIXの待避/復旧が発生して効率が悪い。

両辺が共に Acc A.BとIXを壊す場合が面倒だが、このようなコードになる。


	(右辺を計算する)
	PSHD
	(左辺のアドレスを計算してIXに入れる)
	PULD
	STD off,X

左辺から計算するとこうなる。


	(左辺のアドレスを計算して、Acc A,Bに入れる)
	PSHD
	(右辺を計算して、Acc A,Bに入れる)
	PULX
	STD off,X

6809ならPULSXは簡単だが、6800だと次のような長いコードになる。18cycles,5bytes。遅い。


	TSX
	LDX 0,X
	INS
	INS


結局何が必要か?

コード生成の前に、部分式を評価する場合に破壊されるレジスタ、結果が得られるレジスタを知っておく必要がある。dereferenceが絡むと面倒くさい。

続く