Assignment

Assembly Programming

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, 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:

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. You can add extra debug information to thisfunction.
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.
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

You can use:
– all instructions in the 8086 summary list .pdf (though it’s not really for the 8086) in BlackBoard,
– 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 

;

; Assembly program that recreates a Tic-Tac-Toe game between two players

;

;

SECTION .data

board       db  32,32,32,32,32,32,32,32,32       ; game board initialized to spaces

playerdb  ‘X’

debugdb  0

askdbgdb “Do you want to enable debug information?”,10

lenaskdbgequ $-askdbg

playermsgdb “Player ”

lenplyrmsgequ $-playermsg

promptdb “, choose your location (0-8):”,10

lenpromptequ $-prompt

currentmsgdb “Current board:”,10

lencurmsgequ $-currentmsg

winmsgdb ” wins!”,10

lenwinmsgequ $-winmsg

finalmsgdb “Final board:”,10

lenfinalmsgequ $-finalmsg

errormsgdb  “Invalid board location, please try again.”,10

lenerrmsgequ $-errormsg

occupiedmsgdb  “The position is occupied, please try a different one.”,10

lenoccmsgequ $-occupiedmsg

drawmsgdb  “The game is a draw!”,10

lendrawmsgequ $-drawmsg

nldb  10

player1db  ‘X’

player2db  ‘O’

dbg1db  ” 012345678 “,10

lendbg1equ  $-dbg1

dbg2        db  ”  0  1  2  3  4  5  6  7  8 “,10

lendbg2equ  $-dbg2

par1db  ‘[‘

par2db  ‘]’

currentmsg2db “Current board (hex):”,10

lencurmsg2  equ $-currentmsg2

currentmsg3db “Current board (mem):”,10

lencurmsg3  equ $-currentmsg3

brdaddrdb  “&board = ”

lenbrdaddrequ $-brdaddr

listmsgdb  “+offset / hex / ASCII”,10

lenlistmsgequ $-listmsg

hexprefixdb “0x”

sep1db “: ”

sep2db “h ”

SECTION .bss

bufferresb  10

SECTION .text

global _start:

_start:

movrsi,askdbg          ; prompt the user for activating debug information

movrax,lenaskdbg

call print

callread_char

cmpal,’Y’              ; activate debugging info when the user enters Y,y,d or D

je  setDebug

cmpal,’y’

je  setDebug

cmpal,’d’

je  setDebug

cmpal,’D’

jnemain_loop

setDebug:

inc BYTE [debug]        ; set debug to 1

main_loop:

callprint_player        ; print prompt

movrsi,prompt

movrax,lenprompt

call print

movrsi,currentmsg

movrax,lencurmsg

call print

cmp  BYTE [debug],0          ; select board print based on debug state

jneprdebug

callprint_board

jmpreadpos

prdebug:

calldebug_board

readpos:

callread_char           ; read the position

cmp  al,’0′

jl   error

cmp  al,’8′

jg   error

sub al,’0′              ; convert from ascii to integer

movrbx,rax

mov al,[player]

movrsi,board

mov ah,[rsi+rbx]        ; see if the location was occupied

cmp ah,32

jne  occupied

mov [rsi+rbx],al        ; save move in the board

callcheck_winner        ; see if someone won or if there’s a tie

cmp al,0                ; if no winner and no tie, continue

je  continue

cmp al,1

je  tiedGame

jmpwonGame

continue:

callchange_turn         ; change player’s turn

jmpmain_loop            ; continue playing

error:

movrsi,errormsg

movrax,lenerrmsg

call print

callprint_nl

jmpmain_loop

occupied:

movrsi,occupiedmsg

movrax,lenoccmsg

call print

callprint_nl

jmpmain_loop

tiedGame:

movrsi,drawmsg

movrax,lendrawmsg

call print

movrsi,finalmsg

movrax,lenfinalmsg

call print

jmplastboard

wonGame:

callprint_player

movrsi,winmsg

movrax,lenwinmsg

call print

movrsi,finalmsg

movrax,lenfinalmsg

call print

lastboard:

cmp  BYTE [debug],0          ; select board print based on debug state

