存储和加载 $ra

问题描述 投票:0回答:1

我目前在用 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:”分支,它会输出阶乘输入的结果。

assembly mips stack-pointer frame-pointer
1个回答
0
投票

为什么你认为函数应该返回标签

branch:
——这本质上是不正确的,所以你需要重新考虑这一点。函数应该在
jal
.

之后立即返回到指令

这段代码有汇编时间错误,所以我不知道你是如何运行它的。

  • jr end
    不是一个有效的指令——我想你想要
    j end
    那里。

该代码在

$fp
的尾声中弄乱了
factorial
寄存器——旧的
$fp
保存在
8($sp)
但从
16($sp)
恢复,所以它不会回到调用者需要它的地方是。更改还原以使用
8($sp)
.


用于在

main
中打印阶乘结果的系统调用序列是:

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

你能看到这两行是如何破坏你想要打印的

$v0
中的值,所以总是打印
1
吗?

但比这更糟糕,因为您还打印了一个提示(系统调用 #4,几行之前),它将 4(打印字符串的系统调用代码)移动到

$v0
中,消除了函数返回值。

所以,要做的是:在

jal
中的
main
指令之后,立即复制返回值
$v0
,在不同的位置——建议
$t0
,因为它会在
syscall
(如果它是一个然后函数调用会建议记忆或
$s0
)。

然后,在用于打印整数的系统调用 #1 处,将

$t0
复制到
$a0
中作为参数。


您可以在调试器中使用单步查看所有这些。调试是一项关键技能,任何尝试汇编语言编程的人都应该掌握或尽快掌握它。单步观察每一行,验证每行之间的程序状态(寄存器和内存)。

© www.soinside.com 2019 - 2024. All rights reserved.