问题描述
该程序旨在允许用户使用破折号(“-”和“|”)来形成框,连接 3x3 网格的顶点。我想知道这个程序如何能够在游戏板上标记动作。在 markMove 部分,这个代码块到底发生了什么:
lb $t0,offset($a0) #Pass $a0 which is integer move 0-9 into offset,then load that data into $t0
#Actually mark Now; transfer marker to board through $t1
lb $t1,marker($a0) #Load marker into $t1 after finding corresponding marker
sb $t1,board($t0) #Place the marker at spot in board by storing from $t1
当 $a0 和 $t0 被传递到上面相应的数据类型时,实际发生了什么?感谢您的帮助。
以下是完整的程序供参考。在 MARS 4.5 上使用 MIPS 组装
.data #Data declaration segment
board: .ascii "\n\n . . . . 0 . 1 ."
.ascii "\n 2 3 4"
.ascii "\n . . . . 5 . 6 ."
.ascii "\n 7 8 9"
.asciiz "\n . . . . a . b .\n"
offset: .byte 6,8,33,35,37,62,64,89,91,93,118,120
marker: .byte '-','-','|','-'
.text #Text declaration statement
main:
li $t0,6 #Number of loops/moves to make; For counting loop. Whatever this is set to will determine number of moves to make
jal loop1
#Print game board
li $v0,4
la $a0,board
syscall
#Exit Program
li $v0,10
syscall
loop1:
#Load return address to main onto stack pointer
subu $sp,$sp,4
sw $ra,($sp) #Store $ra into $sp
#Inner loop for loop1 is necessary or else return address to main will be infinitely stored.
inner_loop:
#Read integer for marking excluding a and b
li $v0,5
syscall
move $a0,$v0 #Set $v0 to $a0
jal markMove #Jump to markMove; return address is set in $ra
sub $t0,$t0,1 #Decrement counter by 1
bnez $t0,inner_loop #Return to L1 if counter not 0
##Cut off point for loop
#Once counter is 0,pop return address off stack pointer
lw $ra,($sp)
addu $sp,4
jr $ra
#Mark a move on the game board
#Input : $a0 (Integer move 0-9)
markMove:
##Prepare to pass necessary variables ($v0,$ra) into function.
subu $sp,4 #First must subtract 4 to make space on stack pointer. This must always be done before storing
sw $ra,($sp) #Store $ra into $sp
subu $sp,4 #First must subtract 4 to make space on stack pointer. This must always be done before storing
sw $t0,($sp) #Store $t0 into $sp
lb $t0,then load that data into $t0
##Must Now check if move has been done already
jal checkMove
beq $v0,loop2 #Pass $v0 into loop2... after loop2,if $v0 = 1 (valid); else $v0 = 0 (invalid)
#Actually mark Now; transfer marker to board through $t1
lb $t1,marker($a0) #Load marker into $t1 after finding corresponding marker
sb $t1,board($t0) #Place the marker at spot in board by storing from $t1
#Clear stack pointer ($sp) and return to $ra; $t0 is popped off of $sp; necessary to see if $t0 = 1 or 0 depending on checkMove result
loop2:
##Prepare to take the variables ($t0,$ra) from above.
lw $t0,($sp) #Pop $t0 off of stack pointer
addu $sp,4 #First must add 4 to make space on stack pointer. This must always be done before loading
lw $ra,($sp) #Pop $ra off of stack pointer
addu $sp,4 #First must add 4 to make space on stack pointer. This must always be done before loading
jr $ra #Return to return address; jump register
#Check if a move is valid or not depending on if it has already been done
checkMove:
jr $ra
解决方法
你问的三个指令都是 MIPS 伪指令。
它们的目的是访问用标签声明的全局数组。它们通过命名寄存器中的值对这些全局数组进行索引。例如,
lb $t0,offset($a0)
说,给定全局标签offset
,通过寄存器$a0
中的值索引,然后执行lb
操作——加载字节。获取的字节(符号扩展为 32 位并)存储在 $t0
中。索引是运行时添加,因此 offset
的地址与寄存器 $a0
中保存的值相加,以形成 lb
内存加载操作的有效地址。因此,如果 $a0
保持值 5,那么内存位置 offset+5
处的字节将被加载到 $t0
,假设内存保持初始化状态,它将加载 62
。
在类似硬件的描述语言中,这些 lb
的作用是:
ea <- offset + $a0 # compute intermediate result
$t0 <- SignExtend8To32(Mem[ea]) # where Mem[ea] fetches a byte from memory at addr ea
您引用的其他指令工作相同,另一个 lb
使用不同的标签和相同的寄存器(尽管可能具有不同的实际值 - 您必须运行程序以查看是什么在 $a0
时)。
而 sb
,store byte,指令将索引地址处的字节值存储到内存中,其中索引的计算方法相同。
ea <- offset + $a0 # compute intermediate result
Mem[ea] <- $t0[0:7] # where Mem[ea] is a byte store to memory at address ea
注意:虽然 lb
符号扩展了从内存中获取的字节值(此处为 $t0
),但 sb
仅存储 $t0
的低 8 位注册到内存中。
这种形式的指令称为伪指令。 MIPS 处理器实际上并没有将其作为一条指令——汇编程序将其扩展为两个指令的序列。你可以在模拟器中看到两条指令,组装代码后,如果你检查出来。实际操作使用两条MIPS指令对标签的32位地址进行编码,并在一次内存索引操作中添加到寄存器中。两条指令用于组成 32 位地址 - 一条用于加载标签的完整 32 位地址的高 16 位和低 16 位。
除此之外,如果您想了解更多详细信息,请查看模拟器对这些指令的扩展,并查看 MIPS 绿表以查看各个指令。