【问题标题】:How do you perform a recursive operation in MIPS Assembly?您如何在 MIPS 汇编中执行递归操作?
【发布时间】:2013-09-25 09:33:31
【问题描述】:

我的任务是编写一个递归 MIPS 汇编程序,该程序在 function1 中执行以下数学运算:

(-3)*function1(n-2) + 7*function1(n-3) + 15

程序是用c建模的:

 // The function1 is a recursive procedure defined by:
// function1(n) = 1 if n <= 2
// = (-3)*function1(n-2) + 7*function1(n-3) + 15 otherwise.
int function1(int n)
{
 if (n <= 2)
 {
 return 1;
 }
 else
 {
 int comp = (-3)*function1(n-2) + 7*function1(n-3) + 15;
 return comp;
 }
}
// The main calls function1 by entering an integer given by a user.
void main()
1 of 2{
 int ans, n;
 printf("Enter an integer:\n");
 // read an integer from user and store it in "n"
 scanf("%d", &n);
 ans = function1(n);
 // print out the solution computed by function 1
 printf("The solution is: %d\n", ans);
 return;
}

我已经编写了代码,它可以正常编译和执行,但给出的值不正确:

 .data
mes1:   .asciiz "\nEnter an integer: "
mes2:   .asciiz "The solutinon is: "

.text
    .globl main

main:
    #Display message
    la $a0, mes1
    li $v0, 4
    syscall
    #Retrieve Value
    li $v0, 5
    syscall
    #Store value into $a0 and jump to function1
    move $a0, $v0
    jal function1

    #Store return value to $t0
    move $t0, $v0

    #Display solution
    la $a0, mes2
    li $v0, 4
    syscall
    move $a0, $t0
    li $v0, 1
    syscall

    #End
    li $v0, 10
    syscall



function1:
    #Store return address
    addi $sp, $sp, -4
    sw $ra, 0($sp)

    #Store $a0 to stack
    addi $sp, $sp, -4
    sw $a0, 0($sp)

    #If($a0<3):$t0=1:$t0=0
    slti $t0, $a0, 3

    #if($t0=0):math
    beq $t0, $zero, math
    addi $v0, $zero, 1

    #Retrieve from stack
    lw  $a0, 0($sp)
    addi    $sp, $sp, 4
    lw  $ra, 0($sp)
    addi    $sp, $sp, 4

    jr $ra

math:
    addi $a0, $a0, -2
    jal function1
    mul $s0 $v0, -3
    addi $a0, $a0, -3
    jal function1
    mul $s1, $v0, 7
    add $s1, $s0, $s1
    addi $v0, $s1, 15

    #Retrieve from stack
    lw  $a0, 0($sp)
    addi    $sp, $sp, 4
    lw  $ra, 0($sp)
    addi    $sp, $sp, 4

    jr $ra

当我输入 6 时,它应该输出 91。目前,它正在输出 44。也许更令人不安的是,每当我输入任何值时,输出的数字总是能被 4 整除。对于我的生活,我想不通出了什么问题。谁能给点建议?

-编辑-

我考虑了@Tomás Badan 关于保护 $a0 的评论。我试过了:

math:
    #Store $a0 to stack
    addi $sp, $sp, -4
    sw $a0, 0($sp)

    addi $a0, $a0, -2
    jal function1
    mul $s0, $v0, -3

    #Retrieve from stack
    lw  $a0, 0($sp)
    addi    $sp, $sp, 4

    addi $a0, $a0, -3
    jal function1
    mul $s1, $v0, 7
    add $s1, $s0, $s1
    addi $v0, $s1, 15

    #Retrieve from stack
    lw  $a0, 0($sp)
    addi    $sp, $sp, 4
    lw  $ra, 0($sp)
    addi    $sp, $sp, 4

    jr $ra

但它仍然返回不正确的值,虽然更接近正确的数字。

【问题讨论】:

    标签: assembly recursion mips


    【解决方案1】:

    好吧,我会指定一些错误,这不是一个完整的调试会话:

    function1:
    ...
    #If($a0<3):$t0=1:$t0=0
    slti $t0, $a0, 3
    
    #if($t0=0):sub1
    beq $t0, $zero, math
    
    #Load 4 to $v0
    addi $v0, $zero, 4 // I thought you should return 1
    ...
    
    math:
    addi $a0, $a0, -2  // you are changing $a0 here
    jal function1
    lw $t2, constn3
    mult $v0, $t2
    mflo $t0           // you use $t0 to keep temporary values, but you function change $t0 too (look above, at instruction slti). You need to protect its contents
    addi $a0, $a0, -3  // but you need your original value here
    jal function1
    lw $t2, const7
    mult $v0, $t2
    

    已编辑

    正如我在 cmets 中所说,您需要保留所有需要在过程调用之间保持状态的寄存器。

    如果您遵循 MIPS 约定调用,它表示您必须保存您在函数内部使用的 s* 系列中的任何寄存器。您使用其中的两个 s0 和 s1,因此,您必须将它们保存在入口点。嗯,那是另一个问题,你必须只有一个入口点和一个出口点。

    function1:
    #Store return address
    addi $sp, $sp, -12
    sw $ra, 0($sp)
    sw $s0, 4($sp)
    sw $s1, 8($sp)
    
    #If($a0<3):$t0=1:$t0=0
    slti $t0, $a0, 3
    
    #if($t0=0):math
    beq $t0, $zero, math
    addi $v0, $zero, 1
    j exit
    
    math:
    addi $sp, $sp, -4
    sw $a0, 0($sp)
    addi $a0, $a0, -2
    jal function1
    lw  $a0, 0($sp)
    addi    $sp, $sp, 4
    
    addi $t6, $zero, -3
    mul $s0 $v0, $t6
    
    addi $sp, $sp, -4
    sw $a0, 0($sp)
    addi $a0, $a0, -3
    jal function1
    lw  $a0, 0($sp)
    addi    $sp, $sp, 4
    
    addi $t6, $zero, 7
    mul $s1, $v0, $t6
    add $s1, $s0, $s1
    addi $v0, $s1, 15
    
    #Retrieve from stack
    exit:
    lw $ra, 0($sp)
    lw $s0, 4($sp)
    lw $s1, 8($sp)
    addi    $sp, $sp, 12
    
    jr $ra
    

    【讨论】:

    • 我已经更新了我的代码。在 addi $v0, $zero, 4 行应该是 1 而不是 4。关于您的第二条评论,我相信我应该将 $a0 减 2,直到 function1 返回 1。关于您的第三条评论,我已经取消了 mflo,只使用了 mul。我还使用了不同的寄存器,这样我就不会覆盖它们。关于评论 4,我再次认为这是由堆栈处理的,并且有必要类似于评论 2?
    • 现在输入 6 得到 -35
    • 例如,如果 n=5,则必须将函数调用为:(-3)*function1(3) + 7*function1(2) + 15。在您的代码中,您使用 $a0 = 3 调用第一个函数 1,但在第二个调用中,您的 $a0 = 3 - 3,即 0
    • 调用函数前保护值,调用函数后压栈弹出
    • 无需过多介绍,您需要注意您正在调用递归函数。因此,在调用函数之前和之后需要保留的任何值或状态,都需要将其保护到堆栈中。例如,您需要在函数调用后保持 $s0 有效。但是在您的函数内部,您正在更改 $s0,因此,为了工作,您需要在调用函数之前将其保存到堆栈中,并在调用后将其弹出
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 2020-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多