Assembly MIPS计数数为1';s给定用户输入(B指令不在MARS中跳转)
我用MIPS写了一个代码,我想它会接受用户的输入(十进制数),然后用1加1,然后将位位置向左移动1,直到数字达到0,然后用二进制表示法显示十进制数中的1。我不确定哪里出了错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
# 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