前回のデモのソースコード

前回公開したデモのソースコードを公開。
デバッグ用コードや見た目が地味で没にしたコードもコメント化されて残ってます。

nasm -f bin -o ファイル名.bin ファイル名.asm
でブートセクタ用バイナリを生成
nasm -dMSDOS -f bin -o ファイル名.com ファイル名.asm
でcomファイルを生成

%ifdef MSDOS
	origin equ 0x100
%else
	origin equ 0x7c00
%endif

%if 0
%ifdef MSDOS
	%define DISABLE_VIDEO
%endif
%endif

%ifdef DISABLE_VIDEO
	%define INT0x10
%else
	%define INT0x10 int 0x10
%endif

%ifdef DEBUG
	%macro assert_eq 2
		cmp %1, %2
		je %%skip
		mov ax, __LINE__
		call assert_fail
	%%skip:
	%endmacro
	%macro assert_seg_eq 2
		mov ax, %1
		cmp ax, %2
		je %%skip
		mov ax, __LINE__
		call assert_fail
	%%skip:
	%endmacro
%else
	%macro assert_eq 2
	%endmacro
	%macro assert_seg_eq 2
	%endmacro
%endif

BITS 16		;real mode用のコードを生成.
ORG origin	;プログラムが読み込まれるメモリ位置を指定.

vd_width	equ 320
vd_height	equ 200
vd_area		equ vd_width*vd_height
vd_bpp		equ 1
vd_size		equ vd_area*vd_bpp
ram_segment	equ origin+512/16
stack_begin equ 0x1600					;stackの開始offset.
vd_segment	equ ram_segment+0x1000

position_frac_part equ 6
position_frac equ 64	;2^position_frac_part
speed_frac_part equ 5

start:
	mov ax, ram_segment
	mov ss, ax
	mov sp, stack_begin

	;video mode設定
	;320x200x8bit
	mov ax, 0x13
	INT0x10

	;パレット設定
	xor dx, dx
	mov cx, 0x3f
pallets:
	push cx
;	xchg bx, cx	;xchgのオペランドの一方がax,もう一方がレジスタだと1byteなんだけどな.
	mov bx, cx
	mov dh, bl	;Red
	mov ch, bl	;Green
;	mov cl, bl	;Blue
	mov ax, 0x1010
	INT0x10
	pop cx
	loop pallets

	;internal speakerを初期化する.
	mov al, 182
	out 0x43, al

particles			equ stack_begin ;offset of particles data
bytes_per_particle	equ 2*2*3
					;word position x
					;word position y
					;word speed x
					;word speed y
					;word accel x
					;word accel y
max_num_particles	equ (0x10000-0x1600)/bytes_per_particle
num_particles		equ 0x1000;4992 ;305
particles_size		equ num_particles*bytes_per_particle
%if 0
field_force			equ particles+particles_size
					;word position x
					;word position y
					;field force flag; 0:disable, 1:enable
field_force_size	equ 2+2+1
%endif
average				equ particles+particles_size
old_average			equ average+2
old_beep_freq		equ old_average+2

	;粒子データ初期化の準備
	cld
	mov ax, ram_segment
	mov ds, ax

	mov di, particles
	mov cx, num_particles
	pusha

%if 0
init_particle:
;	mov ax, 4
;	mul cx
	mov ax, cx
	shl ax, 2
	and ax, 0xff
	sal ax, position_frac_part
	mov word [di], ax		;position x
	mov ax, cx
	and ax, 0xffff-0x3f
	shl ax, 3
	mov [di+2], ax		;position y
	xor ax, ax
	mov [di+4], ax		;speed x
	mov [di+6], ax		;speed y
	lea di, [di+bytes_per_particle]
	loop init_particle
%endif

	;粒子の位置とか速度とか加速度は全部0にしとけ.
	mov es, ax
	mov cx, particles_size
	xor ax, ax
	rep stosb

step:
;	mov ax, vd_segment
;	mov es, ax		;esレジスタは常にback surfaceのセグメントを指すようにする.
	push vd_segment
	pop es
	call vd_clear
%if 0
	;set field_force
	mov word [field_force], (vd_width*2/3)*position_frac
	mov word [field_force+2], vd_height/2*position_frac
	xor bl, bl
