正在调用子程序,但算术有问题?

Subroutine being being called, but something wrong with arithmetic?

我几乎完成了用汇编编写这个程序,让用户输入两个值,(u,v),然后调用一个子程序来计算 X * Y * Z。公式是

7u^2 - 25uv + 63v^2

其中第一个 X * Y * Z 是 7u^2,第二个是 25uv 等等。

我应该调用该子程序三次,我相信一切都被正确调用,但我无法弄清楚为什么算术错误,例如当我输入 u=1 和 v= 1 结果返回为 -25。下面是我的代码,有什么想法吗?

同样,当我输入 0 和 0 来结束程序时,它得出的结果是:当我试图结束它时,我输入的两次都是 0。

谢谢大家!

编辑: 我在这个程序中取得了一些进展,但仍然无法打印出正确的结果,现在只差几个数字了。

编辑2: 它可能不漂亮,但在评论员的大力帮助下,我只是让它按照预期的方式工作,但这段代码运行并按照预期的方式工作。

        # $a0 - 7/25/63
        # $a1 - u
        # $a2 - v

        .text
        .globl main

main:

loop:
li $v0, 4 # print string syscall method
la $a0, prompt # prints prompt 
syscall

li $v0, 4 # print string syscall method
la $a0, prompt1 # prints prompt 
syscall

li $v0, 5 # reads the int typed
syscall

#gets u
move $a1, $v0
addu $s0, $a1, 0
beq $a1, $zero, test
nop
test:


li $v0, 4 # print string syscall method
la $a0, prompt2 # prints prompt 
syscall

li $v0, 5 # reads int
syscall

# gets v
move $s1, $v0
beq $s1, $zero, endlp 
nop
addu $s3, $s1, 0
addu $a2, $a1, 0
li $a0, 7 # loads the int 7 into $a0
jal arithmetic # jumps to subroutine
nop # nop

move $a2, $s1 
move $s2, $v1 # where subrouting comes back to, moves the result into $s1

li $a0, -25 # loads -25 into $a0
jal arithmetic
nop

addu $s2, $s2, $v1 # moves result to s2
move $a1, $s3 # puts v in a1
li $a0, 63
jal arithmetic
nop

addu $s2, $s2, $v1

li $v0, 4
la $a0, prompt3
syscall

li $v0, 1
move $a0, $s2
syscall

li $v0, 4
la $a0, newline
syscall

j loop
nop




        .data
prompt: .asciiz "Type zero for both u and v to end program\n"
prompt1: .asciiz "Please type value for 'u': \n"
prompt2: .asciiz "Please type value for 'v': \n"
prompt3: .asciiz "Result is: "
newline: .asciiz "\n"


        .text
        .globl arithmetic
arithmetic:

mul $a3, $a1, $a2
mul $v1, $a0, $a3
jr $ra
nop


endlp:
li $v0, 10
syscall
beq $a1, $zero, test1 # tests if $a1 is equal to zero, will go to test if it is

你过早地测试了 u,当 v 还没有被输入时,然后你跳转到一个地方,那里 v 应该已经被输入了,并且它不是。让用户同时输入 uv,然后测试 [0, 0] 对,您实际上可以通过单个测试来做到这一点:

or  $at, $a1, $a2      ; assumes: a1 = u, a2 = v
beq $at, $zero, endlp  ; when (u|v) == 0 end

此外,您没有将 v 存储到 $a2,但您使用 t1a2 做了一些奇怪的事情,以 [=13] 的副本结尾=] a1a2.

使用您的 MARS/SPIM 模拟器环境来单步执行指令,观察寄存器中的值,并了解为什么会发生这种情况。还有你的 -25 来自 [1, 1] 输入的地方。

我不介意帮助调试一些令人困惑的问题,但是测试寄存器中明显错误的值是否为零看起来你根本不知道如何调试,或者你没有花任何精力在这上面.

您是如何设法错过过早跳转到 test1 跳过第二个值的输入的?


编辑:关于算术结果。我认为你混淆了寄存器。

最初的评论将 a0 精确指向常量,a1 指向 ua2 指向 v 听起来不错,但是你为什么要和他们玩弄,就像你没有另外一百万的备用 tX 寄存器来获得中间结果?此外,如果你想 return 结果为 v0,那么你几乎可以一直直接计算它,也可以将它用于中间结果,避免修改函数参数,如果你打算以后使用它们.

