Another Tic Tac Toe game

Assignment

Your goal is to recreate the game Tic-Tac-Toe in Assembly. That’s it!
This will be a strictly two player game – no AI. Each player (X and O) will place their mark on theboard on alternate turns until one wins, or the game ends in a tie.
Game Rules

Board: 3×3 grid, with all 9 positions initially empty.
Two Players: X and O. X always goes first.
Goal: Each player tries to get 3 of their marks in a row, column, or diagonally. If achieved, the gameends and the player who got the 3 marks lined up wins.
Gameplay: On their turn, each player will place their mark on an empty location on the board, and,unless their move is a winning move (see above), then allow the other player to do the same in the nextturn.
This continues until one player wins or all nine locations are filled, which is considered a draw (tie).

Program guidelines

Name your source file <YourName>_TTT.asm, for example: Jorge_TTT.asm. The compiled executableshould be called simply <YourName>_TTT, for example: Jorge_TTT.
You MUST include your name and email as comments near the beginning of the source file.
You MUST store the game board as a 9-element (contiguous) array in memory and label it board.
For every play you MUST store the mark on the user-supplied board location (9 possibilities) on thememory array (if that board location is still empty) or print an error message “That location is alreadytaken.” if that board location has been used already, and ask for a new location.
You can label the locations in two ways (choose whichever one you prefer):

0 1 2
3 4 5
6 7 8
1 2 3
4 5 6
7 8 9

Start the game proper by asking player X to enter a board location, then player O, and so on until oneof them wins or there is a draw. Upon a win or a draw, the program should end.
After each move (valid or invalid), you MUST redraw the game board. If the move was valid, draw theupdated board. If the move was invalid, print an error message “That location is out of range or alreadytaken.”, draw the board as it is, and ask for input again.
Your program MUST have two functions/routines/labels to print the board: print_boardand
debug_board. print_boardMUST display the board in a human readable way, while debug_boardMUST print the entire linear array contents,to aid you in developing and debugging the program.
The selection of which function to use MUST be done by prompting the user “Do you want to enabledebug information?” at the program’s start. If the first character in the user’s reply is “Y”, “y”, “d”, or“D” then debugging MUST be enabled. Any other response disables debugging.

Output examples:
print_board:
Current board:
|X|
—–
O| |X
—–
|O|
debug_board:
Current board:
012345678
[ X O X O]
Current board (hex):
0 1 2 3 4 5 6 7 8
[20 58 20 4F 20 58 20 20 4F]
Current board (mem):
&board = 0x0100
+offset / hex / ASCII
0x00: 20h
0x01: 58h X
0x02: 20h
0x03: 4Fh O
0x04: 20h
0x05: 58h X
0x06: 20h
0x07: 20h
0x08: 4Fh O
Other program restrictions and tips

Your program MUST compile and run on gl (under the same NASM command lines), so make sure you test it before submitting.
Remember that gl is running in 64-bit mode and on a much more advanced microprocessor than the8086! This means that you may face new challenges (and take advantage of new opportunities) thatwere not addressed in class.

You can use:
– all instructions in the 8086 summary list .pdf (though it’s not really for the 8086) in BlackBoard,but you can use the REP “family”;
– the natural 64-bit extensions to the instructions allowed above;
– only the registers we covered in class/labs, extended to 64-bit, but no R8-R15, etc.;
– only the program sections we covered in class/labs;
– as much data and code memory as you want (and the architecture and OS allows), though you shouldalways try to optimize its usage a bit;
You cannot use:
– any syscall or external library, except the sys_read, sys_write and exit that we used in the labs;
– any assembler or linker command-line optimizations, tricks, or extra features;
– any NASM-included macros;
– any undocumented instructions;
You may use:
– simple macros/defines that will save you some repetitive typing;
– additional/more advanced instructions that provide some advantage over the “normal” ones, but arenot so advanced that make the problem you’re solving too trivial;
– particularly fast/compact/efficient/odd/quirky/funny code or memory constructs provided that they areproperly commented and explained in the report;