;	mov ax, [di+2]
;	and al, 255
	xor ah, ah
	int 0x1a	;Real clock count
	and dl, 0x3f 
	jnz	store_field_force
		mov bl, 0x4
	store_field_force:
		mov byte [field_force+4], bl
%endif

	xor ah, ah
	int 0x1a	;クロックカウント読み出し.
	mov fs, dx	;fsは常にクロックカウント値を持つ.

	popa
	assert_eq cx, num_particles
	assert_eq di, particles
	assert_eq sp, stack_begin
	pusha
	;mov cx, num_particles
	;mov di, particles

	mov ax, fs
	shl ax, 3
	and ax, num_particles-1
	sub cx, ax

	;粒子の平均速さもどきを更新.
	xor ax, ax
	xchg ax, [average]
	xchg ax, [old_average]

	for_particles:
%if 0
		push ax
		push ax
		call get_explode_force
		pop ax
		add [di+8], ax
		pop ax
		add [di+10], ax
%endif

		call dynamics

	put:
		push di
		mov dx, (vd_width-1)*position_frac
		call integ_comp
		inc di
		inc di

;床が下から上がってくるやつ
;それほど面白くないのでボツ.
%if 0
		xor ax, ax
		mov bx, fs
	;	add bx, 0x77
		test bl, 0xc0
		jnz .integ
	.mov_wall:
		xchg ax, bx
		and ax, 0x3f
		shl ax, 2

	.integ:
		mov dx, vd_height-1
		sub dx, ax
		shl dx, position_frac_part
%else
		mov dx, (vd_height-1)*position_frac
%endif
		call integ_comp
		pop si
	positioning:
		;vramでの粒子の位置を計算.
		mov ax, [si+2]
		sar ax, position_frac_part
		mov bx, vd_width
		mul bx
		mov bx, [si]
		sar bx, position_frac_part
		add ax, bx
		mov di, ax
		;collision
		mov al, [es:di]
		cmp al, 0x3f
		jne puton
		mov bx, si
		shr bx, 4
		and bx, 0x2
		sub word [bx+si], position_frac ;*2
		;メモリに描画して,後で一気にvramへコピーする.
		;double buffering.
	puton:
		mov al, 0x3f
		stosb
	;	mov byte [bx], 0x3f
		lea di, [si+bytes_per_particle]
		loop for_particles

	call vd_flash

%if 1
	;FPSがべらぼうに高くならないようにwaitする.
	;cx:dx interval in microseconds
	mov ah, 0x86
	xor cx, cx
	mov dx, 0x2000
	int 0x15
%endif

;	call speaker_off
	call music
	jmp short step

;以下にサブルーチンを定義

vd_clear:
	mov ax, fs
	test ax, 0x300
	jnz	.smoke
	;画面を真っ黒にする.
	pusha
	mov cx, vd_area/2
	xor di, di
	xor ax, ax
	rep stosw
	popa
	ret
.smoke:
;画面をクリアしないけど煙たくする.
;各ピクセルについて,下らへんのピクセル値の平均値を設定
	push ds
	push es
	pop ds
	xor bx, bx
	mov cx, vd_height-2
.vert:
	push cx
	mov cx, vd_width ;-1;一番右の垂直ラインがおかしくなるかもしれんが、見た目良ければOK.
.line:
	mov si, cx
	lea si, [bx+si+vd_width]
	lodsb
	shl ax, 8
	lodsb
	add ah, al

	lea si, [si-2+vd_width]
	lodsb
	add ah, al
	lodsb
	add ah, al

	shr ax, 10
	lea di, [si-2-vd_width*2]
	stosb
	loop .line
	add bx, vd_width
	pop cx
	loop .vert
	;一番下のライン2本は黒くしとけ.
	mov cx, vd_width*2
	mov di, bx
	xor al, al
	rep stosb
	pop ds
	ret


;力学計算っぽいことあれこれ.
dynamics:

	;di+10はよく使うからsiに入れてコードサイズ節約.
	lea si, [di+10]
	;渦を作るよ.
	mov ax, fs
	add ax, 0x80
	test ax, 0x100
	jz .skip_vortex
	vortex_force equ 90
	mov bx, vortex_force
;	test ax, 0x80
	neg al
	js .start_vortex
	neg bx
.start_vortex:
	push bx
	mov bx, [di]
	mov dx, [di+2]
	mov ax, bx
	add ax, dx
	cmp bx, dx
