Skip to content

Instantly share code, notes, and snippets.

@jl2
Created July 7, 2010 05:51
Show Gist options
  • Save jl2/466355 to your computer and use it in GitHub Desktop.
Save jl2/466355 to your computer and use it in GitHub Desktop.
;; Copyright (c) 2010 Jeremiah LaRocco
;; All rights reserved.
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;; 1. Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;; 2. Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in the
;; documentation and/or other materials provided with the distribution.
;; 3. The name of the author may not be used to endorse or promote products
;; derived from this software without specific prior written permission.
;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[BITS 16]
[ORG 0]
jmp start ; Skip data
;; Reboot message
rebooting db 'Reboot',0
;; Input prompt
prompt db 'Enter move.',0
;; 9 empty spaces for the board
game_moves db '123456789'
;; Lame, but simplifies the logic.
;; Indexed by ecx, in reverse, for the next move character
game_order db 'XOXOXOXOX'
win_msg db ' has won!',13,10,0
draw_msg db 'Tie',13,10,0
;; The game board
game_line db ' | | ',13,10,0
sep_line db '-+-+-',13,10,0
;; Ask if they want to play again
exit_msg db 'Play again (y or n)?',13,10,0
;; Winning bitmaps (see check_for_win for details)
matrix_of_win dw 0x0124, 0x01C0, 0x0111, 0x0092, 0x0054, 0x0049, 0x0038, 0x0007, 13, 10, 0
;; Displays the message in si
message: ; Dump ds:si to screen.
push eax
push ebx
inner_message:
lodsb ; load byte at ds:si into al
or al,al ; test if character is 0 (end)
jz done
mov ah,0eh ; put character
mov bx,0008 ; attribute
int 0x10 ; call BIOS
jmp inner_message
done:
pop ebx
pop eax
ret
getkey:
mov ah, 0 ; wait for key
int 016h
ret
reboot:
db 0EAh ; machine language to jump to FFFF:0000 (reboot)
dw 0000h
dw 0FFFFh
start:
mov ax,0x7c0 ; BIOS puts us at 0:07C00h, so set DS accordinly
mov ds,ax ; Therefore, we don't have to add 07C00h to all our data
cli ; clear interrupts while we setup a stack
mov ax,0x9000 ; this seems to be the typical place for a stack
mov ss,ax
mov sp,0xffff ; let's use the whole segment. Why not? We can :)
sti ; put our interrupts back on
;; call detect_cpu ; check if we've got a 386
.386
;; Skip game functions
jmp beginning
;; Use int 0x10 to set video mode and clear screen
clear_screen:
mov ax, 3
int 10h
ret
start_game:
;; Fill game_moves with '123456789'
mov cl, '1'
clear:
mov [game_moves+ecx-'1'], cl
inc ecx
cmp cl, '9'
jle clear
;; Main game loop
mov cl, 9
game_loop:
call show_screen
push edx
call check_for_win
;; dl == 'X' || dl=='O' means somebody won
;; dl == 0 means no win
cmp dl, 0
jne win_found
pop edx
;; cx==0 means there are no more moves
;; Since nobody's won, it's a draw
jecxz game_drawn
;; Get user input
call get_pos
;; Convert user input into an offset
sub dl, '1'
;; Convert current move # into 'X' or 'O'
mov bl, [game_order+ecx-1]
;; Place the 'X' or 'O'
mov [game_moves+edx], bl
dec cl
jmp game_loop
win_found:
;; dl contains token of the winner,
;; so fill in the "win message"
mov [win_msg], dl
pop edx
mov si, win_msg
;; Inform the user
call message
ret
game_drawn:
;; Inform the user of the draw
mov si, draw_msg
call message
ret
show_screen:
;; Paint the TTT screen
push ecx
push eax
push ebx
call clear_screen
xor ecx,ecx
xor eax,eax
top_of_show:
;; game_line contains the ' | | ' template
;; game_moves contains 'X's, 'O's and ' 's for
;; empty spots
;; Fill game_line with 'X's, 'O's and ' 's,
;; one spot at a time
mov bl, [game_moves+eax]
mov [game_line+ecx], bl
inc al
add cl, 2
;; Loop for each spot
cmp cl, 6
jne top_of_show
;; Now display game_line
mov si, game_line
call message
xor cx, cx ; Zero ECX
;; Get si ready in case we have to display '-+-+-',
mov si, sep_line
;; al contains the next index into game_moves
;; sep_line gets displayed before the 4th and 7th
;; game_moves pieces (i.e. it's 0 indexed)
cmp al, 3
je do_seperator
cmp al, 6
je do_seperator
;; No seperator this time
jmp seperators_done
do_seperator:
call message
seperators_done:
;; Check if we're done
cmp al, 9
jne top_of_show
;; Fin
pop ebx
pop eax
pop ecx
ret
get_pos:
;; Get user's input
;; Returns key press in dl
push ax
mov si, prompt
call message
get_pos_get_key:
call getkey
;; Move key to dl
mov dl, al
;; Check for valid move
;; game_moves contains 'X', 'O', or 1..9
;; To detect a valid move use dl - '1' as
;; an index into game_moves
;; This will result in an error if [game_moves+edx-'1']==dl
;; Fortunately that's very unlikely
cmp dl, byte [game_moves+edx-'1']
;; Would be nice to display a message, but there's no space
jne get_pos_get_key
pop ax
ret
check_for_win:
;; Check for a win...
;; Winning bit masks
;; 111000000 - 0x01C0
;; 000111000 - 0x0038
;; 000000111 - 0x0007
;; 100100100 - 0x0124
;; 010010010 - 0x0092
;; 001001001 - 0x0049
;; 100010001 - 0x0111
;; 001010100 - 0x0054
push eax
push ecx
push ebx
;; Check 'X' first
mov ebx, 'X'
;; game_moves contains 'X', 'O' and ' '
;; Create a bitmap from it
;; The first time through, each 'X' is converted to a 1
;; The second time through, each 'O' is converted to a 1
;; Everything else is 0
begin_bitmap:
xor edx, edx
mov ecx, 8 ; Test indexes 0-8
moves_to_num:
shl edx, 1
;; Compare to 'X' or '0'
cmp bl, byte [game_moves+ecx]
;; If it's not 'X' or 'O' don't add a bit
jne cont_mtn
;; Add a bit
inc dx
cont_mtn
;; Loop
dec cl
jnl moves_to_num
;; At this point dx has a 9 bit bitmap indicating which spots
;; were equal to bl
cont_wc:
mov ecx, 8
;; Now compare the bitmap to the known winners
compare_to_winners:
mov ax, word [matrix_of_win+2*ecx]
mov di, dx
;; Bitwise and to get rid of unnecessary bits
and di, ax
;; Then compare
cmp ax, di
;; Jump out early if there's a win
je got_winner
;; Loop
dec cl
jnl compare_to_winners
;; Now do 'O'
cmp bx, 'O'
mov bx, 'O'
jne begin_bitmap
no_winner:
;; Return 0 in dx if there's no winner
xor dx, dx
jmp win_check_cleanup
got_winner:
;; If there's a winner, return it
mov dl, bl
win_check_cleanup:
pop ebx
pop ecx
pop eax
ret
beginning:
call start_game
;; Exit message
mov si, exit_msg
call message
;; Check if they want to play again
exit_check:
call getkey
or al, 0x20 ; Convert to lower case
cmp al, 'y'
je beginning
cmp al, 'n'
jne exit_check
;; Clear screen, display reboot message, and reboot
call clear_screen
mov si, rebooting
call message
call reboot
times 510-($-$$) db 0 ; Fill out the bootsector
dw 0xAA55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment