assembly 存储和加载$ra

wz8daaqr  于 2023-04-06  发布在  其他
关注(0)|答案(1)|浏览(156)

我目前在MIPS中编写这个递归阶乘汇编代码时遇到了麻烦。我不想改变这个代码的格式,因为它是一个项目,但我想了解如何存储或调用$ra,以便它将返回到“分支:“,以输出计算的阶乘。

## 
## 

.data
prompt1:    .asciiz "\nPlease enter an integer: \n"
negativePrompt: .asciiz "\nYour input must be greater than or equal to 0 in order to function.\n"
factorialIs:    .asciiz "\nThe factorial of your input number is: \n"

.globl main
.text
main:

    subu $sp, $sp, 32   # push memory for 8 values
    sw $ra, 20($sp)     # save return address
    sw $fp, 16($sp)     # save old frame pointer 
    addiu $fp, $sp, 28  # set up frame pointer

branch:

    li $v0, 4           # load macro for print_str 
    la $a0, prompt1     # pass argument to string
    syscall             # print string prompt1

    li $v0, 5           # load macro for read_int
    syscall             # get N value from user 

    move $a0, $v0               # store N as arg
    blt $a0, 0, negBranch       # if N is negative, handles case    
    jal factorial               # calls factorial function

    li $v0, 4               # macro for print string
    la $a0, factorialIs     # print string
    syscall

    li $v0, 1               # macro for print int
    move $a0, $v0           # print output int in v0
    syscall 

    lw $ra, 20($sp)         # restore return address of caller
    lw $fp, 16($sp)         # restore frame pointer
    addiu $sp, $sp, 32      # pop stack
    jr $ra                  # return to caller 
#exit:
#   "would you like to calculate again?" 
#   li $v0, 10              # load macro to exit
#   syscall                 # execute exit
    
negBranch: 
    li $v0, 4                   # load macro for print_str 
    la $a0, negativePrompt              # pass argument to string
    syscall                     # print string prompt1  
    li $t0, 0                   # initialize input value to 0
    j branch                        # ask user to input new number
    
factorial: 
    subu $sp, $sp, 32       # pushes memory for variables
    sw $ra, 12($sp)         # stores return address of recursion
    sw $fp, 8($sp)          # save frame pointer
    addiu $fp, $sp, 28      # set up frame pointer
    sw $a0, 0($fp)          # stores n in frame pointer

    lw $v0, 0($fp)
    bgtz $v0, zeroBranch        # if N is greater than 0, recurse
    li $v0, 1                   # return 1
    jr end

zeroBranch:
    lw $v1, 0($fp)          # load n value
    subu $v0, $v1, 1        # v0 = n - 1 
    move $a0, $v0           # a0 = v0 = n - 1 
    jal factorial           # recursive function call
    lw $v1, 0($fp)          # load original n value to v1
    mul $v0, $v0, $v1       # v0 = n * fact(n-1)    

end: 
    lw $ra, 12($sp)         # loads return address of main call
## this ^ line does not access the address from "jal factorial"
    lw $fp, 16($sp)         # loads initial n value
    addiu $sp, $sp, 32      # collapses stack frame
    jr $ra                  # return to main then exit

当我输入factorial(1)时,它如预期的那样递归地运行;然而,当我到达我的“end:”分支时,返回地址没有返回到我的“branch:”分支,它将输出阶乘输入的结果。

pbpqsu0x

pbpqsu0x1#

为什么你认为函数应该返回到标签branch:-这是本质上不正确的,所以你需要重新考虑这一点。函数应该立即返回到jal之后的指令。
这段代码有汇编时错误,所以我不知道你是如何运行它的。

  • jr end不是一个有效的指令-我认为你需要j end

factorial的后记中,这段代码把$fp寄存器搞乱了-旧的$fp保存在8($sp),但从16($sp)恢复,所以它不会回到调用者需要它的位置。
用于在main中打印阶乘结果的系统调用序列是:

li $v0, 1               # macro for print int
move $a0, $v0           # print output int in v0

你能看到这两行代码是如何破坏了$v0中你想要打印的值的,所以将总是打印1吗?
但比这更糟糕的是,因为您还打印了一个提示符(syscall #4,前面几行),它将4(打印字符串的syscall代码)移动到$v0中,消除了函数返回值。
所以,要做的是:在main中的jal指令之后,立即复制返回值$v0,在不同的位置-建议$t0,因为它将在syscall中存活(如果它是一个函数调用,则建议内存或$s0)。
然后,在用于打印整数的系统调用#1处,将$t0复制到用于参数的$a0中。
您可以通过使用调试器中的单步调试来查看所有这些。调试是一项关键技能,任何尝试汇编语言编程的人都应该拥有它或尽快掌握它。单步调试并查看每行,验证每行之间的程序状态(寄存器和内存)。

相关问题