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

时间:2019-03-29 00:36:56

标签: assembly mips procedure subroutine

我编写了以下函数来检查字符是否为数字:

# 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 

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

编辑:以下是版本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 

编辑2 :以下是版本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 

1 个答案:

答案 0 :(得分:1)

在一般情况下,您使用2个分支,它们经过if()主体。如果采用任何一种,则if正文不会运行。在汇编中,您通常要使用C条件的否定,因为您要跳过循环主体,因此它不会运行。您的新版本会向后执行,因此也需要无条件的j指令,从而使您的代码更加复杂。

<=(le)相反的是>(gt)。对于编写为使用包含范围(le和ge)的C,使用相同数值的asm应该在使用排除范围(不包括eq等于)的相反条件下分支。或者,您可以调整常量和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

如果这不是在函数的末尾,则可以从j end_of_else主体的末尾if跳过else块。或者,在这种情况下,我们可以将li $v0, 0放在第一个blt的前面,以填充负载延迟时隙,而不用停滞管道。 (当然,真正的MIPS也具有分支延迟插槽,并且您不能具有背对背的分支。但是bgt始终是伪指令,因此不会真正被向后分支。)

此外,我没有跳到常见的jr $ra,而是将jr $ra复制到了另一个返回路径中。如果您需要执行更多清理工作,则可以跳到一条常见的返回路径。否则,尾部重复对于简化分支是一件好事。


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

并且由于返回的是布尔值0/1,所以根本不希望分支,而是使用sltu将条件转换为0或1一个寄存器。 (这就是MIPS用来代替x86或ARM之类的FLAGS寄存器的功能)。无论如何,两个寄存器之间的ble之类的指令无论如何都是slt + bne的伪指令。 MIPS的硬件确实有blezbltz,并且在两个寄存器之间也有bnebeq


顺便说一句,IsDigit上的注释与代码不匹配:他们说$a0 一个字符,但实际上您在使用{{1 }}作为加载字符的指针。因此,您无缘无故通过引用传递$a0 ,或者传递字符串并采用第一个字符。

char

MARS的汇编器拒绝立即汇编# 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 ,您必须将其编写为-'0'-48。 clang的汇编器对-0x30没问题。

如果您编写addiu $v0, $a0, -'0',则MARS会使用Braindead lui + ori构造subiu $v0, $a0, '0',因为对于大多数汇编程序不支持的扩展伪指令而言,它非常简单。 (MIPS没有'0'指令,只有subi / addi,这两个指令都使用符号扩展的立即数。)