;	mov bx, vortex_force
	pop bx
	ja .right_area
	cmp ax, (vd_height-1)*position_frac
	jae .left_bottom
	add word [si], bx;left
	jmp short .break_vortex
.left_bottom:
	add word [di+8], bx
	jmp short .break_vortex
.right_area:
	cmp ax, (vd_width-1)*position_frac
	jae .right_right
	sub word [di+8], bx	;right top
	jmp short .break_vortex
.right_right:
	sub word [si], bx;
	jmp short .break_vortex
.skip_vortex:
	mov byte [si], 24 ;重力を設定.
%if 1
	;地面にぶつかったら飛び跳ねるやつ.
	cmp word [di+2], (vd_height-1)*position_frac
	jb  .break_vortex
;	mov word [di+2], (vd_height-1)*position_frac
;	mov word [di+6], 0
	mov word [si], -5900
%endif

.break_vortex:

	;高速移動
	mov dx, fs
	test dl, 0xff-0x20
;	test dx, 0xff-0x20
	jnz .skip_scatter
	mov ax, [di]
	mov bx, [di+2]
;	sar ax, 2
;	sar bx, 2
	mov [di+4], ax
	mov [di+6], bx
.skip_scatter:

	;回転
	;粒子の速度に垂直な方向へ力を加えれば速度の向きが変わるだろ 物理的に考えて.
%if 1
%if 1
;	mov dx, fs
;	test dl, 0x80
;	jnz .skip_rotation
	neg dl
	jns .skip_rotation
%else
;	cmp byte [old_average+1], 0xef
;	jb .skip_rotation
	test byte [old_average+1], 0x40
	jz .skip_rotation
%endif
	mov ax, [di+4]
	mov bx, [di+6]
	neg bx
	sar ax, 5
	sar bx, 5
	add [di+8], bx
	add [si], ax
.skip_rotation:
%endif

;	mov dx, 2
%if 0
	;矩形コリジョン.
	rect_len_part equ 4
	rect_len equ (1<<rect_len_part)*position_frac

	pusha	;push cx, push di
	mov cx, dx
.collision_detection:
	test word [di], rect_len
	jnz .skip_rect_collision
	inc di
	inc di
	loop .collision_detection
	popa
	pusha
;	mov cx, dx
.avoid_collide:
%if 1
	mov ax, [di]
	mov bx, [di+2]
	add ax, bx
	test ax, rect_len/2
	jnz .upper
;	sub ax, rect_len
	jmp .continue_loop
.upper:
	or ax, rect_len
.continue_loop:
%endif
	and ax, ~(rect_len-1)
	mov [di], ax
;	inc di
;	inc di
;	loop .avoid_collide

%if 0
	or word [di], (rect_len*position_frac)
	or word [di+2], (rect_len*position_frac)
%endif

	xor ax, ax
;	mov word [di+4], ax
	neg word [di+4]
	mov word [di+8], ax
;	mov word [di+6], ax
.skip_rect_collision:
	popa
%endif

	;速度の各成分の絶対値の和を足してくよ.
	pusha
	cmp cx, 0xd0;num_particles-0xd0	;
	jg .skip_average
	mov cx, 2
.comp:
	mov ax, [di+4]
	sar ax, 8
%if 0
	imul al	;速度の2乗だとつまらん音しかでねーよ.
%else
	cmp al, 0
	jnb .not_neg
	neg al
.not_neg:
%endif
	add [average], ax
	inc di
	inc di
	loop .comp
.skip_average:
	popa
	ret

;メインメモリに描画した内容をvramに送る.
vd_flash:
%ifndef DISABLE_VIDEO
	push ds
	push es
	mov cx, vd_area
	xor si, si
	push es
	pop ds
	push 0xa000
	pop es
	xor di, di
	rep movsb
	pop es
	pop ds
%endif
	ret

;加速度から速度を更新し、速度から位置を更新する.
;Δt=1ということにしてΔtを掛けるのは端折る.
;args:
;	di :particle data pointer
;	dx :limit of x or y value
integ_comp:
	;v=v+t*f/m
	;p=p+t*v
	;where m=1, t=1
	lea si, [di+4]	;speed x
	mov ax, [si]
	mov bx, ax		;damping
	sar bx, 6
	sub ax, bx
	add ax, [di+8]	;accel x
	mov word [di+8], 0
	mov [si], ax	;update speed
	mov bx, [di]	;position x
	sar ax, speed_frac_part
	add bx, ax
	jns .check_range
	xor bx, bx
	neg word [si]
