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的输出。我也得到了算术溢出错误。
1条答案
按热度按时间4si2a6ki1#
您没有正确地从
main
向collatz
传递参数。第一个参数寄存器$a0
中的值是对字符串“->"的引用,这很难满足您的需要。举例说明:
字符串
在
$a0
中放入的最后一个值是arrow
,而不是xarr[i]
。当在代码中间插入用于打印输出的系统调用时,您需要小心$a0
和$v0
以及其他一些值。更糟糕的是,如果使用其他值,如printf
(在MARS/QtSpim上不可用),因为这些值在保留寄存器方面不如系统调用友好。我们还需要注意的是,您假设
collatz
使用一个参数进行调用,如下所示:型
然而,它被宣布为
型
在C中,
uint32_t collatz(uint32_t n, int d);
总而言之,这段代码为
n
传递了一个字符串地址,而d
没有传递任何地址。否则,你的代码看起来基本上是正确的,但有很多不必要的进出堆栈的传输。在大多数情况下,可以使用更多的寄存器中的值。
单步调试将突出
main
到collatz
的参数传递问题。在单步调试期间,检查每条指令的正确性,但在函数调用开始和函数返回时,这些都是检查较大程序状态有效性的好点,例如参数,返回值,大多数指令只涉及修改一个寄存器或内存位置,但有些指令(如系统调用和函数调用)可能有更多的副作用,因此在它们的转换时进行额外的验证是很好的。