Tutorial 15
We're going to make a game !!!

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).

Tutorial 16

 

    Click to return to the site's menu... or here to get back to the tutorial's menu.