ひところに比べるとMC6800向けのCコンパイラも だいぶ増えてきた。2つは EtchedPixels氏によるもので、2つは私が移植したものだ。
ack-6800は C言語以外(BASICやPascalなど)も動く。まだPascalの集合型がおかしい(私の実装ミス)。いい案がなくて放置中。
- zu2/chibicc-6800-v1: C Compiler for MC6800 (fork from chibicc)
- zu2/ack-6800: The Amsterdam Compiler Kit for MC6800
以下はEtchedPixels氏によるもの。
- EtchedPixels/Fuzix-Compiler-Kit: Fuzix C Compiler Project
- zu2/CC6303: A C compiler for the 6800 series processors
C言語の構成
これらの実装にはそれぞれ特徴がある。
CC6303
6502用のCC65の移植版。ソースコードを解析しながら、オブジェクトを生成する(ASTを作らない)。
なので、関数引数は左から積む。元が6502用なので、スタックの使い方が独特。
Fuzix-Compiler-Kit
Fuzix用に開発されたCコンパイラ。多数のcpuをサポートしているのと、Fuzix上でのセルフコンパイルを目標としている。
セルフコンパイルのために、(速度をある程度犠牲にしても)小さなオブジェクトを出すようにしている。
コンパイラも最適化を追求するよりも、コンパクトになることを目標としている(と思われる)。
ASTを作ったあと、byte操作で済む部分にマークを付けて、その後オブジェクトを出す。
chibicc-6800
x64用の小さなコンパイラ実装であるchibiccを、私が6800向けに改造したもの。
ASTを作ったあと、最適化作業で木を修正し、その後オブジェクトを出す。
高速なオブジェクトを出すことを優先しているが、生成されたオブジェクトサイズはFuzix-CCとあまり変わらない。
ack-6800
なつかしの Amsterdam Compiler Kit のコンパイラを6800向けに私が移植したもの。
コンパイラは中間コード(EM)を作るので、EMからMC6800向けの変換テーブルを書けば 移植終わりである。もちろんライブラリは必要。
コードの質はそこそこであるが、高速にコンパイラ開発できて素晴らしい。
フロントエンドはいじらなくていいので、EMからの変換さえ書けば各種言語が動く( C, pascal, basic, fortran, occamなど)。
EMがlittle endian前提なので、罠が多数ある。
そろそろIRを使ったコンパイラに手を出したい
chibicc-6800も落ち着いてきたので、別のCを探してみた。sdccがあった。これはASTからIR(中間表現)を作って、そこからコードを生成する。
IRの時点で共通部分式の括りだしや、変数の生存管理を行うのでかなり良いコードが出る。
だけど、その分コードサイズが大きくて、手をつけたものの 作業は遅々として進まなかった。
AIに助けてもらう
AIは大きなコードを読んで何をしているかの把握に使うと非常に便利である。コードも書いてくれるが、品質はやや怪しい( Claude Opus 5 )。
Claude のWeb UIは、クラウド上にコンテナを作ってそこで作業してくれる。絶対に自分の環境を壊さないという安心感がある。
これは ChatGPT workでもできるけど、慣れている Claudeでやってみた。
以下、現在作業中の guthub repository。
Ackerman関数をコンパイルすると、chibicc-6800よりも速いコードを出す。素晴らしい。
デフォルトでは再帰呼び出しできないので、関数側に __reentrant を付けてある。コンパイルオプションに –stack-auto を付けても良い。
/*
* ackerman
*/
unsigned char
ack_c(unsigned char x, unsigned char y)
__reentrant
{
if(x==0){
return y+1;
}else if(y==0){
return ack_c(x-1,1);
}else{
return ack_c(x-1,ack_c(x,y-1));
}
}
引数が2つともレジスタ渡しになっている。他に使う変数がないので、スタックを一切使っていない。
しかも末尾再帰を最適化している(引数をintにすると、末尾最適化はしなくなるけど、それでも速い)。
;------------------------------------------------------------
;Allocation info for local variables in function 'ack_c'
;------------------------------------------------------------
;y Allocated to registers a
;x Allocated to registers b
;------------------------------------------------------------
;9005-ack.c:6: ack_c(unsigned char x, unsigned char y)
; -----------------------------------------
; function ack_c
; -----------------------------------------
; Register assignment is optimal.
; Stack space usage: 0 bytes.
_ack_c:
;9005-ack.c:9: if(x==0){
tstb
bne 00105$
;9005-ack.c:10: return y+1;
inca
tab
rts
00105$:
;9005-ack.c:11: }else if(y==0){
tsta
bne 00102$
;9005-ack.c:12: return ack_c(x-1,1);
decb
ldaa #0x01
bra _ack_c
00102$:
;9005-ack.c:14: return ack_c(x-1,ack_c(x,y-1));
deca
pshb
jsr _ack_c
tba
pulb
decb
;9005-ack.c:16: }
bra _ack_c
比較のために chibicc-6800-v1/ztest/9005-ack.cを実行してみた。
sdccにはucsimというエミュレーターがあるので、それを使う。実行サイクルは 456,142,659 であった。
chibicc-6800 + emu6800 だと 529,788,849(-O2)である。
sdcc-6800は まだ無駄なコードを出している。それで この速度は凄い。
Simulation started, PC=0x000100
Stop at 0x00017b: (110) Program stopped itself
F 0x00017b
CPU state= OK PC= 0x00017b frequency= 1000000 HZ
Operation since last reset= 250943514 vclks
Inst= 108170009 Fetch= 170428550 Read= 47562079 Write= 32952885
Total time since last reset= 456.142658234719704 sec (456142659 clks)
Time in isr = 0.000000000000000 sec (0 clks) 0.00%
Time in idle= 0.000000000000000 sec (0 clks) 0.00%
Most value of stack pointer= 0x000000
Simulation: stopped
Runtime: 24.221586 sec
0
ライブラリはまだちゃんと作っていないけど、そこそこ速い
乗除算などは まだCで書かれた共通関数を使っている。アセンブラに比べると遅い。でも 動く。凄い。
整数版の mandelbrot が 327,909,745 cyc。chibicc-6800は私の書いた高速乗除算が使えるので 83,727,146 cyc。
Simulation started, PC=0x000100
111111111111111111222222222333334568BC67443322222211111111111000000000000000000
11111111111111111122222222233344598C 77943333222221111111110000000000000000000
111111111111112222222233324444556 955433332211111111100000000000000000000
111111111211112222222333455665778 976554444222211111100000000000000000000
11111112222222233333334457 AB9 787B5432111111111100000000000000000
11112222222222333333444667 532222111111111000000000000000
11111222333444444444555A 96443322211111110000000000000000
12222223345D6657 6555679 AA43322211111110000000000000000
222233334569 8C E8789 B43322111111110000000000000000
223333345578D E 43322211111111111100000000000
3344444789A 543322111111111111100000000000
5555658A C6433222222111111111100000000000
975443322221111111111100000000000
5555658A C6433222222111111111100000000000
3344444789A 543322111111111111100000000000
223333345578D E 43322211111111111100000000000
222233334569 8C E8789 B43322111111110000000000000000
12222223345D6657 6555679 AA43322211111110000000000000000
11111222333444444444555A 96443322211111110000000000000000
11112222222222333333444667 532222111111111000000000000000
11111112222222233333334457 AB9 787B5432111111111100000000000000000
111111111211112222222333455665778 976554444222211111100000000000000000000
111111111111112222222233324444556 955433332211111111100000000000000000000
11111111111111111122222222233344598C 77943333222221111111110000000000000000000
111111111111111111222222222333334568BC67443322222211111111111000000000000000000
Stop at 0x00017b: (110) Program stopped itself
F 0x00017b
CPU state= OK PC= 0x00017b frequency= 1000000 HZ
Operation since last reset= 272282183 vclks
Inst= 88907649 Fetch= 200602573 Read= 49780095 Write= 21899515
Total time since last reset= 327.909744572030320 sec (327909745 clks)
Time in isr = 0.000000000000000 sec (0 clks) 0.00%
Time in idle= 0.000000000000000 sec (0 clks) 0.00%
Most value of stack pointer= 0x000000
Simulation: stopped
Runtime: 19.570739 sec
0
まだバグが残っているし、ライブラリがないけど、簡単なテストプログラムは動く。興味ある方はお試しあれ。
以下はテスト用のスクリプト。
#!/bin/sh
PATH=/usr/local/bin:$PATH
OPT="-mmc6800"
OPT_O=""
OPT_g=""
while true; do
case "$1" in
--opt-code-speed|--opt-code-size)
OPT_O=$1
shift
;;
-g)
OPT_g="--i-code-in-asm"
shift
;;
--stack-auto)
OPT="$OPT --stack-auto"
shift
;;
--)
shift
break
;;
-*)
echo "Unknown option: $1" >&2
exit 1
;;
*)
break
;;
esac
done
CCRUN="sdcc $OPT $OPT_O $OPT_g "
s=$(basename $1 .c)
shift
echo "compiling:" $s
echo $CCRUN $s.c $OPT_lm
$CCRUN $s.c $OPT_lm
err=$?
if [ $err -ne 0 ] ; then
echo $err
exit $err;
fi
ls -Ggl $s.s19
printf "run\nstate\nquit\n" | ucsim_m6800 -q -I 'if=rom[0xfefe]' $s.s19
ret=$?
echo $?
exit $ret

コメント