assembly 我正在练习将C代码转换为MIPS汇编,我如何将下面的collatz函数转换为MIPS?

vaqhlq81  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(109)
uint32_t collatz(uint32_t n, int d) {
  /*   printf("%d\n", n);*/
  if (n != 1) {
    if (n % 2)
      return collatz(3 * n + 1, d + 1);
    else {
      return collatz(n / 2, d + 1);
    }
  }

  return d;
}

字符串
这是我试图在MIPS汇编中重写的C函数。我想在我的汇编版本中使用递归,我真的很难让函数工作。
我有这个驱动程序的汇编版本的功能.

.data

arrow: .asciiz " -> "

.text

main:
    li      $sp,        0x7ffffffc      # initialize $sp

# PROLOGUE
    subu    $sp,        $sp,        8   # expand stack by 8 bytes
    sw      $ra,        8($sp)          # push $ra (ret addr, 4 bytes)
    sw      $fp,        4($sp)          # push $fp (4 bytes)
    addu    $fp,        $sp,        8   # set $fp to saved $ra

    subu    $sp,        $sp,        12  # save s0 and s1 on stack before using them
    sw      $s0,        12($sp)         # push $s0
    sw      $s1,        8($sp)          # push $s1
    sw      $s2,        4($sp)          # push $s2

    la      $s0,        xarr            # load address to s0

main_for:
    lw      $s1,        ($s0)           # use s1 for xarr[i] value
    li      $s2,        0               # use s2 for initial depth (steps)
    beqz    $s1,        main_end        # if xarr[i] == 0, stop.

# save args on stack rightmost one first
    subu    $sp,        $sp,        8   # save args on stack
    sw      $s2,        8($sp)          # save depth
    sw      $s1,        4($sp)          # save xarr[i]

    li      $v0,        1
    move    $a0,        $s1             # print_int(xarr[i])
    syscall 

    li      $v0,        4               # print " -> "
    la      $a0,        arrow
    syscall 

    jal     collatz                     # result = collatz(xarr[i])

    move    $a0,        $v0             # print_int(result)
    li      $v0,        1
    syscall 

    li      $a0,        10              # print_char('\n')
    li      $v0,        11
    syscall 

    addu    $s0,        $s0,        4   # make s0 point to the next element

    lw      $s2,        8($sp)          # save depth
    lw      $s1,        4($sp)          # save xarr[i]
    addu    $sp,        $sp,        8   # save args on stack
    j       main_for

main_end:
    lw      $s0,        12($sp)         # push $s0
    lw      $s1,        8($sp)          # push $s1
    lw      $s2,        4($sp)          # push $s2

# EPILOGUE
    move    $sp,        $fp             # restore $sp
    lw      $ra,        ($fp)           # restore saved $ra
    lw      $fp,        -4($sp)         # restore saved $fp
    jr      $ra                         # return to kernel


这是我的预期输出:

2 -> 1
4 -> 2
6 -> 8
8 -> 3
10 -> 6


这是我最新的函数实现:

# collatz function in MIPS Assembly
# Assumes n is in $a0 and d is in $a1
# Returns the result in $v0

    .text
    .globl collatz
collatz:
    addi    $sp, $sp, -12    # Allocate stack space for local variables and return address
    sw      $ra, 8($sp)      # Save return address
    sw      $a0, 4($sp)      # Save n
    sw      $a1, 0($sp)      # Save d

    # Check if n is 1 (base case)
    li      $t0, 1
    beq     $a0, $t0, base_case

    # Check if n is even or odd
    andi    $t1, $a0, 1      # t1 = n % 2
    beqz    $t1, even_case

    # Odd case: 3n + 1
    li      $t2, 3
    mul     $t2, $a0, $t2    # t2 = 3 * n
    addi    $t2, $t2, 1      # t2 = 3 * n + 1

    j       recursive_call

even_case:
    # Even case: n / 2
    srl     $t2, $a0, 1      # t2 = n / 2

recursive_call:
    # Prepare arguments for recursive call
    lw      $a0, 4($sp)      # Restore n
    lw      $a1, 0($sp)      # Restore d
    addi    $a1, $a1, 1      # Increment d

    move    $a0, $t2         # Update n
    jal     collatz          # Recursive call

    # Returning from recursive call
    j       end_function

base_case:
    # Base case: n is 1, return d
    lw      $v0, 0($sp)      # Load d into return value register $v0

end_function:
    lw      $ra, 8($sp)      # Restore return address
    addi    $sp, $sp, 12     # Deallocate stack space
    jr      $ra              # Return to caller


这是我得到的输出:

2 -> 2147476133
4 -> 2147476262
6 -> 2147476391
8 -> 2147476520
10 -> 2147476649


我从以前的版本中得到的其他输出包括一个大整数,就像上面的那个,除了它对所有输入都是相同的数字。我也得到了所有0的输出。我也得到了算术溢出错误。

4si2a6ki

4si2a6ki1#

您没有正确地从maincollatz传递参数。第一个参数寄存器$a0中的值是对字符串“->"的引用,这很难满足您的需要。
举例说明:

li      $v0,        4               # print " -> "
la      $a0,        arrow
syscall 

jal     collatz                     # result = collatz(xarr[i])

字符串
$a0中放入的最后一个值是arrow,而不是xarr[i]。当在代码中间插入用于打印输出的系统调用时,您需要小心$a0$v0以及其他一些值。更糟糕的是,如果使用其他值,如printf(在MARS/QtSpim上不可用),因为这些值在保留寄存器方面不如系统调用友好。
我们还需要注意的是,您假设collatz使用一个参数进行调用,如下所示:

jal     collatz                     # result = collatz(xarr[i])


然而,它被宣布为

# collatz function in MIPS Assembly
# Assumes n is in $a0 and d is in $a1
# Returns the result in $v0


在C中,uint32_t collatz(uint32_t n, int d);
总而言之,这段代码为n传递了一个字符串地址,而d没有传递任何地址。
否则,你的代码看起来基本上是正确的,但有很多不必要的进出堆栈的传输。在大多数情况下,可以使用更多的寄存器中的值。
单步调试将突出maincollatz的参数传递问题。在单步调试期间,检查每条指令的正确性,但在函数调用开始和函数返回时,这些都是检查较大程序状态有效性的好点,例如参数,返回值,大多数指令只涉及修改一个寄存器或内存位置,但有些指令(如系统调用和函数调用)可能有更多的副作用,因此在它们的转换时进行额外的验证是很好的。

相关问题