我编写了以下函数来检查字符是否为数字:
# 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
答案 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的硬件确实有blez
和bltz
,并且在两个寄存器之间也有bne
和beq
。
顺便说一句,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
,这两个指令都使用符号扩展的立即数。)