#include /*recall-the\ /-good--old-\ /IOCCC-days!\ */<unistd.h> typedef unsigned/*int*/ short U;U(main) [32768],n,r[8]; __attribute__(( # define R(x) A(r[ 7-(n >>x& 7)], (n>> x>>3 )%8) #define C(x) (U*) ((/* |IO| -dpd */char*) main +(x) )/*| |CC| ll*/ # define A(v, i)(i ?i<2 ?C(v ):i\ -4?v+=2, C(i- 6?v- 2:v+ *C(v -2)) :C(v -=2) :&v) /*lian*/ constructor))U( x)(){for(;;*r+= 2,*r+=!n?_exit( write(2,"Illeg" "al ins" "truction ;-" "(\n",24)),0: n>>8==001?( signed char )n*2 :548==n>> 6&&usleep /**/(10 )+n% 64== 4?0* write (r[7 /**/],C( *C(* r)), *C(* r+2) )+4: /**/ n>>9 ==63 &&--r[7-n/ 64%8]?n%+ /**/ 64*- 2:0, n>>6 ==47 ?*R( 0):n>>12==1? *R(0 )=*R (+6) :n>> 12==+ 14?* R(0) -=*R(2*3) :0)n=*C(* r);}
普通に実行してもエラーが出るだけです。
$ gcc -o prog prog.c $ ./prog Illegal instruction ;-(
さて、これはなんでしょうか。
ネタバレ紹介
このプログラムを理解するには、第一回 IOCCC の優勝作品を知っている必要があります。これです。
short main[] = { 277, 04735, -4129, 25, 0, 477, 1019, 0xbef, 0, 12800, -113, 21119, 0x52d7, -1006, -7151, 0, 0x4bc, 020004, 14880, 10541, 2056, 04010, 4548, 3044, -6716, 0x9, 4407, 6, 5568, 1, -30460, 0, 0x9, 5570, 512, -30419, 0x7e82, 0760, 6, 0, 4, 02400, 15, 0, 4, 1280, 4, 0, 4, 0, 0, 0, 0x8, 0, 4, 0, ',', 0, 12, 0, 4, 0, '#', 0, 020, 0, 4, 0, 30, 0, 026, 0, 0x6176, 120, 25712, 'p', 072163, 'r', 29303, 29801, 'e' };http://ioccc.org/1984/mullender/mullender.c
有名なので知っている人も多いかと思います。main が配列になっている素敵なプログラムです。つまり、機械語を直接、数字の配列として定義しています。C 言語の関数ってのは、実装上は機械語の先頭アドレスに対応するシンボルに過ぎないので、こういうことも可能なのです。
ぜひ動かしてみたいと思いますが、この機械語は PDP-11 とかいう 1980 年ごろのマシンで動くので、残念ながら現代では動かせません。*1
そこで冒頭に示したプログラムが使えます。PDP-11 の「なんちゃって」エミュレータです *2 。以下のように実行してください。
$ wget http://ioccc.org/2015/endoh3/prog.c $ wget http://ioccc.org/1984/mullender/mullender.c $ clang -o mullender prog.c mullender.c $ ./mullender
伝説の mullender.c が動きます。やったね。
このプログラムは GCC 拡張の __attribute__( (constructor) ) を使って、main より先に実行をフックします。その中で main 配列の要素を読み出し、PDP-11 のエミュレーションを行う、という仕組みです。
なお、PDP-11 の全命令はサポートしていません。というか、mullender.c が使っている 6 種類の命令しかサポートしてません。なので「なんちゃって」エミュレータ。
戦略と狙い
IOCCC の Rule 1 、「あなたの作品は完全なプログラムでなければならない」に挑戦するというテーマで作りました。過去作品の mullender.c にべったり依存しているので、これは完全なプログラムと言えるのかどうか。しかも mullender.c が使っている命令しかサポートしていないので、他のものはとても動かせない。どやー。
と思って作ったのですが、よく見たら SOB という命令があったのでした。これは 1 引いて非ゼロだったら(後ろ方向限定で)ジャンプするという命令なんですが、実はこの一種類の命令だけでチューリング完全になるというたいへん強力な命令です。なので、やってみたらBrainfuck がエンコードできてしまいました。まあそれはそれでむしろ面白いので、円周率計算とコッホ曲線のプログラムを PDP-11 のマシン語で書いて、おまけとしてつけました。
当初のテーマが達成できたかどうかは微妙ですが、いろんな意味で IOCCC ならではのプログラムになったと思います。過去に類のないプログラムなんで一抹の不安はありましたが、今大会 2 位のポジションにつけたようなのでまずまず。