【问题标题】:How does passing into label in this MIPS Assembly program work?在这个 MIPS 汇编程序中传递到标签是如何工作的?
【发布时间】:2021-04-06 21:44:27
【问题描述】:

此程序旨在允许用户将破折号('-' 和 '|')放入框,连接 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, $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, $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, $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, offset($a0) #Pass $a0 which is integer move 0-9 into offset, then load that data into $t0
    
    ##Must now check if move has been done already
    jal checkMove
    beq $v0, 0, 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, $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, $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

【问题讨论】:

    标签: assembly parameter-passing mips cpu-registers mars-simulator


    【解决方案1】:

    你问的三个指令都是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 使用不同的标签和相同的寄存器(尽管可能具有不同的实际值 - 您必须运行程序才能查看 @ 中的内容987654336@当时)。

    sb,存储字节,指令将索引地址处的字节值存储到内存中,索引的计算方式相同。

    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 位,另一条用于加载标签的完整 32 位地址的低 16 位。

    除此之外,如果您想了解更多详细信息,请查看模拟器对这些说明的扩展,并查看 MIPS 绿皮以查看各个说明。

    【讨论】:

    • 谢谢!真的很棒和彻底的回应。需要花一些时间来分析你所说的,但我会回来的......
    • 好的,我阅读了所有内容并理解了,除了一点:为什么 offset+5 会是 62?我假设这是因为 offset 或 offset+0 自动是存储的第一个索引,对吗?
    • 是的,offset == offset+0,指的是用.byte 6 初始化的内存地址。下一个地址offset+1指的是用.byte 8初始化的内存地址,以此类推。
    • 太棒了!谢谢!
    猜你喜欢
    • 1970-01-01
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多