マイクロプランは、1978年のbit誌(共立出版)のマイクロコンピュータ用プログラム増刊号に、「マイクロプランのプロセッサ」として、戸村哲氏が公表された小さな言語であり、増刊号には、その文法、コンパイラ、ローダ、インタプリタ、コンパイラ初期化用のID表が掲載された。ソースコードは、マイクロプランで記述され、約400行、11kBのサイズであった。これを実際にコンパイルすると、分岐命令、コール命令の参照が未定義で残されたR-Codeが生成される。この未定義参照をR-Codeローダが解決して、Q-Codeに変換する。Q-Codeは、仮想機械Qマシンの機械命令であり、Q-Codeインタプリタが実行する。マイクロプランコンパイラは、最終的に2kBほどのQ-Codeとして実行される。当時、最も小さなTiny BASICが2kBほどであったので、コンパイラとしては非常にコンパクトであった。R-CodeローダとQ-Codeインタプリタは、8080マイコンのアセンブリコードで記述されており、両方合わせた実行コードは1kBほどであったので、4kBのメモリがあれば、自分自身をコンパイルすることができた。ただし、ジャンプ命令、コール命令のオフセットが12bitしかないので、メモリ空間はわずか4kBであった。
2021年の忘年会シーズンに、昔の研究室の先輩、同僚と会話していて、マイクロプランのことを思い出した。私(松井俊浩)は、1978年当時、大学3年生で、自宅で6502マイコンを育てており、このマイクロプランの記事を見て、コレダ!と思い、6502用に改造して実装したのだった。記事に掲載されていたマイクロプランコンパイラのR-Codeの16進ダンプを目と手で打ち込んだ。R-Codeローダ、Q-Codeインタプリタの8080のアセンブリコードを6502のプログラムに書き換えた。結果、小さな6502マイコンで、コンパイラ型の言語が動くようになり、オセロのプログラムを書いたり、ペントミノパズルの解を全部求めたりして勉強した(遊んだ!)。研究室の先輩が、bit増刊号の記事を送ってくれたので、今、これを再現したらどういうことになるかと思った。私は、大学院でプログラミングの授業を担当しており、言語の仕様やそれがコンパイルされる有様を講義するのだが、そこでコンパイラのすごい能力や謎に興味を持つ学生も多い。コンパイラを解剖して中を見せてあげたいが、GCCなどは手に余るほど複雑である。マイクロプランなら、非常に小さいから、言語処理系の講義にも適していると思う。
再現してみると、これを公開したくなる。苦労して戸村哲氏に連絡を取って公開に賛同いただき、その意見にしたがってbit誌の共立出版にも著作権の許可をいただいた。bit誌は、残念ながら2000年頃に廃刊になったが、今は、PDF版が復刻されて、Amazonでも入手可能である。しかし、増刊号は手に入らない!
古い記事をスキャンしてOCRでテキストファイルに直そうとしたのだが、何せ、1978年の7x9のドットプリンタの出力であり、スキャンも不鮮明であったので、得られたテキストファイルには大量の誤りがあった。(と[、)とJ、Oと0、1とl(el) などの見た目での取り間違いだから、人がエディタで見ても、間違いとは気づかない。最後に、シンボル表を出力するプログラムを書いてみたら、GENCODEがGENCOOEになっており、GENCODE1がGENCODElになっていた。表に二つ出てくるからおかしいとわかった。さらにタブコード(0x09)をスペースとは解釈しないようだし、コメントの中に日本語コードが入っていても誤動作するように見受けられた。はっきりいって、OCRなど使わず、人間がソースコードを打ち込む方が早く、間違いがなかったとすら思える。
R-Codeを打ち込む元気はなかったので、コンパイラをCで書き直した。これは簡単だった。さらに、仕様を見てR-CodeローダとQ-CodeインタプリタをCで書き直す。これも簡単だった。これで8-Queenプログラムをコンパイルして実行させることができた。次は、マイクロプランのコンパイラである。前記のように打ち間違いのないコンパイラソースを得るのは苦労した。さて、これで自分自身をコンパイルできるようになった。いくつかエラーがあって、デバッグしたが、一つ、なかなかわからないバグがあった。バグと言うより、仕様の違いである。マイクロプランには、シフト演算子がないので、乗除算を代わりに使っている。#x1234 div 16 は、#x123 になるという仕組みである。ところが、負の数のdiv, mod は、マイクロプランのQ-interpreterと、C言語というかx86では、動作が異なるのだった。すなわち、#xffffe01 div 256 は、#xfffffffe になってほしいのだが、X86では、0xffffffff になる。
コンパイラには、2点、改良が加えてある。1つは、ID表を内蔵させたことである。もともとは、ID表を外部から入力していたので、コンパイルしたいソースコードの前に、すべからくこのID表を書き込んでおく必要がある。これは面倒である。こんな風にID表を分離させてあるのは、ID表の初期化コードが数百バイトになるので、省メモリしたかったのだと思う。確かにメモリ空間を圧迫するが、4kBに収まってコンパイルできるのだから、コンパイラの初期化コードに含めるべきだろう。もう1点は、エントリポイント、Cで言うmain()のアドレスをR-Codeローダが記憶するが、これを不要にして、プログラムの先頭にジャンプ命令を入れて、エントリアドレスに飛ぶようにした。R-CodeローダとQ-Codeインタプリタを別プログラムとして実行するので、その間でエントリアドレスを引き渡すのが面倒だったからだ。Q-Codeインタプリタは、常にゼロ番地から実行を開始すれば良い。
そのほかにQcodeインタプリタには、実行トレースを出力したり、逆アセンブルするユーティリティを付けた。デバッグ用に必要だったのだが、コンパイラの動作理解にも役立つだろう。同時に、シンボル表を出力するようにしている。これもデバッグ用である。
さて、性能はどうか? 記憶では、8bitの6502マイコンで、マイクロプラン・コンパイラをコンパイルするのには、1分くらいかかったのだが、今、AWSのx64で同じようにコンパイルすると、1.5msであった。1万倍以上高速になった!相変わらずメモリ空間は4kBであり、いろいろと改良したくなるが、教育用以外に役に立つことはないだろう。教育用には、シンプルなままがよいだろうということで、このまま公開することにする。