Assembly MIPS计数数为1';s给定用户输入(B指令不在MARS中跳转)

Assembly MIPS计数数为1';s给定用户输入(B指令不在MARS中跳转),assembly,mips,mars-simulator,Assembly,Mips,Mars Simulator,我用MIPS写了一个代码,我想它会接受用户的输入(十进制数),然后用1加1,然后将位位置向左移动1,直到数字达到0,然后用二进制表示法显示十进制数中的1。我不确定哪里出了错 # Count number of 1s in a 32 Bit Number #$t0 = user input #$t1 = counter .data prompt: .asciiz "Enter Number: " result: .asciiz "Number of 1s counted: " n: .word

我用MIPS写了一个代码,我想它会接受用户的输入(十进制数),然后用1加1,然后将位位置向左移动1,直到数字达到0,然后用二进制表示法显示十进制数中的1。我不确定哪里出了错

# Count number of 1s in a 32 Bit Number
#$t0 = user input
#$t1 = counter
.data
prompt: .asciiz "Enter Number: "
result: .asciiz "Number of 1s counted: "
n:  .word   0
counter:.word   0
    .text

#Prompt User for Number 
    li $v0, 4       #load text stored in v0
        la $a0, prompt  #print prompt
        syscall     #call prompt string

#Get user Input   
        li $v0, 5   #load number stored in v0
        la $t0, n   #load int into $t0
        syscall     #display integer

#Store user input in $t0 to do function
        move $t0, $v0   #Move value from $v0 to $t0 to modify
        la $t1, counter #load address of counter
        lw $t1, 0($t1)  #load counter to t1

AND:
    andi $t2, $t0, 1 #and user input($t0) with 1 and store in $t2
    beq  $t2, 1, loop #if t2 equals 0 branch
    srl  $t0, $t0, 1 #shift user input($t0) to right 1 position logically
    b AND       #branch this function

loop:
    add $t1, $t1, 1 #add 1 to the counter
    srl $t0, $t0, 1 #shift $to to the right 1 position logically with counter
    beqz $t0, display #If $t0 equals 0 send to display function
    b AND       #send back to AND function if not

display:
       li $v0, 4    #load text stored in v0
       la $a0, result   #print text from address a0
       syscall
       la $a0, ($t1)    #load the address of the counter to a0
       li $v0, 1      #load integer stored in v0
       syscall        #print final integer


使用MARS 4.5 MIPS emulator逐步查看这段代码发现,对于我来说,由于一些未知的原因,
b和
无法正常工作。如果你写
j和
,一切都会正常工作

编辑:正如@Peter Cordes指出的,这很可能是一个汇编程序错误

注意:您可以删除整个
循环
部分,然后在
部分执行此操作:

and:
    andi $t2, $t0, 1 #and user input($t0) with 1 and store in $t2
    add $t1, $t1, $t2 #add the result it is either 0 or 1
    srl  $t0, $t0, 1 #shift user input($t0) to right 1 position logically
    beqz $t0, display #If $t0 equals 0 send to display function
    j and       #send back to AND function if not

MARS显然在
b
指令上有一个汇编程序错误,而标签名也是有效的指令助记符。它最终成为下一条指令的分支

将标签名称从
更改为
和_TOP
使其正确组装。
Eraklon发现使用
j
指令也有效。(代替操作数的
消除了标记作为标签而不是指令的歧义,因此这是MARS中的一个bug,而不是代码中的bug。clang可以很好地汇编源代码。这并不是说您可以在MARS之外运行它;它取决于MARS系统调用和无延迟插槽分支。)

我在Arch GNU/Linux上的Java OpenJDK 1.8.0232下的Mars 4.5中进行了测试,并复制了@Eraklon的结果。(但答案中的推理是错误的。)

b和
指令组合成
0x043fFFFF
(与列出的指令
bgez$0和
),因此分支目标是分支后的指令。(实际MIPS上的延迟槽是多少)。显示MIPS相对分支指令为I型,符号将16位立即数(指令字的低位16)和左移位扩展2。(或者将其视为单词中的偏移量)。相对于分支延迟槽的末端。因此,偏移量
-1
使我们到达分支后的指令

还要注意,
0x043f
不是
bgez
的正确操作码和寄存器编码。对于
bgez$zero
,它应该是
0x0401
。(显示二进制编码)。根据
llvm objdump-d
(使用
clang-target mips
组装
.word 0x043fffff
后),它实际编码
4 3f ff-synci-1($1)
。我想知道MARS bug是不是在一个比它应该的更宽的
-1
值中ORing,覆盖了一些更高的字节

bgez$0
是在MIPS中编码无条件相对分支的一种方法。另一种方法是
beq$0,$0,target
。没有单独的操作码,您只需选择一个I型
b..
机器指令,输入总是正确的。)

MARS将此错误组装的指令模拟为一个分支,始终使用其编码的偏移量:它有效地通过分支。或者在启用设置->延迟分支的情况下,分支后的指令执行两次:一次作为延迟槽,一次作为分支目标

所以很明显,模拟器并不能真正地从机器代码中解码,即使你打开设置->自我修改代码。或者只是显示器坏了?IDK,其实并不重要,这是一个100%的bug,与其去戳症状,还不如去看看火星的来源并修复它


使用不同的标签名会给出正确的结果:第一个
b和_TOP
组合到
0x0401FFB
,MARS将其列为
bgez$0,0xfffffffb
。(请注意列表中的数字偏移量,而不是和标签名称。)

它正确地模拟了,这些分支指向正确的位置


我没有检查你的逻辑,但它似乎太复杂了。请注意,
la$a0,($t0)
是编写
move$a0,$t0
的疯狂方式。显然火星允许这样做。不过,一开始没有理由从
计数器加载
;您可以使用
addu$t0、$zero、$zero
或其他任何您想要的方式将寄存器归零。或者将其写为
move$t0,$zero

这也是愚蠢的:

    beqz $t0, display #If $t0 equals 0 send to display function
    b AND_TOP         #send back to AND function if not

display:
只需
bnez和_TOP
,而不是有条件地跳过无条件分支

此外,这两条评论都没有增加任何理解。如果有什么关于跳转的原因或语义(例如高级变量,而不是注册名称)的话,请将其放在评论中。e、 g.
bnez$t0,计数循环#还有更多位需要计数?

当然,正如@Eraklon所指出的,您的整个分支逻辑超级复杂。只需隔离低位并将其添加到计数中,无论它是零还是一


或者,如果您关心性能,请屏蔽偶数和奇数位,右移1,然后添加。然后将16x2位累加器打包到寄存器中。使用另一个掩码重复,直到有字节,然后继续或使用乘法技巧将字节相加为高字节。(有关bithack答案,请参见此处堆栈溢出上的fast popcount Q&As,或者。您的方法与之类似,但更复杂。有一些中间选项,例如清除最低设置位并计算迭代次数以使其为零。)

您正在进行大量不必要的内存加载和存储。还有很多空闲寄存器,只是使用MIPS分支指令使用有符号相对位移;它们可以向后分支
bgez$zero,target
是MIPS对相对分支进行编码的一种非常正常的方式,如
beq$zero,$zero,target
reachi