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