相変わらず70年代のCPUでクロスコンパイラがあるものを探していました。
今回はミニコンPDP-8をワンチップにしたIM6100です。
マニュアル
http://www.bitsavers.org/pdf/dec/pdp8/cmos8/IM6100.pdf
なんといっても一番の特徴は12ビットCPUであることです。
12ビットのレジスタはAC(アキュムレータ)、MQ(補助)、PCの3本しかありません。
あとはキャリーに相当するLと割込み可のIEだけ、究極の少なさです。
命令もかなり削られています。例えば、メモリからACへのロードがないとか、XORがないとか。
かと思えば、OPRはお盆にどの小鉢を乗せるかでさまざまな機能を実現するような変わった命令です。
例えば、CLA CLL CML IAC RALのビットを選んだ場合は
ACをクリアする、Lをクリアする、Lを反転する、ACをインクリメントする、ACをL込みで左ローテートするの順に実行します。
結局ACに3が入る、というパズルのような命令です(実際に後述のコンパイラが吐きます)。
TAD(加算)で、キャリーがあるときLフラグが立つと思いきや、キャリーがあったときは反転するという仕様も変わっています。
DCAがストアしてACをクリアする命令なのですが、その後TADを使うと実質的にロード命令になります。
その際、フラグを変化させないためかな、と気がつきました。
初期のプロセッサを設計したエンジニアの思考をトレースしているようで面白いものです。
今回は、このCPUのエミュレータを作って、B言語を試してみます。
B言語コンパイラ
B言語リファレンス
https://www.nokia.com/bell-labs/about/dennis-m-ritchie/kbman.html
変数に型はなく、ワードサイズだけです。このCPUのアドレスは標準では12ビットなので、そのままポインタにも使えます。
関数のプロトタイプ宣言はなく、各関数内で使う外部の変数・関数をextrn宣言します。
引数のチェックはないので、Cの...のようなことも実現できます。
なんとも大雑把な仕様ですが、(このCPUの)細切れのページを気にしなくて良いことも相まってアセンブラで書くより、はるかに楽ができます。
以下のコンパイラを使ってみました。
https://github.com/clausecker/8bc
遭遇したバグなどを。
左辺が定数、右辺が変数の場合のシフトに不具合があります。左辺も変数だと大丈夫でした。
サブルーチンは、先頭にリターアドレスをストアし、その次の命令から実行する仕様になっています。
ランタイムbrt.palは、リターンアドレスの部分は操作しないので再帰に対応していません。
除算は実装されていません。
エラーメッセージの行番号がおかしい場合、ソースではなくアセンブルリストの方を見るとエラー箇所がわかります。
ポインタに対する[]演算子が正しく動作しないことがありました。
m = -*p & *p;はエラーになるけどm = *p & -*p;は通ったりします。
ではインストールします。
先述のURLからCodeのZIPファイルをダウンロードします。
unzip 8bc-master.zip
cd 8bc-master
make
sudo make install
とします。
エミュレータ&サンプル
https://github.com/kwhr0/emu6100
からCodeのZIPファイルをダウンロードします。
unzip emu6100-master.zip
cd emu6100-master
このディレクトリに含まれるlife.bがSC/MPのときに作ったライフゲームをB言語に書き換えたもの、queen.bが新たに作ったNクイーンです。
Makefileの1行目でlifeかqueenを指定し、makeするとエミュレータをビルドし、Bソースをコンパイルして実行します。
B言語は、グローバル変数だけのBASICに比べて変数にスコープがあるので楽に書けました。
SC/MPのBASICインタプリタでは驚くほど遅かったライフゲームがコンパイラなのでとても速いです。