- Joined
- Mar 10, 2019
I wrote a minesweeper knock-off in x86 assembly, but any attempts I've made to modify it lately have just resulted in it hanging, and I'm not sure whether to blame DosBox or my coding skills for that...If you want a true challenge beyond C, x86 or ARM Assembly. I find the lower level languages to be way more interesting than the higher level ones personally speaking. Initially when I was looking into programming for video game hobbyist shit, I was looking at C++ and potentially Rust because of the Bevy Engine. I would eventually take a side quest and started looking into operating systems their order of operations, and eventually x86 Assembly.
Code:
a
call 040B ;_seed
mov ax,000d
int 10 ;Set video mode 0d (320x200x16 graphics)
;Set mouse horizontal min/max position
mov ax,7
mov cx,0000
mov dx,027f ;max horizontal position (0-639 for some reason?)
int 33
inc ax
mov dx,00c7 ;max vertical position
int 33
;Set mouse graphics cursor
inc ax
xor bx,bx
xor cx,cx
mov dx,0568 ;_cursor_masks
int 33
;Display the initial grid
xor cx,cx ;CH=text column 0-39, CL=text row 0-24
;_draw_cell
mov dl,19 ;25 rows
sub dl,[077C] ;_grid_size+1 (height)
shr dl,1 ;divide by 2
mov dh,28 ;40 columns
sub dh,[077B] ;_grid_size+0 (width)
shr dh,1 ;divide by 2
add dx,cx
mov si,05A8 ;_sprite_s
call 0535 ;_draw_sprite
inc ch
cmp ch,[077B] ;_grid_size+0 (width)
jnz 0125 ;_draw_cell
xor ch,ch
inc cl
cmp cl,[077C] ;_grid_size+1 (height)
jnz 0125 ;_draw_cell
mov ax,1
int 33 ;Show mouse cursor
;Check for keypress/mouse click
;_input_loop
mov ax,3
int 33
and bx,3
jnz 0175 ;_handle_click
mov ah,06
mov dl,ff
int 21
jz 0154 ;_input_loop
cmp al,1b
jnz 0154 ;_input_loop
;_quit_to_dos
mov ax,0002
int 10 ;Set video mode 2 (25x80x16 text)
mov ax,4c01
int 21 ;return to DOS
;_handle_click
cmp bl,4
jz 01E8 ;_wait_for_mouseup
;shr cx,4 ;divide by 16
db c1,e9,04
;shr dx,3 ;divide by 8
db c1,ea,03
mov ch,cl ;CH=column
mov cl,dl ;CL=row
mov dl,19 ;25 rows
sub dl,[077C] ;_grid_size+1 (height)
shr dl,1 ;divide by 2
mov dh,28 ;40 columns
sub dh,[077B] ;_grid_size+0 (width)
shr dh,1 ;divide by 2
cmp ch,dh
jb 01E8 ;_wait_for_mouseup
cmp cl,dl
jb 01E8 ;_wait_for_mouseup
sub cx,dx ;CX=click location (CH=column, CL=row)
mov dx,[077B] ;_grid_size+0 (DH=height, DL=width)
cmp ch,dl
jnb 01E8 ;_wait_for_mouseup
cmp cl,dh
jnb 01E8 ;_wait_for_mouseup
test bl,2
jz 01F5 ;_left_click
;jnz 01B3 ;_right_click
;_right_click
mov si,0800 ;_grid_revealed
call 0445 ;_get_cell_data
test al,1
jnz 01E8 ;_wait_for_mouseup
mov si,07C0 ;_grid_flags
call 0472 ;_set_cell_data
mov si,06E8 ;_sprite_f
test al,1
jnz 01C9 ;+2
mov si,05A8 ;_sprite_s
mov dl,19 ;25 rows
sub dl,[077C] ;_grid_size+1 (height)
shr dl,1 ;divide by 2
mov dh,28 ;40 columns
sub dh,[077B] ;_grid_size+0 (width)
shr dh,1 ;divide by 2
add dx,cx
mov ax,2
int 33 ;Hide mouse cursor
call 0535 ;_draw_sprite
mov ax,1
int 33 ;Show mouse cursor
;_wait_for_mouseup
mov ax,3
int 33
cmp bx,0
jnz 01EB ;-2
jmp 0154 ;_input_loop
;_left_click
mov si,0800 ;_grid_revealed
call 0445 ;_get_cell_data
test al,1
jnz 01E8 ;_wait_for_mouseup
mov si,07C0 ;_grid_flags
call 0445 ;_get_cell_data
test al,1
jnz 01E8 ;_wait_for_mouseup
cmp word ptr [077E],-1 ;_cells_remaining
jnz 0267 ;_check_click
;_init_grid
mov di,cx ;DI=click location (column, row)
mov al,[077B] ;_grid_size+0 (width)
mul byte ptr [077C] ;_grid_size+1 (height)
mov [077E],ax ;_cells_remaining
mov bp,ax ;BP=height*width
;_get_random_loc
mov cx,bp
call 03BC ;_rnd_scaled
div byte ptr [077B] ;_grid_size+0 (width) (AH=column, AL=row)
mov bx,ax ;BX=mine location (BH=column, BL=row)
mov cx,di ;CX=click location (CH=column, CL=row)
sub bh,ch
jnb 0231 ;+2
neg bh ;BH=absolute value of BH-CH
sub bl,cl
jnb 0237 ;+2
neg bl ;BL=absolute value of BL-CL
cmp bh,bl
jb 023D ;+2
xchg bh,bl ;place the larger value in BL
cmp bl,1 ;check to see if mine is within 1 cell of click
jbe 021E ;_get_random_loc
mov cx,ax ;CX=mine location (CH=column, CL=row)
mov si,0780 ;_grid_mines
call 0445 ;_get_cell_data
test al,1
jnz 021E ;_get_random_loc
mov si,0780 ;_grid_mines
call 0472 ;_set_cell_data
dec word ptr [077E] ;_cells_remaining
xor ah,ah
mov al,[077D] ;_grid_size+2 (number of mines)
add ax,[077E] ;_cells_remaining
cmp ax,bp
ja 021E ;_get_random_loc
mov cx,di ;CX=click location (CH=column, CL=row)
;jmp 023f ;_check_click
;_check_click
mov si,0780 ;_grid_mines
call 0445 ;_get_cell_data
test al,1 ;Check for mine at click location
jz 0298 ;_count_mines
;_lose
mov ax,2
int 33 ;Hide mouse cursor
;Flag the bomb that exploded so the draw routine won't redraw the sprite
mov si,07C0 ;_grid_flags
call 0472 ;_set_cell_data
mov dl,19 ;25 rows
sub dl,[077C] ;_grid_size+1 (height)
shr dl,1 ;divide by 2
mov dh,28 ;40 columns
sub dh,[077B] ;_grid_size+0 (width)
shr dh,1 ;divide by 2
mov bp,dx ;BP=top left offset
add dx,cx
mov si,0708 ;_sprite_x
call 0535 ;_draw_sprite
jmp 02CA ;_game_over
;_count_mines
call 04A7 ;_reveal_cell
dec word ptr [077E] ;_cells_remaining
jz 02A7 ;_win
dec bp
jl 031F ;_open_board
jmp 01E8 ;_wait_for_mouseup
;_win
mov ax,2
int 33 ;Hide mouse cursor
mov dl,19 ;25 rows
sub dl,[077C] ;_grid_size+1 (height)
shr dl,1 ;divide by 2
mov dh,28 ;40 columns
sub dh,[077B] ;_grid_size+0 (width)
shr dh,1 ;divide by 2
mov bp,dx ;BP=top left offset
;Patch the game over loop to update the memory addresses that need to be
;changed so it flags bombs & prints the win message instead of game over
mov word ptr [2F3], 06E8 ;_sprite_f
mov word ptr [30A], 0772 ;_win_message
;_game_over
;Loop through the whole grid and display the remaining unflagged bombs
xor cx,cx
;_game_over_loop
mov si,07C0 ;_grid_flags
call 0445 ;_get_cell_data
shl al,1
mov dx,ax
mov si,0780 ;_grid_mines
call 0445 ;_get_cell_data
or al,dl
cmp al,2
jnz 02EE ;+6
mov si,0748 ;_sprite_e
mov dx,bp
add dx,cx
call 0535 ;_draw_sprite
jmp 02F7 ;+5
cmp al,1
jnz 02F7 ;+3
mov si,0728 ;_sprite_b
jmp 02E5 ;-7
inc ch
cmp ch,[077B] ;_grid_size+0 (width)
jb 02CC ;_game_over_loop
xor ch,ch
inc cl
cmp cl,[077C] ;_grid_size+1 (height)
jb 02CC ;_game_over_loop
mov dx,0768 ;_lose_message
mov ah,09
int 21 ;display string
mov ah,06
mov dl,ff
int 21
jz 0314 ;-1
cmp al,1b
jnz 0314 ;-3
jmp 016B ;_quit_to_dos
;_open_board
xor cx,cx ;CX=cell location 0,0
;_open_board_loop
mov si,07C0 ;_grid_flags
call 0445 ;_get_cell_data
shl al,1
mov dx,ax
mov si,0800 ;_grid_revealed
call 0445 ;_get_cell_data
or al,dl
cmp al,3
jz 0356 ;_open_cells
inc ch
cmp ch,[077B] ;_grid_size+0 (width)
jb 0321 ;_open_board_loop
xor ch,ch
inc cl
cmp cl,[077C] ;_grid_size+1 (height)
jb 0321 ;_open_board_loop
;No more cells need to be opened
cmp word ptr [077E],0 ;_cells_remaining
jnz 0353 ;+2
jmp 02A7 ;_win
jmp 01E8 ;_wait_for_mouseup
;Open the cells surrounding this one and then toggle the placeholder flag off
;_open_cells
mov dx,cx ;DX=middle cell location
cmp cl,0
jz 035F ;_open_cells_row_increment
dec cl
;_open_cells_row_increment
cmp ch,0
jz 0366 ;_open_cells_loop
dec ch
;_open_cells_loop
cmp cx,dx ;skip the middle cell
jz 037F ;_open_cells_increment
mov si,0800 ;_grid_revealed
call 0445 ;_get_cell_data
test al,1
jnz 037F ;_open_cells_increment
push cx
push dx
call 04A7 ;_reveal_cell
pop dx
pop cx
dec word ptr [077E] ;_cells_remaining
;_open_cells_increment
mov bx,dx ;BX=lower right corner of area to open
inc bh
cmp bh,[077B] ;_grid_size+0 (width)
jnz 038B ;+2
dec bh
inc bl
cmp bl,[077C] ;_grid_size+1 (height)
jnz 0395 ;+2
dec bl
cmp ch,bh
jz 039D ;+3
inc ch
jmp 0366 ;_open_cells_loop
mov ch,dh
cmp cl,bl
jz 03A7 ;+3
inc cl
jmp 035F ;_open_cells_row_increment
mov cx,dx
mov si,07C0 ;_grid_flags
call 0445 ;_get_cell_data
cmp al,0
jz 03B9 ;+3
mov si,07C0 ;_grid_flags
call 0472 ;_set_cell_data
jmp 031F ;_open_board
;Get a random number less than CX. Result is stored in AX.
;_rnd_scaled
mov dx,0001
cmp cx,0
jl 03CD ;+5
shl cx,1
shl dx,1
cmp cx,0
jge 03C4 ;-3
call 03DB ;_rand
cmp ax,cx
jnb 03CD ;-2
mov bx,dx
xor dx,dx
div bx
ret
;Return the next random number in AX.
;_rand
xor bh,bh
mov bl,[0444] ;_lastValues+20
mov ax,[bx+0430] ;_lastValues
add bx,06
cmp bx,14
jl 03F0 ;_next1rand
sub bx,14
;_next1rand
add ax,[bx+0430] ;_lastValues
;Store the new random number (AX) in the last values history
mov bl,[0444] ;_lastValues+20
mov [bx+0430],ax ;_lastValues
;Increment our pointer to the oldest last value
inc bx
inc bx
cmp bx,14
jl 0406 ;_next2rand
sub bx,14
;_next2rand
mov [0444],bl ;_lastValues+20
ret
;Seed the random number generator using the system timer
;_seed
xor ah,ah
int 1a ;Get ticks since midnight in CX:DX
mov [0430],dx ;_lastValues
mov ax,dx
xor bx,bx
;_LCG
mov dx,4e35
mul dx
inc ax
inc bx
inc bx
mov [bx+0430],ax ;_lastValues
cmp bx,12
jl 0417 ;_LCG
or ax,0001 ;LFG needs at least 1 odd initial value
mov [bx+0430],ax ;_lastValues
ret
;_lastValues
dw 0,0,0,0,0,0,0,0,0,0
db 0
;Get the cell data (CH=text column 0-39, CL=text row 0-24, SI=bitmap address)
;Returns a bit in AL
;_get_cell_data
push cx
xor ah,ah
mov al,[077B] ;_grid_size+0 (width)
mov bl,8
div bl
cmp ah,0
jz 0458 ;+3
xor ah,ah
inc al
mul cl
add si,ax
xor ah,ah
mov al,ch
mov bl,8
div bl
xor bh,bh
mov bl,al
mov al,[bx+si]
mov cl,ah
shr al,cl
and al,1
pop cx
ret
;Updates cell data (CH=text column 0-39, CL=text row 0-24, SI=bitmap address)
;Toggles bit at the referenced address; returns new state as a bit in AL
;_set_cell_data
push cx
xor ah,ah
mov al,[077B] ;_grid_size+0 (width)
mov bl,8
div bl
cmp ah,0
jz 0485 ;+3
xor ah,ah
inc al
mul cl
add si,ax
xor ah,ah
mov al,ch
mov bl,8
div bl
xor bh,bh
mov bl,al
mov al,[bx+si]
mov cl,ah
mov ah,1
shl ah,cl
xor al,ah
mov [bx+si],al
shr al,cl
and al,1
pop cx
ret
;Reveals a cell (CH=text column 0-39, CL=text row 0-24)
;Returns the number of adjacent mines in BP
;_reveal_cell
xor bp,bp ;BP=mines adjacent
mov di,cx ;DI=click location
mov cx,-1 ;CH=ff, CL=ff
;_count_mine_loop
mov dx,di ;DX=click location (DH=column, DL=row)
add dh,ch
cmp dh,0
jl 04D8 ;+14
cmp dh,[077B] ;_grid_size+0 (width)
jge 04D8 ;+12
add dl,cl
cmp dl,0
jl 04D8 ;+9
cmp dl,[077C] ;_grid_size+1 (height)
jge 04D8 ;+7
mov si,0780 ;_grid_mines
xchg cx,dx ;CX=position to check, DX=counters
call 0445 ;_get_cell_data
mov cx,dx ;CX=counters
xor ah,ah
add bp,ax
inc ch ;increment column
cmp ch,1
jle 04AE ;_count_mine_loop
mov ch,ff
inc cl
cmp cl,1
jle 04AE ;_count_mine_loop
mov ax,bp ;AX=number of adjacent mines
;shl ax,5 ;multiply by 32 (sprite size)
db c1,e0,05
mov si,05C8 ;_sprite_0
add si,ax ;calculate offset of sprite
mov cx,di ;CX=click location (CH=column, CL=row)
mov dl,19 ;25 rows
sub dl,[077C] ;_grid_size+1 (height)
shr dl,1 ;divide by 2
mov dh,28 ;40 columns
sub dh,[077B] ;_grid_size+0 (width)
shr dh,1 ;divide by 2
add dx,cx
mov ax,2
int 33 ;Hide mouse cursor
call 0535 ;_draw_sprite
mov ax,1
int 33 ;Show mouse cursor
mov si,0800 ;_grid_revealed
call 0472 ;_set_cell_data
;Check if the opened cell had a flag
mov si,07C0 ;_grid_flags
call 0445 ;_get_cell_data
cmp al,0
jz 0529 ;+3
mov si,07C0 ;_grid_flags
call 0472 ;_set_cell_data
cmp bp,0
jnz 0534 ;+4
;Toggle a flag on the revealed cell to indicate that it needs to be opened
mov si,07C0 ;_grid_flags
call 0472 ;_set_cell_data
ret
;Draw a sprite (DH=text column 0-39, DL=text row 0-24, SI=address of sprite)
;Sprites are in EGA (G,B,R,I bitmask) format
;_draw_sprite
mov ax,a000 ;EGA/VGA video RAM segment
mov es,ax
mov al,dl
;shl al,3 ;multiply by 8
db c0,e0,03
mov dl,28
mul dl
mov dl,dh
xor dh,dh
add ax,dx
mov bx,ax ;BX=starting address of sprite
mov dx,03c4
mov al,02
out dx,al ;select EGA map mask register
;DX=port address, AL=EGA bit plane (1=blue, 2=green, 4=red, 8=intensity)
inc dx
mov al,1
out dx,al
mov ah,8 ;AH=counter
mov di,bx ;ES:DI=address in EGA video memory
movsb ;copy byte from DS:SI (sprite RAM) to ES:DI (video RAM)
add di,27 ;move video address to same column of next row
dec ah
jnz 0559 ;-3
shl al,1
cmp al,10
jnz 0554 ;-9
ret
;_cursor_masks
dw 3fff,1fff,0fff,07ff,03ff,07ff,03ff,c3ff
dw c3ff,ffff,ffff,ffff,ffff,ffff,ffff,ffff
dw 0000,4000,6000,7000,7800,7000,1800,1800
dw 0000,0000,0000,0000,0000,0000,0000,0000
;_sprite_s
db fe,fc,fc,fc,fc,fc,80,00,fe,fc,fc,fc,fc,fc,80,00
db fe,fc,fc,fc,fc,fc,80,00,ff,83,83,83,83,83,ff,ff
;_sprite_0
db fe,fe,fe,fe,fe,fe,fe,00,fe,fe,fe,fe,fe,fe,fe,00
db fe,fe,fe,fe,fe,fe,fe,00,01,01,01,01,01,01,01,ff
;_sprite_1
db fe,fe,fe,fe,fe,fe,fe,00,fe,f6,e6,f6,f6,f6,fe,00
db fe,f6,e6,f6,f6,f6,fe,00,01,01,01,01,01,01,01,ff
;_sprite_2
db fe,c6,fa,e6,de,c2,fe,00,fe,fe,fe,fe,fe,fe,fe,00
db fe,c6,fa,e6,de,c2,fe,00,01,01,01,01,01,01,01,ff
;_sprite_3
db fe,fe,fe,fe,fe,fe,fe,00,fe,fe,fe,fe,fe,fe,fe,00
db fe,c6,fa,e6,fa,c6,fe,00,01,01,01,01,01,01,01,ff
;_sprite_4
db fe,ea,da,c2,fa,fa,fe,00,fe,ea,da,c2,fa,fa,fe,00
db fe,fe,fe,fe,fe,fe,fe,00,01,01,01,01,01,01,01,ff
;_sprite_5
db fe,fe,fe,fe,fe,fe,fe,00,fe,c6,de,c6,fa,c6,fe,00
db fe,fe,fe,fe,fe,fe,fe,00,01,01,01,01,01,01,01,ff
;_sprite_6
db fe,e6,de,c6,da,e6,fe,00,fe,fe,fe,fe,fe,fe,fe,00
db fe,fe,fe,fe,fe,fe,fe,00,01,01,01,01,01,01,01,ff
;_sprite_7
db fe,c2,fa,f6,f6,f6,fe,00,fe,c2,fa,f6,f6,f6,fe,00
db fe,c2,fa,f6,f6,f6,fe,00,01,3d,05,09,09,09,01,ff
;_sprite_8
db fe,e6,da,e6,da,e6,fe,00,fe,e6,da,e6,da,e6,fe,00
db fe,e6,da,e6,da,e6,fe,00,01,01,01,01,01,01,01,ff
;_sprite_f
db fe,fc,c4,e4,f4,fc,80,00,fe,fc,c4,e4,f4,fc,80,00
db fe,fc,fc,fc,fc,fc,80,00,ff,83,83,83,83,83,ff,ff
;_sprite_x
db 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
db fe,e6,ee,c6,82,82,c6,00,01,01,01,01,21,01,01,ff
;_sprite_b
db fe,e6,ee,c6,82,82,c6,00,fe,e6,ee,c6,82,82,c6,00
db fe,e6,ee,c6,82,82,c6,00,01,01,01,01,21,01,01,ff
;_sprite_e
db fe,e6,ee,fe,ba,ee,c6,00,fe,e6,ee,fe,ba,ee,c6,00
db fe,e6,ee,fe,ba,ee,c6,00,01,c7,6d,39,39,6d,c7,ff
;_lose_message
db "GAME OVER$"
;_win_message
db "YOU WIN!$"
;Width, height, number of mines (hex)
;_grid_size
;Beginner (8x8, 10 mines)
;db 8,8,a
;Intermediate (16x16, 40 mines)
;db 10,10,28
;Expert (30x16, 99 mines)
db 1e,10,63
;Number of non-mine cells left to uncover (-1=uninitialized, 0=win)
;_cells_remaining
dw ffff
;Mines, flags, and revealed bitmaps
;Each bitmap needs to be at least height * ceil(width / 8) bytes in size
;_grid_mines
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;_grid_flags
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;_grid_revealed
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
rcx
740
nmines.com
w
q
If you pipe that into debug it'll produce a .com