Example run of the program:
./Jorge_TTT
Do you want to enable debug information?
n
Player X, choose your location (0-8):
Current board:
| |
—–
| |
—–
| |
0
Player O, choose your location (0-8):
Current board:
X| |
—–
| |
—–
| |
4
Player X, choose your location (0-8):
Current board:
X| |
—–
|O|
—–
| |
6
Player O, choose your location (0-8):
Current board:
X| |
—–
|O|
—–
X| |
1
Player X, choose your location (0-8):
Current board:
X|O|
—–
|O|
—–
X| |
7
Player O, choose your location (0-8):
Current board:
X|O|
—–
|O|
—–
X|X|
8
Player X, choose your location (0-8):
Current board:
X|O|
—–
|O|
—–
X|X|O
3
Player X wins!
Final board:
X|O|
—–
X|O|
—–
X|X|O

Solution

 Ahmed_TTT.asm

 ;   Name:

;   Email:

section .data

msg1db      “Do you want to enable debug information?”,0xA,0xD

len1equ     $-msg1

msg2db      “Player X, choose your location (1-9): “,0xA,0xD

len2equ     $-msg2

msg3db      “Current board:”,0xA,0xD

len3equ     $-msg3

row1db      ” | | “,0xA,0xD

db      “—–“,0xA,0xD

row2db      ” | | “,0xA,0xD

db      “—–“,0xA,0xD

row3db      ” | | “,0xA,0xD

lenbequ     $-row1

dbg1db      ” 012345678 “,0xA,0xD

arr1db      “[         ]”,0xA,0xD

lendequ     $-dbg1

msg5db      “That location is out of range or already taken.”,0xA,0xD

len5equ     $-msg5

msg6db      “The game is a draw!”,0xA,0xD

db      “Final board:”,0xA,0xD

len6equ     $-msg6

msg7db      “Player X wins!”,0xA,0xD

db      “Final board:”,0xA,0xD

len7equ     $-msg7

eoldb      0xA,0xD

eol_lenequ     $-eol

boarddb      0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20

markdb      “XO”

player  db      0

debugdb      0,0

movedb      0,0

section .bss

rdbuffresb    1024    ; 1K read buffer

section .text

global _start

; print_string subroutine

; prints a string using sys_write

; on entry: edx = number of bytes to print

;           ecx = address of string

print_string:               ; defining function print_string

movebx, 1              ; print to stdout

moveax, 4              ; sys write function

int 0x80                ; call kernel

ret                     ; return from subroutine

; read_string subroutine

; reads a string using sys_read

; on entry: edx = number of bytes to read

;           ecx = address of read buffer

read_string:                ; defining function read_string

moveax, 3              ; sys read function

movebx, 2              ; read from stdin

int 0x80                ; call kernel

ret                     ; return from subroutine

read_input:

movecx,rdbuff

mov edx,1024

callread_string

ret

read_int:

callread_input         ; read all input until enter is pressed

movesi,rdbuff

mov ebx,0

mov ecx,10

cnv: movzxeax,BYTE [esi]

incesi

cmp al,10

je  o1

cmp al,’0′

jl  err1

cmp al,’9′

jg  err1

sub al,’0′

xchgeax,ebx

mulecx

xchgeax,ebx

add  ebx,eax

jmpcnv

err1:

mov ebx,-1              ; error reading number

o1: moveax,ebx

ret

read_char:

callread_input

mov  al,[rdbuff]

ret

; print_current_board subroutine

; prints the current board using the board template and the current board

print_current_board:

; fill row 1 of the board template with current board contents

mov edi,row1            ; load row1 address in edi

movesi,board           ; load board address in esi

mov ecx,3               ; load ecx to loop 3 times (3 chars per row)

setrow1:

mov al,[esi]            ; load a char from the board

mov [edi],al            ; save char in the template

incesi                 ; go to next char in board

add edi,2               ; go to next position in template, skipping the column separator