我会清理那个寄存器的使用,而是用 "functional" 的方式多写一点代码,你只设置一次特定的寄存器值,比如假的 "read only",所以你的算术子程序会做 v0 = a0 * a1 * a2(只修改 v0),然后你可以在 s1 中总结总结果并且......好吧,我看不出有任何理由使用更多的寄存器(对于计算本身,输入完成后)。精简一下。


编辑2: 关于 u 的副本……你是对的,我忘记了那个……所以我建议再重新设计一个寄存器用法。在常见的 MIPS 调用约定中,调用子程序时会保留 sX 寄存器,所以让我们换一种方式,在输入 s0 = u, s1 = v, s2 = reserved for result 之后,每次都从这些寄存器设置 a0, a1, a2 ,它将在代码审阅者至少要读得好。抱歉造成混淆。

即:

v0 = arithmetic(a0=7, a1=s0 (u), a2=s0 (u))
s2 = v0   ; reset result to first intermediate
v0 = arithmetic(a0=-25, a1=s0 (u), a2=s1 (v))
s2 += v0  ; add to result
v0 = arithmetic(a0=63, a1=s1 (v), a2=s1 (v))
s2 += v0  ; add to result to have final total
; display s2 as result

答案 2,包含审查来自 OP 的更新源(在他修复了原始源的问题之后):

刚刚调整了源代码,我希望它是这样写的:

# register allocation through main code:
# s0 = u, s1 = v, s2 = intermediate/final result

# WARNING: code needs delayed branching ON (like real MIPS CPU)

        .text
        .globl main
main:

    li $v0, 4       # print string syscall method
    la $a0, prompt  # prints prompt 
    syscall

    # input "u"
    li $v0, 4       # print string syscall method
    la $a0, prompt1 # prints "u" prompt
    syscall

    li $v0, 5       # reads the int typed
    syscall
    move $s0, $v0   # remember "u" input

    # input "v"
    li $v0, 4       # print string syscall method
    la $a0, prompt2 # prints "v" prompt 
    syscall

    li $v0, 5       # reads the int typed
    syscall
    move $s1, $v0   # remember "v" input

    # result = 0
    move $s2, $zero

    # exit when user did enter pair of zeroes
    or   $at, $s0, $s1 # binary OR(u, v)
    bne  $at, $zero, doCalculation  # when both inputs are zero -> terminate
    li   $v0, 10    # nop not needed, v0=10 is harmless in case of calculation
    syscall         # terminate

doCalculation:
    # calculate result += 7*u*u
    move $a1, $s0   # a1 = u
    move $a2, $s0   # a2 = u
    jal arithmetic  # jumps to subroutine
    li   $a0, 7     # load a0 in delayed branching as argument for subroutine
    add  $s2, $s2, $v0   # update result

    # calculate result += -25*u*v
    move $a1, $s0   # a1 = u
    move $a2, $s1   # a2 = v
    jal arithmetic  # jumps to subroutine
    li   $a0, -25   # load a0 in delayed branching as argument for subroutine
    add  $s2, $s2, $v0   # update result

    # calculate result += 63*v*v
    move $a1, $s1   # a1 = v
    move $a2, $s1   # a2 = v
    jal arithmetic  # jumps to subroutine
    li   $a0, 63    # load a0 in delayed branching as argument for subroutine
    add  $s2, $s2, $v0   # update result

    # display result    
    li   $v0, 4
    la   $a0, prompt3
    syscall

    li   $v0, 1
    move $a0, $s2
    syscall

    li   $v0, 4
    la   $a0, newline
    syscall

    j    main   # rinse and repeat until [0, 0] user input
    nop         # needed, to not execute slow MUL from subroutine

arithmetic:
    mul $v0, $a1, $a2
    jr  $ra
    mul $v0, $a0, $v0  # v0 = a0 * a1 * a2

        .data
prompt:  .asciiz "Type zero for both u and v to end program\n"
prompt1: .asciiz "Please type value for 'u': \n"
prompt2: .asciiz "Please type value for 'v': \n"
prompt3: .asciiz "Result is: "
newline: .asciiz "\n"