如何在 MIPS 中实现 if(condition1 && condition2)?

How can I implement if(condition1 && condition2) in MIPS?

我写了下面的函数来检查一个字符是否是数字:

# IsDigit - tests a if a character a digit or not 
# arguments:
#   $a0 = character byte
# return value:
#   $v0 =   1 - digit
#           0 - not a digit
IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character
    bge $t0, $t1, condition1
condition1:
    ble $t0, $t2, condition2

    li $v0, 0
    j return

condition2:
    li $v0, 1

return:                                                                                 
    # return 
    jr $ra 

有没有更好的方法来做或写这个?

编辑:以下是version-2

IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character
    bge $t0, $t1, condition1

    j zero

condition1: 

    ble $t0, $t2, condition2

zero:
    li $v0, 0
    j return

condition2:
    li $v0, 1
    j return

return:                                                                                 
    # return 
    jr $ra 

Edit-2: 以下为version-3

IsDigit:
    lb $t0, ($a0) # obtain the character
    li $t1, 48 # '0' - character
    li $t2, 57 # '9' - character


    bge $t0, $t1, con1_fulfilled #bigger tha or equal to 0  
    j con1_not_fulfilled

con1_fulfilled:
    ble $t0, $t2, con2_fullfilled #less than or equal to 9
    j con2_not_fulfilled

con2_fullfilled:
    li $v0, 1
    j return

con1_not_fulfilled:
con2_not_fulfilled:
    li $v0, 0

return:                                                                     
    # return 
    jr $ra 

在一般情况下,您使用 2 个分支,这些分支经过 if() 正文。如果其中一个被采用,if 主体不会 运行。在汇编中,您通常希望使用 C 条件的否定,因为您要跳过循环体所以它不会 运行。您的更高版本向后执行,因此还需要无条件 j 指令,使您的代码更加复杂。

<= (le) 的反义词是 > (gt)。对于使用包含范围(le 和 ge)编写的 C,使用相同数值的 asm 应该使用排他范围(排除 equal 情况)在相反的条件下分支。或者您可以调整常量和 bge $t0, '9'+1 或其他任何内容,这在适合 16 位立即数的内容末尾很有用。

# this does assemble with MARS or clang, handling pseudo-instructions
# and I think it's correct.
IsDigit:
    lb  $t0, ($a0)    # obtain the character

    blt   $t0, '0', too_low    # if(   $t0 >= '0'  
    bgt   $t0, '9', too_high   #    && $t0 <= '9')
      # fall through into the if body
    li    $v0, 1
    jr    $ra                    # return 1

too_low:
too_high:                    # } else {
    li    $v0, 0

#end_of_else:
    jr    $ra                # return 0

如果这不是在函数的末尾,您可以从 if 主体的末尾 j end_of_else 跳过 else 块。或者在这种情况下,我们可以将 li $v0, 0 放在第一个 blt 之前,以填充加载延迟槽而不是停止管道。 (当然,一个真正的 MIPS 也有分支延迟槽,你不能有背靠背的分支。但是 bgt 无论如何是一个伪指令所以没有不会真的背 -到后面的分支。)

此外,我没有跳转到通用 jr $ra,而是简单地将 jr $ra 复制到另一个 return 路径。如果您有更多的清理工作要做,您可能会跳转到一个常见的 return 路径。否则尾部重复是简化分支的好事。


在这种特定情况下,您的条件是相关的:您正在进行范围检查,因此您只需要 1 sub 和 1 个与范围长度的无符号比较. 有关 ASCII 字符范围检查的更多信息,请参阅 What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa?

并且 因为您 return 是一个布尔值 0/1,所以您根本不想分支 ,而是使用 sltu将条件变为寄存器中的 0 或 1。 (这是 MIPS 使用的,而不是像 x86 或 ARM 那样的 FLAGS 寄存器)。无论如何,两个寄存器之间的 ble 之类的指令是 slt + bne 的伪指令; MIPS在硬件上确实有blezbltz,两个寄存器之间也有bnebeq


顺便说一句,你的 IsDigit 上的评论与代码不符:他们说 $a0 一个角色,但实际上你'重新使用 $a0 作为加载字符的指针。因此,您无缘无故地通过引用传递了 char,或者传递了一个字符串并获取了第一个字符。

# IsDigit - tests a if a character a digit or not 
# arguments:
#   $a0 = character byte (must be zero-extended, or sign-extended which is the same thing for low ASCII bytes like '0'..'9')
# return value:
#   $v0 = boolean: 1 -> it is an ASCII decimal digit in [0-9]

IsDigit:
    addiu   $v0, $a0, -'0'            # wraps to a large unsigned value if below '0'
    sltiu   $v0, $v0, 10              # $v0 = bool($v0 < 10U)  (unsigned compare)
    jr      $ra

MARS的assembler拒绝assemble -'0'作为立即数,你得写成-48-0x30。 clang 的 assembler 与 addiu $v0, $a0, -'0'.

没有问题

如果你写 subiu $v0, $a0, '0',MARS 使用脑残 lui+ori 构造 '0',因为它对于大多数 assemblers 不支持的扩展伪指令来说非常简单。 (MIPS 没有 subi 指令,只有 addi/addiu,它们都采用符号扩展立即数。)