loop setrow1            ; repeat 3 times while ecx is not zero

; fill row 2 of the board template with current board contents

mov edi,row2            ; load row2 address in edi

mov ecx,3               ; load ecx to loop 3 times (3 chars per row)

setrow2:

mov al,[esi]            ; load a char from the board

mov [edi],al            ; save char in the template

incesi                 ; go to next char in board

add edi,2               ; go to next position in template, skipping the column separator

loop setrow2            ; repeat 3 times while ecx is not zero

; fill row 3 of the board template with current board contents

mov edi,row3            ; load row3 address in edi

mov ecx,3               ; load ecx to loop 3 times (3 chars per row)

setrow3:

mov al,[esi]            ; load a char from the board

mov [edi],al            ; save char in the template

incesi                 ; go to next char in board

add edi,2               ; go to next position in template, skipping the column separator

loop setrow3            ; repeat 3 times while ecx is not zero

; print the template board

mov ecx,row1            ; load address of start of template

movedx,lenb            ; load length of board template

callprint_string       ; print it on the screen

ret                     ; return from subroutine

; print_board subroutine

; prints the current board and a message on top of it

print_board:

; print message “Current board”

mov ecx,msg3                ;load address of message in ecx

mov edx,len3                ; load length of message in edx

callprint_string           ; print the message in the screen

callprint_current_board    ; print the current board using the function

ret                         ; return from subroutine

; debug_current_board subroutine

; prints the current debug board

debug_current_board:

; fill array 1 with current board contents

