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