COMMENT ^
One evening my 15 year-old son approached me and asked me about writing a
Tic-Tack-Toe game in assembly. I gave him pointers along the way and gave
assistance in finding errors which caused his code to malfunction. The result
is the code which follows this message.
OK optimizers, here is a challenge for you. See just how small you can
reduce this program to. However, reducing calls to procedures is not
considered a valid means of reducing code or increasing program speed! The
calls provide a structured environment and increasing calls to procedures is
considered valid, if justified. Let's face it, no one can hit keys as fast as
assembly can process the instuctions, almost, no matter how sloppy the code is
written!
Keyboard must be locked in caps mode, as only a capital X and a capital O
are recognized by the game(checking for lowercase could be added).
The arrow keys are used to position cursor to the point of where
an X or O is entered (or appropiate characters can be used for program
movement). Entering a B from the keyboard will erase any character being
displayed at the cursor.
Ctrl-c and ctrl-break are suspended because of the interupt used to read
the keyboard.
The spacebar can be used to halt the game at any point, and if a stalemate
occurs it must be used to halt the game. A routine could be added to halt the
game and declare a stalemate.
Game will recognize a horizontal, verticle or diagonal win, and will
declare the character (X or O) which has won. The program also keeps
track of the last character entered(if it was an X or O).
Tasm 2.1 compiles this code to a .com file of 885 bytes in length, as now
written. Have fun!
END COMMENT ^
;TIC-TACK-TOE GAME
;PROGRAMMER: David Z. Ostrom (with assistance from father, Brett
;L. Ostrom)
;DATE PROGRAMMED: 6/10/96
;REVISION HISTORY: NONE
;COPYRIGHT: NONE (FREEWARE!)
;WRITTEN IN TASM IDEAL MODE.
;TO ASSEMBLE AND LINK:
; TASM TIC
; TLINK/T TIC
;THIS PRODUCES THE FILE "TIC.COM"
;
IDEAL
SEGMENT CODE PUBLIC 'CODE'
ORG 100H
ASSUME CS:CODE, ES:CODE, DS:CODE, SS:CODE
GO:
CALL DRIVER
MOV AH, 4CH
INT 21H
EGO_MSG DB 'TIC-TAC-TOE by David Z.Ostrom', '+'
LAST_MSG DB 'LAST MOVE MAKE BY: '
CHAR DB 00H
DB '+'
WIN_MSG DB 'WINNER IS: '
DB '+'
BOX_START DW 1174
ROW DB 10
COLUMN DB 36
WIN_FLAG DB 0
GEN_CTR DB 0
TEMP1 DW 0000h
DATA_ADDR DW 0000h
CTR DB 00H
BOX_TOP DB 0DAH, 0C4H, 0C2H, 0C4H, 0C2H, 0C4H, 0BFH
CELL_SIDE DB 0B3H, 020H, 0B3H, 020H, 0B3H, 020H, 0B3H
DIV_LINE DB 0C3H, 0C4H, 0C5H, 0C4H, 0C5H, 0C4H, 0B4H
BOX_BOTTOM DB 0C0H, 0C4H, 0C1H, 0C4H, 0C1H, 0C4H, 0D9H
SCREEN_ADDR DW 0000H
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC DRIVER NEAR
MOV AX, 0B800H
MOV ES, AX
MOV [ROW], 23
MOV [COLUMN], 79
CALL CLS
CALL EGO_THINGY
CALL DRAW_BOX
MOV [ROW], 10
MOV [COLUMN], 36
CALL POSITION_CURSOR
CALL PROCESS
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC EGO_THINGY NEAR
MOV DI, 102
MOV SI, OFFSET EGO_MSG
NEXT_EGO_CHAR:
CMP [BYTE CS:SI], '+'
JE DONE_EGO
MOV AL, [CS:SI]
MOV [ES:DI], AL
INC SI
INC DI
INC DI
JMP NEXT_EGO_CHAR
DONE_EGO:
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC PROCESS NEAR
TOP:
CALL CHECK_HORZ_WIN
CMP [WIN_FLAG], 1
JE DONE
CALL CHECK_VERT_WIN
CMP [WIN_FLAG], 1
CALL CHECK_DIAG_WIN
CMP [WIN_FLAG], 1
JE DONE
MOV AH, 06H
MOV DL, 0FFH
INT 21H
CMP AL, 20H
JE DONE
CMP AL, 'M'
JE _M
CMP AL, 'K'
JE _K
CMP AL, 'H'
JE _H
CMP AL, 'P'
JE _P
CMP AL, 'X'
JE PRINT_X_
CMP AL, 'O'
JE PRINT_O_
CMP AL, 'B'
JE PRINT_BLANK_
CMP AL, 'B'
JMP TOP
DONE_CHAR:
CALL READ_CHAR
MOV SI, OFFSET LAST_MSG
MOV DI, 0
NEXT_CHAR:
CMP [BYTE CS:SI], '+'
JE TOP
MOV AL, [CS:SI]
MOV [ES:DI], AL
INC SI
INC DI
INC DI
JMP NEXT_CHAR
JMP TOP
DONE:
RET
_M:
CALL M_
JMP TOP
_K:
CALL K_
JMP TOP
_H:
CALL H_
JMP TOP
_P:
CALL P_
JMP TOP
PRINT_X_:
CALL PRINT_X
JMP DONE_CHAR
PRINT_O_:
CALL PRINT_O
JMP DONE_CHAR
PRINT_BLANK_:
CALL PRINT_BLANK
JMP DONE_CHAR
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC CHECK_HORZ_WIN NEAR
MOV [GEN_CTR], 0
MOV SI, 1672
CHECK_NEXT_ROW:
CMP [BYTE ES:SI], 20H
JE HORZ_CHECK_DONE
MOV AL, [ES:SI]
CMP AL, [ES:SI+4]
JNE HORZ_CHECK_DONE
MOV AL, [ES:SI+4]
CMP AL, [ES:SI+8]
JNE HORZ_CHECK_DONE
CALL WINNER
HORZ_CHECK_DONE:
INC [GEN_CTR]
CMP [GEN_CTR], 3
JE FINAL_HORZ_CHECK_DONE
ADD SI, 320 ;INCREMENT ROW
JMP CHECK_NEXT_ROW
FINAL_HORZ_CHECK_DONE:
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC CHECK_VERT_WIN NEAR
MOV [GEN_CTR], 0
MOV SI, 1672
CHECK_NEXT_COLUMN:
CMP [BYTE ES:SI], 20H
JE VERT_CHECK_DONE
MOV AL, [ES:SI]
CMP AL, [ES:SI+320]
JNE VERT_CHECK_DONE
MOV AL, [ES:SI+320]
CMP AL, [ES:SI+640]
JNE VERT_CHECK_DONE
CALL WINNER
VERT_CHECK_DONE:
INC [GEN_CTR]
CMP [GEN_CTR], 3
JE FINAL_VERT_CHECK_DONE
ADD SI, 4 ;INCREMENT COLUMN
JMP CHECK_NEXT_COLUMN
FINAL_VERT_CHECK_DONE:
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC CHECK_DIAG_WIN NEAR
MOV SI, 1672
CHECK_NEXT_RDIAG_ROW:
CMP [BYTE ES:SI], 20H
JE RDIAG_CHECK_DONE
MOV AL, [ES:SI]
CMP AL, [ES:SI+324]
JNE RDIAG_CHECK_DONE
MOV AL, [ES:SI+324]
CMP AL, [ES:SI+648]
JNE RDIAG_CHECK_DONE
CALL WINNER
RDIAG_CHECK_DONE:
MOV SI, 1680
CHECK_NEXT_LDIAG_ROW:
CMP [BYTE ES:SI], 20H
JE LDIAG_CHECK_DONE
MOV AL, [ES:SI]
CMP AL, [ES:SI+316]
JNE LDIAG_CHECK_DONE
MOV AL, [ES:SI+316]
CMP AL, [ES:SI+632]
JNE LDIAG_CHECK_DONE
CALL WINNER
LDIAG_CHECK_DONE:
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC WINNER NEAR
MOV [WIN_FLAG], 1
MOV SI, OFFSET WIN_MSG
MOV DI, 3680
WRITE_NEXT_CHAR:
CMP [BYTE SI], '+'
JE WINNER_DONE
MOV AL, [SI]
MOV [ES:DI], AL
INC SI
INC DI
INC DI
JMP WRITE_NEXT_CHAR
WINNER_DONE:
MOV SI, OFFSET CHAR
MOV AL, [SI]
MOV [ES:DI], AL
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC M_ NEAR
CMP [COLUMN], 40
JB INC_COLUMN
MOV [COLUMN], 36
BACK:
CALL POSITION_CURSOR
RET
INC_COLUMN:
CALL _INC_COLUMN
JMP BACK
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC K_ NEAR
CMP [COLUMN], 36
JA DEC_COLUMN
MOV [COLUMN], 40
BACK1:
CALL POSITION_CURSOR
RET
DEC_COLUMN:
CALL _DEC_COLUMN
JMP BACK1
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC H_ NEAR
CMP [ROW], 10
JA DEC_ROW
MOV [ROW], 14
BACK2:
CALL POSITION_CURSOR
RET
DEC_ROW:
CALL _DEC_ROW
JMP BACK2
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC P_ NEAR
CMP [ROW], 14
JB INC_ROW
MOV [ROW], 10
BACK3:
CALL POSITION_CURSOR
RET
INC_ROW:
CALL _INC_ROW
JMP BACK3
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC PRINT_X NEAR
MOV [CHAR], 'X'
CALL PRINT_CHAR2
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC PRINT_O NEAR
MOV [CHAR], 'O'
CALL PRINT_CHAR2
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC PRINT_BLANK NEAR
MOV [CHAR], 20H
CALL PRINT_CHAR2
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC _INC_COLUMN NEAR
INC [COLUMN]
INC [COLUMN]
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC _DEC_COLUMN NEAR
DEC [COLUMN]
DEC [COLUMN]
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC _INC_ROW NEAR
INC [ROW]
INC [ROW]
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC _DEC_ROW NEAR
DEC [ROW]
DEC [ROW]
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC POSITION_CURSOR NEAR
MOV AH, 02H
MOV BH, 0
MOV DH, [ROW]
MOV DL, [COLUMN]
INT 10H
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC CLS NEAR
MOV AH, 06
MOV AL, 00H
MOV BH, 07H
MOV CH, 0
MOV CL, 0
MOV DH, [ROW]
MOV DL, [COLUMN]
INT 10H
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC PRINT_CHAR NEAR
MOV AH, 02H
MOV DL, [CHAR]
INT 21H
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC PRINT_CHAR2 NEAR
MOV AH, 0AH
MOV AL, [CHAR]
MOV BH, 0
MOV CX, 1
INT 10H
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC READ_CHAR NEAR
MOV AH, 08H ;FUNCTION NO. TO READ CHAR/ARRTIB AT CURSOR
MOV BH, 00H
INT 10H
MOV [CHAR], AL ;AL=CHAR, AH=ATTRIBUTE
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC DRAW_BOX NEAR
MOV AX, [BOX_START]
MOV [SCREEN_ADDR], AX
MOV [DATA_ADDR], OFFSET BOX_TOP
CALL OUT7TOSCREEN
ADD [SCREEN_ADDR], 160
MOV [DATA_ADDR], OFFSET CELL_SIDE
CALL OUT7TOSCREEN
ADD [SCREEN_ADDR], 160
MOV [DATA_ADDR], OFFSET DIV_LINE
CALL OUT7TOSCREEN
ADD [SCREEN_ADDR], 160
MOV [DATA_ADDR], OFFSET CELL_SIDE
CALL OUT7TOSCREEN
ADD [SCREEN_ADDR], 160
MOV [DATA_ADDR], OFFSET DIV_LINE
CALL OUT7TOSCREEN
ADD [SCREEN_ADDR], 160
MOV [DATA_ADDR], OFFSET CELL_SIDE
CALL OUT7TOSCREEN
ADD [SCREEN_ADDR], 160
MOV [DATA_ADDR], OFFSET BOX_BOTTOM
CALL OUT7TOSCREEN
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PROC OUT7TOSCREEN NEAR
MOV DI, [SCREEN_ADDR] ;ADDRESS TO BEGIN SCREEN WRITE
MOV SI, [DATA_ADDR] ;DATA TO BE SENT TO SCREEN
MOV [CTR], 0 ;CTR WILL COUNT CHARS, ADDRESS SCREEN
OUT_ANOTHER:
MOV AL, [CS:SI] ;SINCE THIS IS .COM CS=DS=SS
MOV [ES:DI+CTR], AL ;CTR PROVIDES INCREMENTING OF SCREEN ADDR
INC SI ;TO NEXT DATA BYTE
INC DI ;INC DI PAST SCREEN ATTRIBUTE BYTE
INC DI
INC [CTR] ;CTR IS SERVING A DUAL PURPOSE OF COUNTING
INC [CTR] ;LINES AND IN ADDRESSING THE SCREEN
CMP [CTR], 14 ;NO. OF CHARS PRINTED = CTR/2
JB OUT_ANOTHER ;UNTIL WE HAVE PRINTED 7 CHARS
RET
ENDP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ENDS
END GO