mov edi,arr1+1          ; load address of the debug board template in edi, just after the ‘[‘

movesi,board           ; load address of board in esi

mov ecx,9               ; load ecx to loop 9 times (9 chars in the board)

copyarr1:

mov al,[esi]            ; load a char from the board

mov [edi],al            ; save char in the debug template

incesi                 ; go to next char in board

incedi                 ; go to next char in debug template

loop copyarr1           ; repeat 9 times while ecx is not zero

; print entire debug board template

mov ecx,dbg1            ; load address of start of template

movedx,lend            ; load length of debug board template

callprint_string       ; print it on the screen

ret                     ; return from subroutine

; debug_board subroutine

; prints the current debug board and a message on top of it

debug_board:

; print message “Current board”

mov ecx,msg3                ;load address of message in ecx

mov edx,len3                ; load length of message in edx

callprint_string           ; print the message in the screen

calldebug_current_board    ; print the current debug board using the function

ret                         ; return from subroutine

; is_tie subroutine

; if the board is a tie returns eax=1, otherwise returns eax=0

; on exit:  eax = 0 if is a tie

;                 1 if it’s not a tie

is_tie:

mov eax,0               ; initialize eax to zero to return no tie by default

movesi,board           ; load address of board in esi

mov ecx,9               ; load ecx to loop 9 times (9 chars in the board)

find20:

cmp  BYTE [esi],0x20    ; see if the curren position in the board has a empty space (0x20)

je   nope               ; if so, then the board is not yet full, return no tie

incesi                ; otherwise, increment esi to go to next character in board

loop find20             ; repeat 9 times while ecx is not zero

mov  eax,1              ; if we get here, we have found no spaces on the board so it is full, return tie=1

nope:

ret                     ; return from subroutine

; is_full subroutine

; if 3 positions on the board separated by ebx are the same and are not spaces

; it returns al = repeated mark, otherwise returns al = 0x20 (space)

; On entry:     esi = start address of the characters to compare

;               ebx = separation between the characters to compare

; On exit:      al  = 0x20 if no repeated characters are found or if there was a space

;                     ‘X’  if there were 3 repeated X characters

;                     ‘O’  if there were 3 repeated O characters

is_full:

pushrsi                ; save rsi register on the stack

mov al,0x20             ; set al to 0x20 to return no repeated characters by default

movah,BYTE [esi]       ; load character at given start position into ah

cmp ah,0x20             ; compare loaded character with space

je  noteq               ; if it was a space, exit, there were no repeated chars

addesi,ebx             ; otherwise, go to next character by incrementing the addres by the given separation ebx

cmpah,BYTE [esi]       ; compare the initial character with the character at second position

jnenoteq               ; if they are not equal, exit, there were no repeated chars

addesi,ebx             ; otherwise, go to next character by incrementing the address by the given separation ebx

cmpah,BYTE [esi]       ; compare the initial character with the character at third position

jnenoteq               ; if they are not equal, exit, there were no repeated chars

; if we get here, the initial char was equal to the second and to the third one

; and there were no spaces so ah has the repeated character

moval,ah               ; move it to al to return it as the character repeated 3 times

noteq:

poprsi                 ; restore rsi register from the stack

ret                     ; return from subroutine

; is_win subroutine

; if there is a winner returns the mark for the winner, otherwise it returns 0x20

; On exit:      al  = 0x20 if no 3 repeated characters are found (no winner)

;                     ‘X’  if there were 3 repeated X characters, winner is X

;                     ‘O’  if there were 3 repeated O characters, winner is O

is_win:

; try to find repeated marks in the rows first

mov  ecx,3              ; load ecx to loop 3 times (3 chars per row)

movesi,board          ; load board address in esi

mov  ebx,1              ; compare elements separated by 1

cmprows:                    ; loop through each row

callis_full            ; call is_full to see if three characters in a row are equal

cmp  al,0x20            ; see if it returned 0x20

jnewinret             ; if it wasn’t 0x20, it found a character repeated 3 times in a row, this is the winner

add  esi,3              ; advance to next row

loopcmprows            ; repeat 3 times while ecx is not zero

; try to find repeated marks in the columns

mov  ecx,3              ; load ecx to loop 3 times (3 chars per column)

movesi,board          ; load board address in esi

mov  ebx,3              ; compare elements separated by 3

cmpcols:                    ; loop through each column

callis_full            ; call is_full to see if three characters in a column are equal

cmp  al,0x20            ; see if it returned 0x20

jnewinret             ; if it wasn’t 0x20, it found a character repeated 3 times in a row, this is the winner

add  esi,1              ; advance to next column

loopcmpcols            ; repeat 3 times while ecx is not zero

; try to find repeated marks in the main diagonal

movesi,board          ; load board address in esi

mov  ebx,4              ; compare elements separated by 4

callis_full            ; call is_full to see if three characters in the diagonal are equal

cmp  al,0x20            ; see if it returned 0x20

jnewinret             ; if it wasn’t 0x20, it found a character repeated 3 times in the diagonal, this is the winner

; try to find repeated marks in the lower diagonal

mov  esi,board+2        ; load board address in esi

mov  ebx,2              ; compare elements separated by 2

callis_full            ; call is_full to see if three characters in the diagonal are equal

; after this we simply return the result from is_full

winret:

ret                     ; return from subroutine

; main procedure

_start:

; prompt the user for the debug option

mov ecx,msg1            ;load address of message in ecx

mov edx,len1            ; load length of message in edx

callprint_string       ; print the message in the screen

; read debug input from user

callread_char          ; read the character

mov  [debug],al

cmp   BYTE [debug],’Y’  ; compare the read debug option with Y

jedebugy            ; if the option entered Y, go to debugy

cmp   BYTE [debug],’y’  ; compare the read debug option with y

jedebugy            ; if the option entered y, go to debugy

cmp   BYTE [debug],’D’  ; compare the read debug option with D

jedebugy            ; if the option entered D, go to debugy

cmp   BYTE [debug],’d’  ; compare the read debug option with d

jedebugy            ; if the option entered d, go to debugy

; if we get here, the user entered a character other than Y,y,D,d

mov   BYTE [debug],0    ; move zero to debug since the user select no debugging

jmp   begin             ; jump to start of game

debugy:                     ; if we get here, the user entered Y,y,D or d

mov   BYTE [debug],1    ; move 1 to debug since the user select activate debugging

begin:                      ; start of game

; prompt the user for the position to play

mov ecx,msg2            ;load address of message in ecx

mov edx,len2            ; load length of message in edx

callprint_string       ; print the message on the screen

test BYTE [debug],1     ; see if the debug option was selected by comparing with 1

jnz  dbb1               ; if it’s not zero, debug is enabled, jump to dbb1

callprint_board        ; otherwise use the print_board subroutine to print the current board

jmp  dbb2               ; skip the next instruction to continue

dbb1:

calldebug_board        ; if we get here, use the debug_board subroutine to print the current board

dbb2:

; read position from user

callread_int

cmp eax,1

jl outrange

cmp eax,9

jg outrange

deceax

mov [move],al

; see if the board location is free

movzxebx,BYTE [move]      ; load the position selected by the user into ebx

movedi,board              ; load board address in edi

cmp  BYTE [edi+ebx],0x20    ; compare the selected position on the board with an empty space

jne  outrange               ; if they are not equal the position is taken, go to outrange

; get player mark

movzxecx,BYTE [player]     ; load the current player (0 or 1) into ecx

movesi,mark               ; load the address of the marks into esi

mov  al,[esi+ecx]           ; get the mark for the current player into al

mov  [edi+ebx],al       ; make the move to the selected position by writing the player mark on the board

callis_win             ; see if a player has won the game by calling is_win

cmp  al,0x20            ; see if the subroutine returned a space (no winner)

jnewinend             ; if is not space, it found the winner with mark O or X in al, end the game going to winend

callis_tie             ; see if the game is tied by calling the subroutine is_tie

cmp  eax,1              ; see if the subroutine returned a 1 (tie)

jetieend             ; if it returned 1, the game is a tie, end the game going to tieend

xor  BYTE [player],1    ; if no tie and no winner, change turn by using the xor which inverts either 0 to 1 or 1 to 0

; modify the prompt message to use the current player mark

movzxebx,BYTE [player] ; load the current player (0 or 1) into ecx

movesi,mark           ; load the address of the marks into esi

mov  al,[esi+ebx]       ; get the mark for the current player into al

mov  edi,msg2           ; load address of the prompt message into edi

mov  [edi+7],al         ; change the mark shown in the message to the mark for the current player

jmp  begin              ; go back to the beginning so the new player can make a move

outrange:                   ; we get here when the read position is out of range or the position is taken

; we simply print an error message to indicate the error and then repeat the prompt

mov ecx,msg5            ;load address of message in ecx

mov edx,len5            ; load length of message in edx

callprint_string       ; print message on the screen

jmp  begin              ; repeat from the start to ask for a new position

tieend:                     ; we get here when a tie is found, we simply print the game is a draw, print

; the board and exit the game

mov ecx,msg6            ;load address of message in ecx

mov edx,len6            ; load length of message in edx

callprint_string       ; print the message on the screen

jmplast_board          ; go to last_board to print the last board and exit

winend:                     ; we get here when a winner is found, we simply print the win message

; and the winner mark, print the last board and exit

; first, change the mark of the winner in the win message

mov  edi,msg7           ; load address of win message into edi

mov  [edi+7],al         ; change the mark for the winner in the win message

; print the game is a win and show the mark for the winner

mov ecx,msg7            ;load address of message in ecx

mov edx,len7            ; load length of message in edx

callprint_string       ; print message on the screen

last_board:                     ; print last board

test BYTE [debug],1         ; see if the debug option was selected by comparing with 1

jnz  dbb3                   ; if it’s not zero, debug is enabled, jump to dbb3

callprint_current_board    ; otherwise use the print_board subroutine to print the last board

jmp  endgame                ; skip the next instruction to continue

dbb3:

calldebug_current_board    ; if we get here, use the debug_board subroutine to print the current board

endgame:                    ; end of game, exit to os by calling the kernel

mov eax,1               ; system call number

int 0x80                ; call kernel