jneprdbg

callprint_board

jmp  exit

prdbg:

calldebug_board

exit:

mov rax,1               ; sys_exit

mov rbx,0

int 80h

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print one or more characters to stdout

; rsi = base address of characters to print

; rax = number of chars to print

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print:

pushrax                ; preserve registers used by the procedure

pushrbx

pushrcx

pushrdx

movrdx,rax

movrcx,rsi

mov rax,4

mov rbx,1               ; print to stdout

int 80h                 ; use sys_readsyscall to print to the screen

poprdx                 ; restore used registers

poprcx

poprbx

poprax

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print a new line

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_nl:

pushrax

pushrsi

mov rax,1

movrsi,nl

call print

poprsi

poprax

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print a character

; On entry:

;   al = character to print

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_char:

pushrax

pushrsi

mov [buffer],al

movrsi,buffer

mov rax,1

call print

poprsi

poprax

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to read one characters from stdin

; On exit:

;   rax = read character

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

read_char:

pushrbx                ; preserve registers used by the procedure

pushrcx

pushrdx

pushrdi

mov rsi,0

movrdi,buffer

rdloop:

movrax, 3              ; read the selected debug state

movrbx, 2              ; read from stdin

movrcx, rdi

movrdx, 1              ; read 1 byte

int 80h                 ; use sys_writesyscall to read input from the user

cmp BYTE [rdi],10       ; see if it was an enter

je  rdend

incrsi                 ; read only one char, discard every other char until an enter is found

cmp rsi,1

jgrdloop

incrdi

jmprdloop

rdend:

mov rax,0               ; return read character in rax

mov al,[buffer]

poprdi

poprdx                 ; restore used registers

poprcx

poprbx

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print the current player

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_player:

pushrax

pushrsi

movrsi,playermsg      ; print “Player”

movrax,lenplyrmsg

call print

mov  al,[player]

callprint_char         ; print player character

poprsi

poprax

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to change the player

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

change_turn:

pushrax

moval,BYTE [player1]       ; if the current player is player1

cmp BYTE [player],al

jne chg2

moval,BYTE [player2]       ; change player to player2

mov [player],al

jmpchgdone

chg2:

moval,BYTE [player1]       ; if the current player is player2

mov [player],al             ; change player to player1

chgdone:

poprax

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print the current board

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_board:

movrsi,board           ; point to board with rsi

movrdi,buffer

mov BYTE [rdi],’|’

mov rax,0x2d2d2d2d2d    ; save five consecutive dashes ‘-‘

mov [rdi+1],rax

mov rax,1

mov rcx,3

prloop:

call print              ; print the current board char

incrsi

xchgrsi,rdi            ; use di to print the separator

call print              ; print the separator

xchgrsi,rdi            ; restore rsi

call print              ; print the current board char

incrsi

xchgrsi,rdi            ; use di to print the separator

call print              ; print the separator

xchgrsi,rdi            ; restore rsi

call print              ; print the current board char

incrsi

callprint_nl

cmp  rcx,1              ; don’t print a dash row at the end

je   skip

xchgrsi,rdi            ; use di to print a dash row

incrsi

mov  rax,5              ; print 5 chars

call print              ; print the separator

decrsi

xchgrsi,rdi            ; restore rsi

mov  rax,1              ; print 1 char next

callprint_nl

skip:

loopprloop

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to see if any player has won or if the

; game is a draw.

; On exit: al = 0 for no event

;          al = 1 for tied game (draw)

;          al = ‘X’ if the X player has won

;          al = ‘O’ if the O player has won

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

check_winner:

movrsi,board

mov rbx,0               ; we will check the three rows

chkrow:

mov al,[rsi+rbx]

cmp al,32               ; if it’s a space

je  nxtrow

mov ah,[rsi+rbx+1]

cmpal,ah

jnenxtrow

mov ah,[rsi+rbx+2]

cmpal,ah

je  return

nxtrow:

add rbx,3               ; go to next row

cmp rbx,9               ; if we haven’t go beyond the last row, continue

jlchkrow

mov rbx,0               ; we will check the three columns