.check_range:
	cmp bx, dx
	jbe .store
	mov bx, dx
	neg word [si]
.store:
	mov [di], bx
	ret

music:
%if 0
	mov ax, fs
	and ax, 0x7f
	or  ax, 0x3
	sal ax, 6
%else
	;粒子の平均の速さもどきに比例した周波数の音をだす.
	mov ax, [average]
	neg ax
	add ax, 0xffff
	;音を多少平滑化すれば不快な音もましになるかも・・・.
	add ax, [old_beep_freq]
	shr ax, 1
	mov [old_beep_freq], ax
	or al, 0x1f
%endif
;	call speaker_on
;	ax	:frequency
speaker_on:
	out 0x42, al
	mov al, ah
	out 0x42, al

	in al, 0x61
	or al, 0x3
	out 0x61, al
;	ret
	ret

%if 0
speaker_off:
	in al, 0x61
	and al, 0xfc
	out 0x61, al
	ret
%endif

%if 0
;
;return
; pop :力のx値
; pop :力のx値
get_explode_force:
	mov bp, sp
	pusha
	assert_seg_eq ds, ram_segment
	mov cx, 2
	mov si, field_force
	xor bx, bx
.comp:
	mov ax, [di]	;position of particle
	mov dx, [si]	;position of field force
	sub ax, dx
	sar ax, 8
	push ax
	imul al
	add bx, ax	;bx is |particleの位置-field forceの位置|^2
	inc di
	inc di
	inc si
	inc si
	loop .comp
	shr bx, 8
	inc bx		;0での除算を避ける.
;	or  bx, 32
	mov cl, [field_force+4]
	pop ax
	idiv bl
	imul cx
	mov [bp+4], ax
	pop ax
	idiv bl
	imul cx
	mov [bp+2], ax
	popa
	ret
%endif

%ifdef DEBUG
assert_fail:
	push ax
	mov ax, cs
	mov ds, ax
	mov ah, 9
	mov dx, assert_fail_msg
	int 0x21
	pop ax
	push '$'
	mov bx, 10
	mov di, 3
	mov cx, 4
assert_fail__print_line:
	xor dx, dx
	div bx
	add dx, '0'
	mov byte [assert_fail_line+di], dl
	dec di
	loop assert_fail__print_line
	mov dx, assert_fail_line
	mov ah, 9
	int 0x21
	int 0x20
	ret

assert_fail_msg:
	db 'Assertion failed $'
assert_fail_line:
	db 'xxxx$'
%endif

%ifndef MSDOS
	times 510-($-$$) db 0	;0x1feまでを0で埋めるわけだが,このコードには必要ない!
	db 0x55, 0xaa			;ブートセクタの印.
%endif

OSが無くても動くデモ

前回作ったプログラムのブートセクタ版を公開します。
使用するときは自己責任でお願いします。

