如何在 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 应该使用排他范围(排除 eq
ual 情况)在相反的条件下分支。或者您可以调整常量和 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在硬件上确实有blez
和bltz
,两个寄存器之间也有bne
和beq
。
顺便说一句,你的 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
,它们都采用符号扩展立即数。)
我写了下面的函数来检查一个字符是否是数字:
# 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 应该使用排他范围(排除 eq
ual 情况)在相反的条件下分支。或者您可以调整常量和 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在硬件上确实有blez
和bltz
,两个寄存器之间也有bne
和beq
。
顺便说一句,你的 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
,它们都采用符号扩展立即数。)