Created
July 7, 2010 05:51
-
-
Save jl2/466355 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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