B8 20 7C 8E D0 BC 00 16 B8 13 00 CD 10 31 D2 B9 3F 00 51 89 CB 88 DE 88 DD B8 10 10 CD 10 59 E2 F1 B0 B6 E6 43 FC B8 20 7C 8E D8 BF 00 16 B9 00 10 60 8E C0 B9 00 C0 31 C0 F3 AA 68 20 8C 07 E8 70 00 30 E4 CD 1A 8E E2 61 60 8C E0 C1 E0 03 25 FF 0F 29 C1 31 C0 87 06 00 D6 87 06 02 D6 E8 A3 00 57 BA C0 4F E8 48 01 47 47 BA C0 31 E8 40 01 5E 8B 44 02 C1 F8 06 BB 40 01 F7 E3 8B 1C C1 FB 06 01 D8 89 C7 26 8A 05 3C 3F 75 0D 89 F3 C1 EB 04 81 E3 02 00 81 28 40 00 B0 3F AA 8D 7C 0C E2 BD E8 F8 00 B4 86 31 C9 BA 00 20 CD 15 E8 2E 01 EB 89 8C E0 A9 00 03 75 0C 60 B9 00 7D 31 FF 31 C0 F3 AB 61 C3 1E 06 1F 31 DB B9 C6 00 51 B9 40 01 89 CE 8D B0 40 01 AC C1 E0 08 AC 00 C4 8D B4 3E 01 AC 00 C4 AC 00 C4 C1 E8 0A 8D BC 7E FD AA E2 DF 81 C3 40 01 59 E2 D4 B9 80 02 89 DF 30 C0 F3 AA 1F C3 8D 75 0A 8C E0 05 80 00 A9 00 01 74 34 BB 5A 00 F6 D8 78 02 F7 DB 53 8B 1D 8B 55 02 89 D8 01 D0 39 D3 5B 77 0E 3D C0 31 73 04 01 1C EB 21 01 5D 08 EB 1C 3D C0 4F 73 05 29 5D 08 EB 12 29 1C EB 0E C6 04 18 81 7D 02 C0 31 72 04 C7 04 F4 E8 8C E2 F6 C2 DF 75 0B 8B 05 8B 5D 02 89 45 04 89 5D 06 F6 DA 79 13 8B 45 04 8B 5D 06 F7 DB C1 F8 05 C1 FB 05 01 5D 08 01 04 60 81 F9 D0 00 7F 17 B9 02 00 8B 45 04 C1 F8 08 3C 00 73 02 F6 D8 01 06 00 D6 47 47 E2 EC 61 C3 1E 06 B9 00 FA 31 F6 06 1F 68 00 A0 07 31 FF F3 A4 07 1F C3 8D 75 04 8B 04 89 C3 C1 FB 06 29 D8 03 45 08 C7 45 08 00 00 89 04 8B 1D C1 F8 05 01 C3 79 04 31 DB F7 1C 39 D3 76 04 89 D3 F7 1C 89 1D C3 A1 00 D6 F7 D8 05 FF FF 03 06 04 D6 D1 E8 A3 04 D6 0C 1F E6 42 88 E0 E6 42 E4 61 0C 03 E6 61 C3 55 AA


このプログラムをqemuというエミュレータ上で動かす方法とUSBメモリを使ってPC上で直接動かす方法を説明する。

qemuで実行する方法
1, 上記コードをバイナリエディタへコピペする等してファイルに保存。
2, qemu -L . (1で作ったファイル) -soundhw all
(-LでBIOS, VGA BIOS, keymapsファイルのあるディレクトリを指定する。これらはqemuに付属している。-soundhw allでサウンドを有効にする)


・起動用USBメモリを作成して実行する
(PC上で直接動かすので他の方法よりリスクが高いので注意)
http://www.dex4u.com/USBboot.htm
を参考にした方法を使う。
0, 空か消えてもいいデータしか入ってないUSBメモリを用意する
1, 上記ページからHPformatをダウンロードしてインストールする
2, USBメモリを指してHPformatを起動(管理者権限が必要)
3, USBメモリをFAT(FAT32ではない)でフォーマットする
4, 上記ページからHxDをダウンロードしてインストールする
5, HxDを起動(管理者権限が必要)
6, Extrasからremovable disk1を選び"open as readonly"のチェックをはずしてopenする。
7, Sector 0(0~1FF)をすべて上記コードと置き換えます。
8, このまま保存すると起動用USBメモリの完成です。

このUSBメモリを指してPCを起動し,BIOSの画面でfloppy emulationでUSBメモリを起動するように設定。
これでUSBメモリから起動するとデモが動く。

510bytesで何ができるか

gpuppur2008-08-07

PCの電源を入れてからOSが起動するまでをおおまかに説明すると
1, CPUが初期化されてBIOSにあるプログラムが実行される
2, BIOSは設定されたメディア(ハードディスクとかフロッピーディスク等)のブートセクタと呼ばれる最初の512bytesを読み込んでメモリに書き込む
3, メモリに書き込んだ先にジャンプしてそのプログラムを実行する

ここで、ほとんどのOSは512bytesに収まらないのでブートセクタにはさらにメディアからOSを読み込むようなプログラムが置かれている。
そこでブートセクタの512bytesに収まるプログラムの作成に挑戦してみた。
ちなにブートセクタの最後の2bytesには0x55,0xaaがないとブートセクタとは認めて貰えず実行されないので
実質的には510bytesまでしか使えない。

ブートセクタのプログラムが実行されるときはもちろんOSの機能は全く使えないがBIOSに用意された機能を使えば
画面に絵を描いたりすることができるのでメガデモのようなプログラムを作る事にした。


以下に作成したプログラムのバイナリコードを公開する。
実際にOSの無い状態から動かすのは少し面倒なのでまずはcomファイルとして実行できるコードを公開。
このプログラムを実行するのは自己責任でお願いします。
ビープスピーカーから音がでるように作られているので注意。
ms-dos, Windows Vistaより古いwindowsだとそのまま実行できると思うけど
それ以外のOSを使っている人はDOSBox(ms-dosエミュレータ)等を使って実行して下さい。
直接comファイルを実行するのが不安な人やビープスピーカから音がでるのが嫌な人もDOSBoxを使うといいかも。

B8 20 01 8E D0 BC 00 16 B8 13 00 CD 10 31 D2 B9 3F 00 51 89 CB 88 DE 88 DD B8 10 10 CD 10 59 E2 F1 B0 B6 E6 43 FC B8 20 01 8E D8 BF 00 16 B9 00 10 60 8E C0 B9 00 C0 31 C0 F3 AA 68 20 11 07 E8 70 00 30 E4 CD 1A 8E E2 61 60 8C E0 C1 E0 03 25 FF 0F 29 C1 31 C0 87 06 00 D6 87 06 02 D6 E8 A3 00 57 BA C0 4F E8 48 01 47 47 BA C0 31 E8 40 01 5E 8B 44 02 C1 F8 06 BB 40 01 F7 E3 8B 1C C1 FB 06 01 D8 89 C7 26 8A 05 3C 3F 75 0D 89 F3 C1 EB 04 81 E3 02 00 81 28 40 00 B0 3F AA 8D 7C 0C E2 BD E8 F8 00 B4 86 31 C9 BA 00 20 CD 15 E8 2E 01 EB 89 8C E0 A9 00 03 75 0C 60 B9 00 7D 31 FF 31 C0 F3 AB 61 C3 1E 06 1F 31 DB B9 C6 00 51 B9 40 01 89 CE 8D B0 40 01 AC C1 E0 08 AC 00 C4 8D B4 3E 01 AC 00 C4 AC 00 C4 C1 E8 0A 8D BC 7E FD AA E2 DF 81 C3 40 01 59 E2 D4 B9 80 02 89 DF 30 C0 F3 AA 1F C3 8D 75 0A 8C E0 05 80 00 A9 00 01 74 34 BB 5A 00 F6 D8 78 02 F7 DB 53 8B 1D 8B 55 02 89 D8 01 D0 39 D3 5B 77 0E 3D C0 31 73 04 01 1C EB 21 01 5D 08 EB 1C 3D C0 4F 73 05 29 5D 08 EB 12 29 1C EB 0E C6 04 18 81 7D 02 C0 31 72 04 C7 04 F4 E8 8C E2 F6 C2 DF 75 0B 8B 05 8B 5D 02 89 45 04 89 5D 06 F6 DA 79 13 8B 45 04 8B 5D 06 F7 DB C1 F8 05 C1 FB 05 01 5D 08 01 04 60 81 F9 D0 00 7F 17 B9 02 00 8B 45 04 C1 F8 08 3C 00 73 02 F6 D8 01 06 00 D6 47 47 E2 EC 61 C3 1E 06 B9 00 FA 31 F6 06 1F 68 00 A0 07 31 FF F3 A4 07 1F C3 8D 75 04 8B 04 89 C3 C1 FB 06 29 D8 03 45 08 C7 45 08 00 00 89 04 8B 1D C1 F8 05 01 C3 79 04 31 DB F7 1C 39 D3 76 04 89 D3 F7 1C 89 1D C3 A1 00 D6 F7 D8 05 FF FF 03 06 04 D6 D1 E8 A3 04 D6 0C 1F E6 42 88 E0 E6 42 E4 61 0C 03 E6 61 C3

きっちり510(0x1FD)バイトあるはず。
上記16進数のコードをバイナリエディタへコピペするとかして拡張子がcomとなるファイルを作成して下さい。
と思ったら手元にあるバイナリエディタ(BzとStirling)ではテキストからバイナリをコピペして貼り付けできないようだ。