chkcol:

mov al,[rsi+rbx]

cmp al,32               ; if it’s a space

je  nxtcol

mov ah,[rsi+rbx+3]

cmpal,ah

jnenxtcol

mov ah,[rsi+rbx+6]

cmpal,ah

je  return

nxtcol:

incrbx                 ; go to next col

cmp rbx,3               ; if we haven’t go beyond the last col, continue

jlchkcol

chkdiag1:                   ; check first diagonal

mov al,[rsi]

cmp al,32               ; if it’s a space

je  chkdiag2

mov ah,[rsi+4]

cmpal,ah

jne chkdiag2

mov ah,[rsi+8]

cmpal,ah

je  return

chkdiag2:                   ; check second diagonal

mov al,[rsi+2]

cmp al,32               ; if it’s a space

je  chktie

mov ah,[rsi+4]

cmpal,ah

jnechktie

mov ah,[rsi+6]

cmpal,ah

je  return

chktie:                     ; no wins, let’s check tie

mov rcx,9

cmpspc:

mov al,[rsi]

incrsi

cmp al,32

je  notie

loopcmpspc

mov al,1                ; tie, all places on the board are filled

ret

notie:

mov al,0                ; no win, no tie

return:

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print the board as a linear array

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_linear_board:

mov rsi,dbg1

mov rax,lendbg1

call print

mov al,[par1]

callprint_char

movrsi,board           ; point to board characters

mov rax,9

call print

mov al,[par2]

callprint_char

callprint_nl

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print a hexadecimal digit

; On entry:

;   al= digit to print, only the 4 lowest bits are used

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_hex:

pushrax

and al,0xF

cmp al,9        ; if the number is above 9, print as a letter

jghx

add al,’0′      ; convert from 0-9 to ascii ‘0’-‘9′

jmppx

hx: sub al,10       ; convert from 10-15 to A-F

addal,’A’

px:

callprint_char

poprax

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print a character as hexadecimal

; On entry:

;   al= character to print

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_hex_char:

pushrax

pushrcx

movah,al

mov cl,4

shral,cl       ; move upper 4 bits downwards

callprint_hex

moval,ah       ; restore old number

callprint_hex

poprcx

poprax

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print a 32 bit address as hexadecimal

; On entry:

;   eax= address to print

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_hex_address:

pushrax

pushrcx

mov rcx,4

movebx,eax

pbytes:

shld eax,ebx,8  ; shift upper 8 bits into eax

shl  ebx,8      ; shift ebx to uptate it

callprint_hex_char

looppbytes

poprcx

poprax

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print the board as a linear hex array

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_linear_hex_board:

mov rsi,dbg2

mov rax,lendbg2

call print

mov al,[par1]

callprint_char

mov rcx,9

movrsi,board           ; point to board characters

prhex:

mov al,[rsi]

incrsi

callprint_hex_char

cmp  rcx,1              ; if it’s the last number, don’t print a space

jenospc

mov  al,32              ; print a space

callprint_char

nospc:

loopprhex

mov al,[par2]

callprint_char

callprint_nl

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print the board as a list of addresses

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

print_list_hex_board:

movrsi,brdaddr

movrax,lenbrdaddr

call print

movrsi,hexprefix

mov rax,2

call print

movrax,board          ; print the board address

callprint_hex_address

callprint_nl

movrsi,listmsg

movrax,lenlistmsg

call print

mov rcx,9

mov rbx,0

movrdi,board           ; point to board characters

prline:

movrsi,hexprefix

mov rax,2

call print

moval,bl

callprint_hex_char

mov rsi,sep1

mov rax,2

call print

mov al,[rdi]

callprint_hex_char

mov rsi,sep2

mov rax,2

call print

mov al,[rdi]

callprint_char

callprint_nl

incrdi

incrbx

loopprline

ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Procedure to print the debug board

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

debug_board:

callprint_linear_board

mov rsi,currentmsg2

mov rax,lencurmsg2

call print

callprint_linear_hex_board

mov rsi,currentmsg3

mov rax,lencurmsg3

call print

callprint_list_hex_board

ret