简体   繁体   English

给定用户输入的 MIPS 计数 1 的数量(B 指令不会在 MARS 中跳转)

[英]MIPS Counting Number of 1's given a user input (B instruction doesn't jump in MARS)

I wrote a code in MIPS that I thought would take in a users input (decimal number), AND it with 1 and increment this by 1 and shifting the bit position left by 1 until the number reached 0, then display the number of 1s in the decimal number in its binary representation.我在 MIPS 中编写了一个代码,我认为它会接受用户输入(十进制数),并将其加 1 并将其增加 1 并将位位置左移 1 直到数字达到 0,然后显示 1 的数量以二进制表示的十进制数。 I am not sure where I went wrong.我不确定我哪里出错了。

# 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

Stepping through this code with MARS 4.5 MIPS emulator reveals that for some yet unknown reason for me b AND just does not work as should.使用 MARS 4.5 MIPS 模拟器单步执行此代码会发现,由于某些未知的原因, b AND无法正常工作。 If you write j AND instead everything will work correctly.如果你写j AND相反,一切都会正常工作。

EDIT: As @Peter Cordes pointed out it is most likely an assembler bug.编辑:正如@Peter Cordes 指出的那样,这很可能是一个汇编程序错误。

NOTE: You could remove the whole loop part and do this in the AND section:注意:您可以删除整个loop部分并在AND部分执行此操作:

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 apparently has an assembler bug on b instructions when the label name is also a valid instruction mnemonic .当标签名称也是有效的指令助记符时,MARS 显然在b指令上有一个汇编程序错误 It ends up as a branch-to-next instruction.它以分支到下一条指令结束。

Changing the label name from AND to AND_TOP made it assemble correctly.将标签名称从AND更改为AND_TOP使其正确组装。 Eraklon found that using a j instruction also worked. Eraklon 发现使用j指令也有效。 (The : instead of operands disambiguates the token as being a label instead of an instruction, so this is a bug in MARS, not a bug in your code. clang assembles your source code just fine. Not that you could run it outside of MARS; it depends on MARS system calls and no-delay-slot branches.) :而不是操作数消除了作为标签而不是指令的令牌的歧义,因此这是 MARS 中的错误,而不是您的代码中的错误。clang 可以很好地组装您的源代码。并不是说您可以在 MARS 之外运行它;这取决于 MARS 系统调用和无延迟时隙分支。)

I tested in Mars 4.5 under Java OpenJDK 1.8.0_232 on Arch GNU/Linux and reproduced @Eraklon's result.我在 Arch GNU/Linux 上的 Java OpenJDK 1.8.0_232 下在 Mars 4.5 中进行了测试,并复制了@Eraklon 的结果。 (But the reasoning in that answer is wrong.) (但那个答案的推理是错误的。)

Both b AND instructions assemble to 0x043fFFFF (with listed instruction bgez $0, AND ), so the branch target is the instruction right after the branch . b AND指令都汇编为0x043fFFFF (列出指令bgez $0, AND ),因此分支目标是bgez $0, AND在 branch 之后的指令 (What would be the delay slot on real MIPS). (真正的 MIPS 上的延迟槽是什么)。 How to Calculate Jump Target Address and Branch Target Address? 如何计算跳转目标地址和分支目标地址? shows that MIPS relative-branch instructions are I-type, and sign-extend the 16 bit immediate (the low 16 of the instruction word) and left-shift by 2. (Or look at it unshifted as an offset in words).显示 MIPS 相对分支指令是 I 型的,对 16 位立即数(指令字的低 16 位)进行符号扩展并左移 2。(或将其视为未移位的字)。 Relative to the end of the branch-delay slot.相对于分支延迟时隙的结束。 So an offset of -1 gets us to the instruction after the branch.所以-1的偏移量让我们进入分支后的指令。

Also note that 0x043f is not the right opcode and register encoding for bgez .还要注意的是0x043f不正确的操作码和注册编码bgez It should be 0x0401 for bgez $zero . bgez $zero应该是0x0401 ( http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html shows encodings in binary). http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html显示二进制编码)。 According to llvm-objdump -d (after assembling .word 0x043fffff with clang -target mips ), that actually encodes 4 3f ff ff synci -1($1) .根据llvm-objdump -d (在使用clang -target mips组装.word 0x043fffff之后),实际上编码了4 3f ff ff synci -1($1) I wonder if the MARS bug was ORing in a -1 value that was wider than it should have been, overwriting some higher bytes?我想知道 MARS 错误是否在一个比它应该更宽的-1值中进行 ORing,覆盖了一些更高的字节?