http://www.mh-nexus.de/hxd/
にあるバイナリエディタだとバイナリを直接コピペしてcomファイルとして保存することができた。
このバイナリエディタはハードディスクとかを直接生でいじれるらしい。

ループカウンタのi,j,k

C言語系ではよく以下のようにループカウンタにi, jという名前の変数を使う。

for(int i=0; i<3; ++i)
for(int j=0; j<7; ++j)
{
	printf("%d, %d\n", i, j);
}

今まで外側のループカウンタから順番にi, j, k, ...という変数名を付けていたがこのやり方には欠点がある。


以下のようなループが二つあったとして,

//ループ0
for(int i=0; i<3; ++i)
{
	printf("%d\n", i);
}

//ループ1
for(int i=0; i<7; ++i)
{
	printf("%d\n", i);
	for(int j=0; j<11: ++j)
	{
		printf("-%d\n", i*j);
	}
}


ループ1をループ0の中に入れたくなったとき,以下のようにループ1のi, jをj, kに変更するのは面倒だ。

//ループ2
for(int i=0; i<3; ++i)
{
	printf("%d\n", i);
	for(int j=0; j<7; ++j)
	{
		printf("%d\n", j);
		for(int k=0; k<11: ++k)
		{
			printf("-%d\n", j*k);
		}
	}
}

ループ1を別関数にしてループ0から呼び出すようにすれば,ループ2を書き換えずに済むが
小さなループのために関数を追加するのも気が引けるし,もしループ1が同じスコープ内の大量の一時変数に依存していると
関数に分けるのも面倒になる。

しかし,外側からi,j,kという変数名を使うというルールを無視すれば
ループ1をそのままループ0内にカトペ(cut&paste)してインデントするだけで済む。

//ループ3
for(int i=0; i<3; ++i)
{
	printf("%d\n", i);
	//ループ3-1
	for(int i=0; i<7; ++i)
	{
		printf("%d\n", i);
		//ループ3-2
		for(int j=0; j<11: ++j)
		{
			printf("-%d\n", i*j);
		}
	}
}

しかし上記のループ3-2を今度はループカウンタi,jを使っている2重ループの内側へ移すときには修正が必要になる。

そこで多重ループ内では常にiが現在のスコープのループカウンタ,jが一つ外側のループカウンタ,kがそのまた一つ外側のループカウンタ,
となるようにコードを書くようにする。
例えば以下のようにループを書いておくとループ処理を少しの修正でカトペすることができる。

for(int i=0; i<3; ++i)
{
	printf("%d\n", i);
	for(int j=i, i=0; i<7; ++i)
	{
		printf("%d\n", j*i);
		for(int k=j, j=i, i=0; i<11; ++i)
			printf("%d\n", k+j+i);

		for(int j=i, i=0; i<11; ++i)
			printf("%d\n", j+i);
	}
}

この方法はC++と(たぶん)C99と(普段使わないのでよく知らないが)java, C#等のC言語系でも使えると思う。
だが、この方法でも深いループでずっと外側までのループカウンタが必要になるときにint l=k, k=j, j=i, i=0と書くのは
少し面倒だ。
C++ならテンプレートとかBoostライブラリを駆使すればもう少し短く書けるような気がするので今度考えてみよう。

cygwinにboostインストールメモ

・前準備
boost 1.35.0ではmpiが使えるようなのでcygwinにmpich2をインストールしてみた。
Pythonが入ってないとconfigureで失敗するので入れておく。
普通にソースコードをゲットして
./configure
make
make install
でインストールできた。
自宅に一台しかPCが無いので複数マシンでmpich2が使えるかは不明。

Boost Iostreamsのbzip2で圧縮/解凍するfilterを使うにはlibbz2-develが必要っぽいのでインストールしておく。

・boostをconfigureする。
以下のファイルの改行がCR+LFになっているためにCygwinではそのまま実行できないので
vim等を使って改行コードをLFへ変換する。
configure
tools/jam/src/build.sh
次に
./configure
を実行

次に
user-config.jamに
using mpi : mpicxx ;
でMPI wrapper compilerの名前を指定する。(configureで自動的に見つけてくれない)

Cygwin上のg++ではwide charは使えないので
libs/serialization/build/Jamfile.v2
のSOURCES=の中のcodecvt_nullの行を削除する。

・ビルドする
make

