【问题标题】:Displaying number in Binary by shifting通过移位以二进制显示数字
【发布时间】:2012-10-09 06:54:10
【问题描述】:

我编写了一个程序,尝试显示用户输入的数字的二进制形式。但是程序没有正确地进行掩蔽。我该怎么解决?

$s0 中的用户输入

Loop:

and $t0,$s0,2147483648  // anding it with this number because only its MSB is 1 all other bits are zero

sll $s0,$s0,1

move $a0,$t0

li $v0,1
syscall

beq $t1,31,Exit

addi $t1,$t1,1
j Loop

更新: 我按照 dbrank0 的建议修改了这段代码,但现在它只显示一位而不是 32 位

Loop:

and $t0,$s0,2147483648

sll $s0,$s0,1

beq $t1,31,Exit

move $a0,$t0

addi $t1,$t1,1

bgtu $t0,0,Check

li $t0,0
j Loop

Disp:
li $v0,1
syscall

j Loop

Check:
li $t0,1

j Disp

如果有人能帮我解决这个问题,那就太好了。

问候

【问题讨论】:

  • 掩码可能正确完成,但在打印出来之前,您可能需要将要打印的位转换为可表示的内容(通过将其向右移动 31 个位置为 0/1,或转换为 '0' /'1' 通过移位添加额外的 0x30)。
  • 我为什么要这样做。我正在显示存储在 $t0 中的结果,如果两个数字的 MSB 匹配,则可以为 1,如果两个数字的 MSB 不匹配,则为 0。
  • t0 包含 0 或 0x80000000。如果要打印 0 或 1,则应在打印前将这 31 位移动。 (但不要认为我的话是理所当然的,自从我编写 MIPS 以来已经很久了)。

标签: assembly mips bit-shift pcspim


【解决方案1】:

这里有个问题:

bgtu  $t0, 0, Check
li    $t0, 0
j     Loop

如果为零,则不会显示,因为您跳转到 Loop 而不是 Disp。哦,看,Disp 无论如何都写在这条指令之后!解决方案:完全摆脱跳跃。

这是另一个问题,如 dbrank0 所述:

Disp:
li $v0,1
syscall

这会将$a0 的内容显示为整数。但如果该位为 1,$a0 的值将是 0x80000000,而不是 1!当您尝试打印 0x80000000 时,它会将其视为有符号整数并打印 -2147483648。

还有一个问题:

beq $t1,31,Exit

首先,这条指令的位置很尴尬。为什么要在andshift 之后检查退出条件?您应该在开始或结束时检查它,而不是在中间。此外,您需要检查 32,因为有 32 位,并且您在打印每个位之前进行检查。目前最后一点会因此被砍掉。


有一个聪明的方法可以让你的程序做的工作比它需要的少。利用您从左到右显示的事实(即首先显示最高有效位)。设置了 MSB 后,可以将其视为二进制补码中的负数!

li     $t0, 32
li     $v0, 1

Loop:
bltz   $s0, Bit1
li     $a0, 0
j      Disp

Bit1:
li     $a0, 1

Disp:
syscall

Tail:
subi   $t0, $t0, 1
beqz   $t0, Exit
sll    $s0, $s0, 1
j      Loop

Exit:

【讨论】:

  • 通常每个位上的条件分支对性能来说更差。但是 MIPS 的 SLT 设置为小于(已签名)。 (mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html)。所以slt $a0, $s0, $zero$s0的符号位提取到$a0的低位(零扩展)。
【解决方案2】:

给定一个指向$a1 中足够大缓冲区末尾的指针,以及$a0 中的一个输入整数,此函数存储 ASCII 数字以形成一个字符串。

这使用 AND 来提取低位。它从低位到高位工作,所以我们从缓冲区的末尾向后存储,留下一个 ASCII 字符串以打印顺序。

.globl to_base2_end    # args: (unsigned a, char *buf_end)
to_base2_end:

  # Runs at least once, so we get "0" instead of the empty string for 0
.loop:                           # do {
    andi  $t0,  $a0, 1           #    extract the low bit
    ori   $t0,  $t0, '0'         #    ASCII digit

    addiu $a1,  $a1, -1
    sb    $t0,  ($a1)            #    *--buf = ASCII digit

    srl   $a0,  $a0, 1           #    a0 >>= 1
    bne   $a0, $zero,  .loop     # }while (a0!=0);

    move  $v0, $a1           # return pointer to the first digit
    jr   $ra

请注意,这会在第一次存储之前递减,因此您可以将指针传递给缓冲区末尾的 '\n'

您当然可以内联此循环,而不是将其用作可调用函数。它在整数为零时停止,而不是循环固定 32 次,因此它不会打印前导零。

这个算法是基本的base2特例
do { digit = a % base; } while(a /= base);

如果您要按生成的顺序打印数字,您可以使用

    slt   $t0,  $a0, $zero       #    t0 = 0 or 1 = high bit of a0
    sll   $a0,  $a0, 1

这将为您提供@JeffE 代码的无分支版本。但是如果你关心效率,一个写入整个字符串的系统调用比写入 32 个整数的 32 个系统调用要高效得多。 (当然,真正的操作系统没有 write-int 系统调用;这是 Mars/SPIM 的事情。)

【讨论】:

    猜你喜欢
    • 2017-04-12
    • 1970-01-01
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 2016-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多