FizzBuzzを書こう2

きっかけは「文字なし」・・・

本当はここの日記を書くためにとあるネタをやってました。
・・・が、うまいこといかず難航していてもやもやしてました
そこに、
http://d.hatena.ne.jp/ptr1024/20100612/1276368392
というのが舞い込んできました。
現在は修正がかかってますが最初に投稿されたときは「文字なし」でした。
数字なしではなく文字なしらしいです。
いや、ならやってやろうじゃないか、と。

さすがに完全文字なしは無理なので・・・

C言語で文字なしは無理があるのでなるべく少なくするという方針でいきます。
要はプログラムの大方を数字化してしまうということですね。
・・・
もう予想は付きますね・・・

ということで

完成したものがこれです。
http://codepad.org/R6zyIhNn
どう見ても手抜きですね。
ごめんなさい><;
だってだって夜中の遅い時間に半分ヤケで作ったものなんだよ><
お願い、許して〜
ε二二二ε二二二(。゚つД`)゚。ウワァァ-----ン!!!!

解説

本当はこんなことしてる場合じゃなかったりします・・・
ということで、前回のと合わせて時間がある時にやります><
#これの裏話もそのときに・・・
##覚えてたら、ですが・・・ ←

追記:書きました(10/06/20)

ソース

とりあえずソースをペタペタ。

#include <stdio.h>

unsigned int a[] = {
	1374456661,
	33310151,
	2415919104,
	3910462352,
	0,
	2054842694,
	2054504960,
	1680146554,
	3942648320,
	4232416007,
	4232415552,
	1694268803,
	1166768767,
	57317884,
	2247751513,
	2332914898,
	1788476485,
	4193736965,
	980800133,
	2583446923,
	4149805930,
	1976731129,
	59407,
	2203582464,
	3897576168,
	4294967207,
	2583446923,
	4149806442,
	1976731129,
	59407,
	2203582464,
	3897582056,
	4294967179,
	1979650795,
	59644,
	2203582464,
	3897585896,
	4294967159,
	232,
	3900921856,
	1760055420,
	3925868543,
	4294967161,
	3284779059
};

int main( void )
{
	a[4] = (unsigned int)printf - (unsigned int)&a[5];
	((void(*)())a)();
	return 0;
}

どう見てもバイナリです、本当に(ry

大元のソース

で、バイナリが何やってるか、ですが元のソースを載せます。

#include <stdio.h>

int main( void )
{
	int i = 1;

	__asm {
		nop
		nop
		jmp		START
PRINTF:
		jmp		printf
FIZZ:
		;DB		'Fizz', 00h
		nop
		nop
		nop
		nop
		nop
BUZZ:
		;DB		'Buzz', 00h
		nop
		nop
		nop
		nop
		nop
INTEGER:
		;DB		'%d', 00h
		nop
		nop
		nop
RETURN:
		;DB		'\n', 00h
		nop
		nop
	}

START:
	for( ; i <= 100 ; i++ ) {
		if( !( i % 3 ) || !( i % 5 ) ) {
			if( !( i % 3 ) ) {
				__asm {
					call	FIZZ_PRINT
FIZZ_PRINT:
					pop		eax
					sub		eax, 0x00
					push	eax
					call	PRINTF
				}
			}
			if( !( i % 5 ) ) {
				__asm {
					call	BUZZ_PRINT
BUZZ_PRINT:
					pop		eax
					sub		eax, 0x00
					push	eax
					call	PRINTF
				}
			}
		} else {
			__asm {
				push	i
				call	INT_PRINT
INT_PRINT:
				pop		eax
				sub		eax, 0x00
				push	eax
				call	PRINTF
			}
		}
		__asm {
			call	RET_PRINT
RET_PRINT:
			pop		eax
			sub		eax, 0x00
			push	eax
			call	PRINTF
		}
	}
	return 0;
}

基本的には前回のラムダ式で書いたものと同じです。
ただ、全てのコードをバイナリで済ますために各種データ(printf関数のポインタ、printfに渡すデータ)を埋め込めるように先頭にNOPを入れデータ領域を確保しています。
これをコンパイル機械語を取り出します。
その後、データのために確保した領域(先頭のNOP)の部分を各種データに書き換えれば終了です。
ただし、printfの関数へのポインタは定数値ではないので実行時に書き換えることとします。
これが

	a[4] = (unsigned int)printf - (unsigned int)&a[5];

の行です。
実際にはprintfの関数の位置はコンパイル(リンク)終了後には決まるはずなんだけど、コンパイル時には決まらないからどうしてもこの行を書かなくてはならなくなって残念なことに・・・
ちなみにjmp STARTの前に入っているNOPはunsigned intの境界(4バイト境界)に合わせるためにわざと入れてあります。
これ入れないと2バイトずれるためまた面倒なことに・・・

おわり

またバイナリなことをやってしまった・・・
ちょっとは高級言語的なことをしないと・・・><