Overview
We are going to make a game that
plays a simple game of Tic Tac Toe! This game will not use sprites, unless you
consider a cursor that moves about the board a sprite:) Special thanks to
Robert Senser for providing this program!
Preparation
First I will explain the _grfufclr and _grbufcpy ROM calls. _Grfufclr clears the graph
buffer and all 768 bytes in the plotsscreen are set to zero. The
_grbufcpy will display what is stored in the plotsscreen buffer.
Programming
The program below is long,
but really isn't anything more than we've learned already. Get Ready... Set...
Program!
empty .equ 0
oh .equ 1
ohwin .equ 3 ;oh * 3!
ex .equ 4
exwin .equ 12 ;ex * 3!
false .equ 0
true .equ 1
keyquit .equ $40
keyins .equ $0B
keyup .equ $03
keydn .equ $04
keyrght .equ $01
keyleft .equ $02
keyentr .equ $05
#define B_CALL(xxxx) rst 28h \ .dw xxxx ;We'll only be using B_CALL
_RunIndicOff =4570h
_grbufclr = 4BD0h
_grbufcpy = 486Ah
plotsscreen = 9340h
_getkey = 4972h
_puts
= 450Ah
_putc = 4504h
_newline = 452Eh
_clrlcdfull = 4540h
_clrscrf = 4546h
_homeup = 4558h
appTextSave .equ 1
appFlags .equ 13
textInverse .equ 3
textflags .equ 5
.org $9D95
B_CALL(_RunIndicOff) ; Turning off the Run Indicator
B_CALL(_grbufclr)
; clear the buffer
ld hl,picture ; get address of splash screeen
ld de,plotsscreen ; get address of TI graphic buffer
ld bc,768
; set number of bytes
to copy (96*64)/8
ldir ;
copy the bytes
B_CALL(_grbufcpy)
; bind the graphic image
; set the
flip-flip flag
ld a,false
ld (flag),a
ld (skip),a
; start of game
newgme B_CALL(_getkey)
; wait for a key to be
pressed
cp keyquit
jp z,bye ; exit
newgme2 ld a,empty ;
clear the playing grid
ld (grid0),a
ld hl,grid0
ld de,grid0+1
ld bc,8
ldir
ld a,true ; preset the cursor grid
ld (cur0),a
ld a,false
ld (cur0+1),a
ld hl,cur0+1
ld de,cur0+2
ld bc,8
ldir
;
pick symbol for person and ti
ld a,(flag)
cp true
jr z,new1
ld a,true
ld (flag),a
ld a,ex
ld (person),a
ld a,oh
ld (tisymb),a
ld a,false
ld (skip),a
ld hl,youex
jr main2
new1
ld a,false
ld (flag),a
ld a,oh
ld (person),a
ld a,ex
ld (tisymb),a
ld a,true
ld (skip),a
ld hl,youoh
jr main2
; main processing loop
main ld hl,nomsg
main2
call show ; show the current grid
ld a,(skip)
cp true
jp z,aihere ; let the ti go first
B_CALL(_getkey)
; get the next
key input
cp keyquit
jp z,bye ; exit
cp keyins
jp z,newgme2
cp keyup
jr nz,trydn ; up pushed
call up3
jp main
trydn cp keydn
jr nz,tryrght ; down pushed
call dn3
jp main
tryrght cp keyrght
jr nz,tryleft ; right pushed
call rght3
jp main
tryleft cp keyleft
jr nz,tryentr ; left pushed
call left3
jp main
tryentr cp keyentr
ld hl,badmsg
jp nz,main2 ; enter pushed, other keys ignored
; find the current cursor location
ld hl,cur0
ld b,9
fdloop ld a,(hl)
cp true
jr nz,fdnext
; found cursor location!
ld bc,-9
add hl,bc ; back into the grid
; see if empty
ld a,(hl)
cp empty
jr nz,fderr
; if so, insert symbol
ld a,(person)
ld
(hl),a
call chkwin
cp true
jp z,newgme
jr aihere
fderr ld hl,badloc
jp main2
; CAUTION -- AI at work here!!!
aihere ld a,false
ld (skip),a
call timove
call chkwin
cp true
jp z,newgme
ld a,(flag)
cp true
jr z,fdmsg
ld hl,youoh ;show oh msg
jp main2
fdmsg
ld hl,youex ;show
ex msg
jp main2
fdnext inc hl
djnz fdloop
jp z,main ; something is wrong...
; ignore error
jp main
; main exit point
bye
call clrhme ; clear screen
ld hl,byemsg
B_CALL(_puts)
B_CALL(_newline)
ret ;
return to being a mere calculator
;
clrhme set appTextSave,(iy+appFlags)
; get text shadow too
B_CALL(_clrlcdfull)
; clear lcd
B_CALL(_clrscrfull)
; clear scr
B_CALL(_homeup)
; home the cursor
ret
;TI GAME
STRATEGY
; ti makes a
move
; strategy:
; 1) Can ti
win, if so then do it
; 2) Can
"wet-ware" win, if so then take it the square first
; 3) Can ti
take the center, if so then take it
; 4) Can ti
take a corner, if so then take it
; 5) else, take
an open spot
timove ld b,9
ld hl,grid0
tiloop1
ld a,(hl)
cp empty
jr nz,tinext1
push hl
push bc
ld a,(tisymb)
ld (hl),a
call chkwin
pop bc
pop hl
cp true
jr z,tifnd ; winning move!
ld a,empty
ld (hl),a ; remove trial move
tinext1 inc hl
djnz tiloop1
;check for defensive move
ld b,9
ld hl,grid0
tiloop2
ld a,(hl)
cp empty
jr nz,tinext2
push hl
push bc
ld a,(person)
ld (hl),a
call chkwin
pop bc
pop hl
ld d,a
ld a,empty
ld (hl),a
ld a,d
cp true
jr z,tifnd ; good defensive move!
tinext2 inc hl
djnz tiloop2
;check the center
ld hl,grid0+4
ld a,(hl)
cp empty
jr z,tifnd
;check the corners (and center again)
ld b,5
ld hl,grid0
tiloop4 ld a,(hl)
cp empty
jr z,tifnd
inc hl
; +2 goes corner to corner
...
inc hl
djnz tiloop4
;try anyplace (could optimize, but....)
ld b,9
ld hl,grid0
tiloop5 ld a,(hl)
cp empty
jr z,tifnd
inc hl
djnz tiloop5
jr tiexit
tifnd ld a,(tisymb)
ld (hl),a
tiexit ret
;
check for a winner, return true in a if so, else false
chkwin ld hl,chktab
push hl
pop ix
;table addr
-> ix
;start
outer loop
chkloop ld a,(hl)
;are we done?
cp 0
jr z,chknaw ;yes, then no winner!
inc hl
;get to address
ld e,(hl)
;woaw!!! (hl) -> hl
inc hl
; woaw!!!
ld d,(hl)
; woaw!!!
push de
; woaw!!
pop hl
; de -> hl
ld d,0
ld e,a ;de is increment value
ld b,3
;b is loop count
ld a,0
;a is sum add hl,de
;start inner loop
chk1 add a,(hl)
add hl,de
djnz chk1
cp ohwin
jr nz,chk1n
ld hl,ohwins ; O wins!
jr chkwinr
chk1n cp exwin
jr nz,chkno
ld hl,exwins ; X wins
jr chkwinr
;end inner
chkno push ix
pop hl
; ix -> hl
inc hl
; add 3
inc hl
inc hl
push hl
pop ix
; hl -> ix
jr chkloop ;no win this line
;end outer
; exit outer loop early, crow about success and mark win
chkwinr call show
ld a,true
jr chkbye
chknaw ld hl,grid0
;See about the cat
ld b,9
chklop ld a,(hl)
cp empty
jr z,chkncat
inc hl
djnz chklop
ld hl,catmsg ;Darn cat!
call show
ld a,true
jr chkbye
chkncat ld a,false
chkbye ret
; this routine works by summing values in grid, 8 possible cases
chktab .db 1
;covers the 8 cases!
.dw grid0
.db 1
.dw grid1
.db 1
.dw grid2
.db 3
.dw grid0
.db 3
.dw grid0+1
.db 3
.dw grid0+2
.db 4
.dw grid0
.db 2
.dw grid0+2
.db 0
;end of table marker
; move "cursor" up
up3 ld hl,cur0
ld de,curx
call mov3
ld hl,cur1
ld de,cur0
call mov3
ld hl,cur2
ld de,cur1
call mov3
ld hl,curx
ld de,cur2
call mov3
ret
; move "cursor" down
dn3 ld hl,cur0
ld de,curx
call mov3
ld hl,cur2
ld de,cur0
call mov3
ld hl,cur1
ld de,cur2
call mov3
ld hl,curx
ld de,cur1
call mov3
ret
; helper routine
mov3 ld bc,3
ldir
ret
; move "cursor" right
rght3 ld hl,cur0
call shtr3
ld hl,cur1
call shtr3
ld hl,cur2
call shtr3
ret
; helper routine
shtr3 push hl
ld a,(hl)
inc hl
ld b,(hl)
inc hl
ld c,(hl)
pop hl
ld (hl),c
inc hl
ld (hl),a
inc hl
ld (hl),b
ret
; move "cursor" left
left3 ld hl,cur0
call shtl3
ld hl,cur1
call shtl3
ld hl,cur
call shtl3
ret
; helper routine
shtl3 push hl
ld a,(hl)
inc hl
ld b,(hl)
inc hl
ld c,(hl)
pop hl
ld (hl),b
inc hl
ld (hl),c
inc hl
ld (hl),a
ret
;
show the tic-tac-toe grid on the screen
show push hl
; keep possible message
call clrhme ; clear/home screen
ld hl,grid0
; process row 0
call showrow
ld hl,dashs
B_CALL(_puts)
B_CALL(_newline)
ld hl,grid1
; process row 1
call showrow
ld hl,dashs
B_CALL(_puts)
B_CALL(_newline)
ld hl,grid2
; process row 2
call showrow
pop hl
; process message if there
ld a,(hl)
cp 0
jr z,showby
push hl
B_CALL(_newline)
B_CALL(_newline)
pop hl
B_CALL(_puts)
showby ret
showrow
push hl
call showspot ; show first spot
ld a,(bar)
B_CALL(_putc)
pop hl
inc hl
push hl
call showspot ; show 2nd spot
ld a,(bar)
B_CALL(_putc)
pop hl
inc hl
call showspot ; show 3rd spot
B_CALL(_newline)
ret
; show the symbol at (hl), and handle cursor
showspot
push hl
; need this later
ld bc,9
add hl,bc
ld a,(hl)
; load cursor flag
pop hl
cp false
; res textInverse,(iy+textflags)
jr z,shownch
set textInverse,(iy+textflags)
shownch ;
load actual item in spot
ld a,(hl)
cp empty
jr nz,shownot1
ld a,(space)
jr showgun
shownot1
cp oh
jr nz,shownot2
ld a,(leto)
jr showgun
shownot2
ld a,(letx)
showgun bcall(_putc)
res textInverse,(iy+textflags)
ret
; these are really variables
grid .equ *
grid0 .db empty,empty,empty
grid1 .db empty,empty,empty
grid2 .db empty,empty,empty
; this must be 9 bytes past grid0 or else it will fail
cur0 .db true,false,false
cur1 .db false,false,false
cur2 .db false,false,false
curx .db 0,0,0
person .db 0
tisymb .db 0
flag .db 0
skip .db 0
;
basically, these are constants
nine .db 9
space .db "
"
leto .db "O"
letx .db "X"
bar .db "|"
dashs .db "-----",0
; youmsg .db "Your
move!",0
youex .db "Your
move 'X'!",0
youoh .db "Your
move 'O'!",0
ohwins .db "**
O ** Wins!",0
exwins .db "**
X ** Wins!",0
catmsg .db "* Cat got it *",0
nomsg .db 0
badmsg .db "Invalid
key!",0
badloc .db "Place taken!",0
byemsg .db "Game
over.",0
picture:
#include "title.asm" ; graphic image from file poets.asm
.end
END
This is the title.asm screen that will be displayed at the start of the program, start a new text file and copy this into it. Don't try to copy it by hand, it won't help you unless you know what it is. Name the text file "title.asm". Then make sure it is in the same folder as the Tic Tac Toe game when you assemble it.
title.asm
.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
.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,
.db
0,0,0,0,0,0,0,0,0,0,0,1,254,192,31,224,0,31,224,0,0,0,0,1,254,0,31,224,0,31,224
.db
0,0,0,0,0,48,207,3,7,207,3,7,143,0,0,0,0,48,223,3,15,223,3,15,223,128,0,0,0,48,216
.db
3,12,216,3,12,217,128,0,0,0,48,216,3,12,216,3,12,219,128,0,0,0,48,216,3,12,216,3,12,216,0
.db
0,0,0,48,223,3,14,223,3,15,223,0,0,0,0,48,207,3,6,207,3,7,143,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
.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
.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
.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
.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
.db
0,0,0,0,0,0,0,0,0,3,192,0,0,0,1,128,0,0,0,0,0,1,32,0,0,0,0,128,0,0,0
.db
0,0,3,96,0,0,0,1,128,0,0,0,0,0,2,108,198,97,172,145,38,72,0,0,0,0,3,149,40,128,82
.db
145,73,72,0,0,0,0,6,51,236,195,247,179,159,216,0,0,0,0,4,34,4,68,165,35,16,144,0,0,0,0
.db
4,34,68,68,164,194,146,96,0,0,0,0,30,115,187,135,237,134,220,207,192,0,0,0,0,0,0,0,1,0,0
.db
128,0,0,0,0,0,0,0,0,2,0,1,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
.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
.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
.db
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,12,0,0,0,0,0,0,0,0,0,0,6
.db
18,0,0,0,0,0,0,0,0,0,0,6,54,0,0,0,0,0,0,34,107,60,214,4,36,0,0,0,0,0,0
.db
34,149,69,41,4,36,0,0,0,0,0,0,61,252,111,123,12,108,0,0,0,0,0,0,41,8,42,82,8,72,0
.db
0,0,0,0,0,17,40,42,82,8,72,0,0,0,0,0,0,49,221,219,182,63,112,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
.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
Conclusion
What do you think? Cool eh'?
Pressing the "on" key seems to put the game into greyscale for some
reason, oh well. If you think the title screen was cool, you can make them by
using Bill Nagel's Pic83. Click here for instructions.
This will be the last tutorial in this version of TI-83 Plus Asm tutorials v1.01. Don't worry though, if you finish this tutorial early, look out for version 2.01 very soon (2 weeks).
Click to return to the site's menu... or here to get back to the tutorial's menu.