#assembly #mips
#сборка #mips
Вопрос:
- Эй, ребята, у меня проблема с пониманием поведения моей программы. Я пытаюсь сохранить адрес для возврата, но он продолжает каким-то образом перезаписываться. Это домашнее задание, так что не давай мне простых ответов! Возможно, просто объяснение того, как
jal
можно было бы перезаписать мой$s0
реестр. Я не совсем понимаю, как это возможно. Я помещаю комментарии в код, чтобы вы могли видеть, где происходит цикл.
2.
.data # data segment for variables and strings
# These are all the strings that will need to be outputted to the console
title: .asciiz "Tic-Tac-Toe!nn"
oprompt: .asciiz "O player: Please enter row/col, one per line:nn"
xprompt: .asciiz "X player: Please enter row/col, one per line:nn"
invposstr: .asciiz " is an invalid position. Try again.nn"
owinstr: .asciiz "O won the game,nn exiting. . ."
xwinstr: .asciiz "X won the game,nn exiting. . ."
catstr: .asciiz "CAT game,nn exiting. . ."
border: .asciiz "nn _ _ _ n"
# Initialze the board with 9 bytes
board: .byte 'X','X','X','X','X','_','X','_','_'
.text # text segment for instructions
main: # begin the main func
li $v0, 4 #set v0 to print string
la $a0, title #set string to print (in $a0) as title
syscall #execute
li $s1, 0 #use s1 as the game counter (up to 9 turns)
playerx: #player x will always go first
li $v0, 4 #prepare to print string
la $a0, xprompt #load the get move prompt
syscall
jal get_valid_move #go get a valid move address
# I am having trouble returning here
li $t0, 'X' #load asciiz code for X
sb $t0, ($v0) #store the X into the board address
addi $s1, 1 #increment the game counter to next turn
jal print_board #after getting the move, print the board status
# And here
playero: #player o will always go second
li $v0, 4 #prepare to print string
la $a0, oprompt #load the get move prompt
syscall
jal get_valid_move #go get a valid move address
# And here
li $t0, 'O' #load asciiz code for O
sb $t0, ($v0) #store the O into the board address
addi $s1, 1 #increment the game counter to next turn
jal print_board #after getting the move, print the board status
# And here
j playerx #go back to player x turn
# I store the current return address below (first time)
print_board: #function to print the status of the board
move $s0, $ra #save the recent return address
la $t1, board #load the board address
li $v0, 4 #set v0 to print string
la $a0, border #set string to print (in $a0) as prompt
syscall #execute
li $t0, 0 #use as counter for loop
loop: #use loop to print 1 row at a time
li $v0, 11 #prepare to print char
li $a0, 124 #asciiz code for |
syscall
lb $a0, ($t1) #load next byte from board
li $v0, 11 #prepare to print char
syscall
addi $t1, $t1, 1 #increment address of board to next byte/char
addi $t0, $t0, 1 #increment loop counter
beq $t0, 3, rowend #go print special stuff at end of row
beq $t0, 6, rowend #go print special stuff at end of row
beq $t0, 9, rowend #go print special stuff at end of row
j loop #go back to top
rowend:
li $a0, 124 #asciiz for |
li $v0, 11 #prepare to print char
syscall
li $a0, 10 #ascizz for newline
li $v0, 11 #prepare to print char
syscall
blt $t0, 9, loop #unless at the last row, loop
li $a0, 10 #ascizz for newline
li $v0, 11 #prepare to print char
syscall
jr $ra #return to the gameplay in main
entry_address: #function to transform user input (row, col) into board address
move $t0, $a1 #make a copy of row for use
move $t1, $a2 #make a copy of col for use
addi $t0, $t0, -1 #use n-1 for accessing storage address
addi $t1, $t1, -1 #use n-1
mul $t0, $t0, 3 #mul the row by 3
la $v0, board #load base address of the board
add $v0, $v0, $t0 #add the row offset
add $v0, $v0, $t1 #add the col offset
#v0 now holds the correct board position address
jr $ra #now go back to check if valid move
valid_position:
li $v0, 1 #assume valid
blt $a1, 1, inv #if row is less than 1
bgt $a1, 3, inv # or if row is greater than 3
blt $a2, 1, inv # or if col is less than 1
bgt $a2, 3, inv # or if col is greater than 3 change validity
jr $ra #go back to get_valid_move at last instruction
inv:
li $v0, 0 #since branched, change to inv
jr $ra #go back to get_valid_move at last instruction
# I store the current return address into s0 below (the 2nd time)
get_valid_move:
move $s0, $ra #save the recent return address
li $v0, 5 #prepare to read row int
syscall
move $a1, $v0 #copy the read int into argument to be passed
li $v0, 5 #prepare to read col int
syscall
move $a2, $v0 #copy the read int into argument to be passed
jal valid_position #go make sure pos is valid
beq $v0, 0, invalid #go tell user if valid_position returned false
jal entry_address #position was good so go get entry_address
# THE JR STATEMENT BELOW IS LOOPING BACK HERE
# I need to return to the last instruction in main which I thought I stored in $s0
li $t0, '_' #use underscore for comparison
lb $t1, ($v0) #load the byte from the entry_address
bne $t0, $t1, invalid #if entry is taken, get another move
jr $s0 #since move was valid go back to gameplay in main
invalid:
li $v0, 1 #prepare to print int
move $a0, $a1 #load row numb
syscall
li $v0, 11 #prepare to print char
li $a0, 44 #load comma asciiz
syscall
li $v0, 1 #prepare to print int
move $a0, $a2 #load col numb
syscall
li $v0, 4 #prepare to print string
la $a0, invposstr #load invalid pos message
syscall
j get_valid_move #repeat the user input loop
check_all_match:
print_winner_and_exit:
row_winner:
col_winner:
diag_winner:
check_for_win:
# After printing all the results, we can exit the program
exit:
li $v0, 10 # set v0 to exit
syscall # execute exit
Комментарии:
1. У меня такое чувство, что мне нужно начать реализацию стека для моих обратных адресов. В противном случае, возможно, более простым решением было бы прикрепить ярлыки в main, чтобы я мог просто переходить к ним напрямую, когда мне нужно. Какое решение было бы более эффективным / правильным?
2. Стек является более гибким и может использоваться везде одинаково. Если вы когда-нибудь захотите использовать реальные подпрограммы, это будет правильный путь. Но если вы знаете, куда идет каждый переход, вам не нужна гибкость.
3. Я реализовал стек. Мой код, похоже, выполняется в одношаговом режиме точно так, как задумано. Однако при полном запуске я застреваю в том же цикле. Как это вообще возможно?
4. Неважно! Я не открывал стек после восстановления обратного адреса! Черт возьми, это всегда самая маленькая вещь.