Serializationのwchar_t関連のファイルでコンパイルエラーがでるが他のライブラリがコンパイルされていれば大丈夫だろう。
bin.v2/lib/以下のディレクトリにビルドされた各ライブラリがあるので確認しておこう。

・インストールする。
make install
./configureしたときに--prefixを指定していなければ/usr/local下のinclude,libにインストールされる。

g++でwcharが使えない

http://d.hatena.ne.jp/gpuppur/20080420
でビルドしたgccでは
boost-1.35.0をビルドするときにboost::serializeのwchar_t関連を使っているファイルでコンパイルエラーがでる。


ググったところ
http://d.hatena.ne.jp/y-hamigaki/20070327
iconv.hがないとlibstd++でwchar_tが有効にならないとの事でインストールすることにした。


cygwinに普通にlibiconvをインストールしただけではiconv.hは手に入らないようだ。
そこでsetup.exeでlibiconvのSrcにペケをつけてソースコードをゲットする。
それとcygportとg++もインストールする。
ソースコードは/usr/src下に置かれるので
cygport libiconv-1.11-1.cygport all
ソースコードをビルドし
tar xjvf libiconv-1.11-1.tar.bz2 -C /
でインストールすると/usr/include/iconv.hが置かれる。


その後gccをビルドし直したが症状が変わらない。
wchar.hのwprintfは使えるが
g++ではwprintfを使おうとしても定義してないよとコンパイルエラーがでる。
objdir/i686-pc-cygwin/libstdc++-v3/config.log
を見たところiconvのチェックはyesになっている。
だがwchar_t specializationsはnoになっている。


もう少しよく調べてg++でwchar関連の関数を使えるようにするべきか、
wchar関連の関数を使わないようにboostをビルドすべきか・・・。

cygwinにgcc-4.3.0(CとC++のみ)をインストールする

cygwinのsetup.exeでは少し古いgcc-3.4.4しかインストールできないようなので現在最新の4.3.0をインストールすることにした。
ここにやり方をメモしておく。
きちんとgccをインストールしたい方はgccのドキュメントを参照して下さい。

必要なpackageのリスト

cygwinのsetup.exeで以下のものをインストールしておく。

  • tarとgzipかbzip2(落したファイルを解凍する為。bzip2のほうが圧縮率が高い)
  • gcc
  • make
  • libgmp-devel
  • libgmp3
  • libmpfr-devel
  • libmpfr

download

gcc本家のページからgcc-core-4.3.0.tar.bz2とgcc-g++-4.3.0.tar.bz2をダウンロードし

$ tar xjvf gcc-core-4.3.0.tar.bz2
$ tar xjvf gcc-g++-4.3.0.tar.bz2

これでgcc-4.3.0ディレクトリ下に二つのファイルを解凍したものが置かれる。

configuration

gcc-4.3.0内で作業するのはよろしくないので別のディレクトリ内で作業する。
ここではobjdirを作ってそこで作業する。

$ mkdir objdir
$ cd objdir

次にconfigureする

$ ../gcc-4.3.0/configure --enable-languages=c,c++ --enable-threads=posix --enable-version-specific-runtime-libc

configureするときのオプションはcygwingcc -vで表示されるのと同じものを指定しておけばたぶん大丈夫だと思うが
今回はcとc++さえ使えればよいので必要なものだけ指定しておく。

各オプションの簡単な説明
      • enable-languages=c,c++

使うプログラミング言語を指定する

thread modelを指定する。本家ドキュメントによればruntime libraryと例外処理に影響するらしい。

      • enable-version-specific-runtime-libc

複数versionのgccをインストールする人向けにruntime libraryをインストールするgccディレクトリ化にインストールする。

buildする

make bootstrapでビルドできるらしいが,デフォルトだと'-g -O2'でコンパイルされる。
デバッグ情報は必要無いと思うのでBOOT_CFLAGS='-O2'を指定することにした。
bootstrap-leanだとビルド中に生成するファイルがいらなくなったらすぐに消されるので必要なディスク容量が減るらしい。
quad core CPUを持ってるけどビルド中にブラウザを使いたいのでオプション'-j 3'を指定することにした。
約1時間でbuildできた。

$ make BOOT_CFLAGS='-O2' -j 3 bootstrap

install

最後に

make install

で完了。