( bgez $0 is one way to encode an unconditional relative branch in MIPS. Another way is beq $0,$0, target . There's no separate opcode, you just have to pick one of the I-type b... machine instructions with inputs that are always true.) bgez $0是在 MIPS 中对无条件相对分支进行编码的一种方式。另一种方式是beq $0,$0, target 。没有单独的操作码,您只需要选择一个 I 型b...带有输入的机器指令总是正确的。)

MARS simulates this mis-assembled instruction as a branch-always with the offset it encoded: it effectively falls through the branch . MARS 将这个错误组装的指令模拟为一个始终带有它编码的偏移量的分支:它有效地通过了 branch Or with Settings->Delayed Branching enabled, the instruction after the branch executes twice: once as the delay slot, once as the branch target.或者在设置->延迟分支启用的情况下,分支后的指令执行两次:一次作为延迟槽,一次作为分支目标。

So apparently the simulator doesn't truly decode from machine code, even if you turn on settings->self modifying code.所以显然模拟器并没有真正从机器代码解码,即使你打开设置->自我修改代码。 Or it's just the display that's broken?还是只是显示器坏了? IDK, doesn't really matter, this is 100% a bug and instead of poking at the symptoms someone should just look at the MARS source and fix it. IDK,其实并不重要,这 100% 是一个错误,而不是戳别人的症状,而应该查看 MARS 源代码并修复它。


Using a different label name gives correct results: the first b AND_TOP assembles to 0x0401fffb , with MARS listing it as bgez $0, 0xfffffffb .使用不同的标签名称会得到正确的结果:第一个b AND_TOP组装为0x0401fffb ,而 MARS 将其列为bgez $0, 0xfffffffb (Note the numeric offset in the listing, instead of the AND label name.) (注意列表中的数字偏移,而不是 AND 标签名称。)

And it simulates correctly, with those branches going to the right place.它可以正确模拟,这些分支会到达正确的位置。


I didn't check your logic but it seems over-complicated.我没有检查你的逻辑,但它似乎过于复杂。 Note that la $a0, ($t0) is an insane way to write move $a0, $t0 .请注意, la $a0, ($t0)是一种写move $a0, $t0的疯狂方式。 Apparently MARS allows that.显然 MARS 允许这样做。 There was no reason to load from counter in the first place, though;不过,首先没有理由从counter加载; you can zero a register with addu $t0, $zero, $zero or whatever else you want.您可以使用addu $t0, $zero, $zero或其他任何您想要的方式将寄存器addu $t0, $zero, $zero Or write it as move $t0, $zero .或者写成move $t0, $zero

Also this is silly:这也是愚蠢的:

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

display:

Just bnez AND_TOP instead of conditionally jumping over an unconditional branch.只是bnez AND_TOP而不是有条件地跳过无条件分支。

Also, neither comment adds anything to the understanding.此外,这两个评论都没有增加任何理解。 If there's something to say about why you jump, or the semantic meaning (eg in terms of high-level variables, not register names), then put that in a comment.如果对跳转的原因语义有什么要说的(例如,就高级变量而言,而不是寄存器名称),请将其放在评论中。 eg bnez $t0, count_loop # more bits left to count?例如bnez $t0, count_loop # more bits left to count?

Of course, as @Eraklon points out, your whole branching logic is super over-complicated.当然,正如@Eraklon 指出的那样,您的整个分支逻辑非常复杂。 Just isolate the low bit and add it to the count whether it's zero or one.只需隔离低位并将其添加到计数中,无论它是零还是一。

Or if you care about performance, mask away even and odd bits, right shift by 1, and addu.或者,如果您关心性能,请屏蔽偶数位和奇数位,右移 1,然后添加。 Then you have 16x 2-bit accumulators packed into a register.然后您将 16 个 2 位累加器打包到一个寄存器中。 Repeat with another mask until you have bytes, then either keep going or use a multiply trick to get bytes summed into the high byte.用另一个掩码重复,直到你有字节,然后继续前进或使用乘法技巧将字节相加到高字节。 (See fast popcount Q&As here on stack overflow for bithack answers, or https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel . Your method is like https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive but more complicated. There are middle-ground options, eg clearing the lowest set bit and counting iterations to make it zero.) (有关 bithack 答案的堆栈溢出,请参阅此处的快速 popcount 问答,或https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel 。您的方法类似于https://graphics.stanford.edu/~seander/ bithacks.html#CountBitsSetNaive但更复杂。有中间选项,例如清除最低设置位和计数迭代以使其为零。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM