用mips写俄罗斯农民乘法

时间:2018-04-01 16:20:20

标签: algorithm math assembly mips

我想写一个程序,使用俄罗斯农民乘法与mips,但我面临一些问题

/ A method to multiply two numbers using Russian Peasant method
unsigned int russianPeasant(unsigned int a, unsigned int b)
{
    int res = 0;  // initialize result

    // While second number doesn't become 1
    while (b > 0)
    {
         // If second number becomes odd, add the first number to result
         if (b & 1)
             res = res + a;

         // Double the first number and halve the second number
         a = a << 1;
         b = b >> 1;
     }
     return res;
}

我用MARS环境将其翻译成mips

    .data
msg1: .asciiz "give your first number"
msg2: .asciiz "give the second number"

    .text
#a = $t1
#b = $t2
li $t3,0 #p

li $s1,1
li $s2,2

#display msg1
li $v0,4
la $a0,msg1
syscall
#read first number
li $v0,5
syscall
move $t1,$v0

#display msg2
li $v0,4
la $a0,msg2
syscall
#read second number
li $v0,5
syscall
move $t2,$v0

WHILE: 
    bgt $t2,$zero,DO
    move $a0,$t3
    li $v0,1
    syscall
    li $v0,10
    syscall

DO:
     div $t2,$s2
     mfhi $s0 #r $s0 = $t2 / $s2
     beq $s0,$s1,INC # IF b MOD 2 = 1 I jump to INC
    #a = 2xa
     mul $t1,$t1,$s2

     div $t2,$s2
     mflo $t2 # b = $t2/$s2
     j WHILE 

# i = i+ 1   
INC:
    add $t3,$t3,$t1
    jr $ra 
请,有人可以帮帮我吗? 修复“错误:无效的程序计数器值:0x00000000” 我搜索了如何解决它,但我有一个问题与$ ra 如何在$ ra中保存退货地址?

1 个答案:

答案 0 :(得分:1)

jr $ra将跳转到寄存器$ra中存储的地址,该地址未由您的代码设置,因此它仍然为零(在运行代码之前来自MARS的初始寄存器值)。然后,CPU将跳转到地址零,这是失败,并报告。

使用jr $ra来&#34;返回&#34;从某些代码中,您必须首先使用jal指令(或修改$ra内容的其他指令)来设置$ra。此外,当您想要嵌套多个jal&#34;子程序调用&#34;时,您必须存储/恢复外部调用的$ra,以便不会丢失下一个嵌套{{1}的值。 1}}。

以下代码是使用配对jal + jal进行&#34;子程序&#34; -like调用的示例,以及示例在汇编时这种算法的实现是多么简单(因为实际上C源代码更像C中的汇编,所以你的经验不足让你采用了一种非常复杂和复杂的方法,可以用原始C源几乎1:1的方式实现汇编:

jr $ra

子程序本身,可以在.text main: # just minimal "main" to verify the code works # read two integers as input li $v0,5 syscall move $a0, $v0 # $a0 = a li $v0,5 syscall move $a1, $v0 # $a1 = b # call the russianPeasant subroutine jal russianPeasant # $v0 = a * b nop # output result move $a0, $v0 li $v0, 1 syscall # terminate li $v0, 10 syscall a0中使用参数调用,并返回a1的结果。

v0

代码是故意在每个分支# input: $a0 = a, $a1 = b # output: $v0 = a * b russianPeasant: li $v0, 0 # res = 0 beq $a1, $zero, russianPeasant_b_zero nop # neutralize "Delayed branching" setting ("nop" works with both ON/OFF setting) russianPeasant_while_b: andi $at, $a1, 1 # test if b is odd, for even b skip the res = res + a beq $at, $zero, russianPeasant_b_even nop # but such neutralization comes with performance cost of course add $v0, $v0, $a0 # res = res + a russianPeasant_b_even: sll $a0, $a0, 1 # a = a << 1 srl $a1, $a1, 1 # b = b >> 1 bne $a1, $zero, russianPeasant_while_b # repeat when (b != 0) nop # on real MIPS production code the instructions are reordered to avoid useless nop russianPeasant_b_zero: jr $ra # return res nop 指令之后放置,以使其在延迟分支设置为ON或OFF时起作用。

尝试使用带有指令描述的MARS帮助(F1)来弄清楚它是如何工作的,还可以使用调试器的单步功能来注意操作中的代码,一次一条指令,观察所有寄存器值和代码流。

在具有延迟分支的真实MIPS CPU上,代码可以像这样进行优化(您可以在MARS设置中切换&#34;延迟分支&#34; ON,使其模拟真正的MIPS CPU,这种行为有点令人困惑..来自您的原始资源看起来您正在学习MIPS程序集而没有延迟分支,这对于刚刚开始汇编的人来说当然是合理的方法,但这并不是真正的MIPS CPU工作方式):

nop