読者です 読者をやめる 読者になる 読者になる

fizzbuzz.gif こたえ

http://b.hatena.ne.jp/entry/http://d.hatena.ne.jp/shinichiro_h/20081011%231223722332

http://d.hatena.ne.jp/shinichiro_h/comment?date=20081011#c

やった解答してくださる方がいた!

それはともかく答えは

  • "Fizz Buzz" と書かれた GIF (末尾に 3B ゴミがついているという意味で少し不正)
  • Ruby (1.8) で書かれた Fizz Buzz
  • Perl で書かれた Fizz Buzz
  • ゴルフ場の z80 で書かれた Fizz Buzz
  • x86 & MS-DOS の COM で書かれた Fizz Buzz
  • x86 & PC/AT互換機MBR に書くと Fizz Buzz が走る起動イメージ (だから 512B)

でした。最後のは

qemu -boot c -hda fizzbuzz.gif

で確認しました。パーティションテーブルにまともなデータ入ってないので、実際に使うとチェックとかある BIOS だと動かんかったりするかも。ぶっ壊してもいい MBR が今無いことに気付く。いやあるけど動かすのはだるい。

COM で BIOSシステムコール呼べると気付いて、 x86 の二者でコードが共有できちゃって、なんかだいぶ空間が余ってしまったので、 z80FizzBuzz を適当に書いてつっこんだらちょうど良かった。

作った手順としては

横幅 34/35/39 pixels のどれか、という条件で GIF ファイルを作る。 34 だとマージンが非対称になっちゃうので 35 を使った。まぁ # が一番扱いやすいとはいえる。 Ruby/Perl で見ると、

GIF89a#バイナリ〜

という感じになる。 GIF89a は undefined symbol だけどパーサ的には OK なので、後で BEGIN{hogehoge; exit} することで実行しないようにすれば大丈夫。まぁ Ruby/Perl は簡単。

GIF の global color table に不正なインストラクションがあった。白を表現するための 0xffffff の真ん中なので調整が効かぬ。

> objdump -D -m i8086 -b binary fizzbuzz.gif
...
   f:   00 ff                   add    %bh,%bh
  11:   ff                      (bad)
  12:   ff 21                   jmp    *(%bx,%di)

しょうがないので global color table を binspect で local color table にうつしかえた。でも今度は comment extension label + comment block のサイズの部分で

   e:   fe                      (bad)
   f:   11 43 72                adc    %ax,0x72(%bp,%di)

となってたので、最初の comment block のサイズを 2B にした。そしたら valid 。

   e:   fe 02                   incb   (%bp,%si)
  10:   eb 04                   jmp    0x16

で、その 2B のコメントの内容は、上記の通り x86 のコード部分に jump するコードにしといた。 0x10 と 0x16 の間は次のコメントブロックのサイズ情報と、あと z80 コードへの jump を埋めておいた。 z80 はここまで有害な命令が無かった。まぁゴルフ場仕様の z80 で有害な命令なんて HALT とジャンプ系くらいだろう。

で次は x86 のコード。難しい工夫はない。スタックポインタが GIF header の 'a' の部分で popa が呼ばれて狂ってるので、 DOS 窓で異常終了しちゃうのを防ぐためにスタックポインタ戻しておいたのと、あとメモリに置かれる位置が DOS だと 0x100 で起動イメージだと 0x7c00 とかなので、 call で IP 取得するよくあるテクニックでデータ置いてある位置を調整したくらい。

次は z80 。なんの工夫もないあんまゴルフされてないコード。

次はデータ。

cnt:
        db  "00", 13, 10
fizz:
        db  "Fizz", 13, 10
fzbz:
        db  "Fizz"
buzz:
        db  "Buzz", 13, 10

とかそんなかんじ。これは x86z80 で共有。

このへんは Ruby とか Perl で valid なので特に escape とかもせず。 RubyPerl から見ると

GIF89a#バイナリ〜
Fizz
FizzBuzz
;'\x8a';BEGIN{m=#=;print+(Fizz)[$_%3].(Buzz)[$_%5]||$_,$/for 1..100;'
1.upto(?d){|n|puts ["Fizz#{s=[:Buzz][n%5]}"][n%3]||s||n}#';
exit}
__END__

とかになってるだけ。このあたりで comment block のサイズが足りなくなったので、 '\x8a' で次の comment block に送った。今考えるとエスケープせずに comment block とコードのダブルミーニングの方がかっこよかったな。

あとは comment extension を終わって、 local color table つきの image descriptor があって、で GIF の data が入る。 gimp が作ったまんまなので工夫は無し。このへんが MBRパーティションテーブルとかぶってるという話。そんで起動イメージの magic number つっこんで終わり。

        resb 512 - 2 + $$ - $
        dw  0xaa55

これのせいで GIF 末尾にゴミがついちゃうのは避けられないかなぁ、と思う。いや、 513B とかにしたらいいんだろうけど、まぁそれもなぁと。あと 512B 以上にしていいなら bmp でもできる。最初は bmp でやろうと思ってたんだけどね。

感想としてはやっぱメモリ保護とか無い世界は自由だなぁということと、 512B って広いなぁと。

最後にソースコード。これの隙間に z80 コードと Ruby コードと GIF データをつめました。

http://shinh.skr.jp/obf/fizz_gif.asm

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h