【问题标题】:How do I deal with three or more values being stored in stack pointer for MIPS Assembly?如何处理存储在 MIPS 程序集堆栈指针中的三个或更多值?
【发布时间】:2021-04-05 18:53:00
【问题描述】:

该程序旨在允许用户将破折号('-' 和 '|')放入框,连接 3x3 网格的顶点。

因此,如果您愿意,可以将其称为初学者汇编程序员问题,但我在堆栈指针上存储三个或更多值时遇到问题。我想通过从堆栈指针中弹出返回地址从loop1返回到main;但是,在 checkMove 中,堆栈指针被添加了两次。

如何解决我的问题,以便我可以从 loop1 返回到 main?

    .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
    jal loop1
    
    #Print game board
    li $v0, 4
    la $a0, board
    syscall
    
    #Exit Program
    li $v0, 10
    syscall
    
loop1:
    #Load return address onto stack pointer
    subu $sp, $sp, 4 
    sw $ra, ($sp) #Store $ra into $sp
    
    #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, loop1 #Return to L1 if counter not 0
    
    #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.
    
    #Push return address ($ra) onto stack pointer
    subu $sp, $sp, 4 
    sw $ra, ($sp) #Store $ra into $sp
    #Push $t0 onto stack pointer
    subu $sp, $sp, 4

    sw $t0, ($sp) #Store $t0 into $sp
    
    lb $t0, offset($a0)
    
    #Actually mark now
    lb $t1, marker($a0) #Find the marker
    sb $t1, board($t0) #Place the marker at spot in board

#Clear stack pointer ($sp) and return to $ra
loop2:
    ##Prepare to take the variables ($v0, $ra) from above.
    lw $t0, ($sp) #Pop $t0 off of stack pointer
    addu $sp, $sp, 4
    lw $ra, ($sp) #Pop $t0 off of stack pointer
    addu $sp, $sp, 4
    
    jr $ra #Return to return address; jump register

【问题讨论】:

    标签: assembly return mips cpu-registers mars-simulator


    【解决方案1】:

    您的问题是您在每次从用户那里读取数字的迭代中推送$ra(根据您的注释代码,6 次循环/移动)。

    每次输入一个新函数/过程时,你应该只存储一次返回地址,并在从它返回之前恢复它。

    因此,您可以在保存上下文并在控制循环中使用它之后添加另一个标签。在这个例子中我使用了inner_loop:

    ...
    loop1:
        #Load return address onto stack pointer
        subu $sp, $sp, 4 
        sw $ra, ($sp) #Store $ra into $sp
        
    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
        
        #Once counter is 0, pop return address off stack pointer
    ...
    

    【讨论】:

    • 非常好的答案!谢谢你的帮助。一个问题:我的问题措辞不正确吗?从理论上讲,您可以继续将这些返回地址存储在堆栈指针上吗?我以为我读到堆栈指针只能保存 2 个值。
    • @IDoNotKnowHowToProgram:堆栈指针只保存一个值(堆栈顶部的地址)。堆栈本身保存在内存中,您可以在那里“堆叠”各种信息。例如,您可以在那里存储返回地址,这就是您在每个函数的序言中所做的。您还可以存储上下文,例如寄存器的值以“释放”它以用于其他目的并恢复它(您可以使用markMove 中的$t0 来执行此操作。所以只要您有空闲内存分配给堆栈您可以将值推到那里的区域。
    • 在您的程序中,您多次存储相同的返回地址。这本身不是问题,但是您的逻辑之后没有正确恢复这些值(堆栈不平衡),我相信您的意思是在进入函数时只存储一次。
    • 再次得到很好的回复。感谢@gusbro 的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多