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 waht 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

_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(_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 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,cur2
        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,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