如何在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
有没有更好的方法来编写或编写此代码?
编辑:以下是版本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
在一般情况下,您使用2个分支,这些分支经过if()
主体.如果采用任何一种,则if
主体将不运行.在汇编中,您通常要使用C条件的否定,因为您要跳过循环主体,因此它不会运行.您的新版本会向后执行此操作,因此也需要无条件的j
指令,从而使您的代码更加复杂.
<=
(le)的反面是>
(>).对于编写为使用包含范围(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
复制到了另一个返回路径中.如果您需要执行更多清理工作,则可以跳到一条常见的返回路径.否则,尾部重复对于简化分支是一件好事.
在这种特定情况下,您的条件与之相关:您正在进行范围检查,因此只需要1个sub
,然后再对范围长度进行1个无符号比较. ^ = 32背后的想法是什么,将小写字母转换为大写字母,反之亦然?了解有关ASCII字符的范围检查的更多信息.
并且由于返回的是布尔值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的汇编器拒绝立即汇编-'0'
,您必须将其编写为-48
或-0x30
. clang的汇编器对addiu $v0, $a0, -'0'
没问题.
如果您编写subiu $v0, $a0, '0'
,则MARS会使用Braindead lui + ori构造'0'
,因为对于大多数汇编程序不支持的扩展伪指令而言,它非常简单. (MIPS没有subi
指令,只有addi
/addiu
,它们都使用符号扩展的立即数.)
I have written the following function to check whether a character is a digit or not:
# 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
Is there any better way to do or write this?
Edit: The following is the 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: the following is 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
In the general case, you use 2 branches that go to past the if()
body. If either one is taken, the if
body doesn't run. In assembly, you usually want to use the negation of the C condition, because you're jumping past the loop body so it doesn't run. Your later version does it backwards so also need unconditional j
instructions, making your code extra complicated.
The opposite of <=
(le) is >
(gt). For C written to use inclusive ranges (le and ge), asm using the same numerical values should branch on the opposite conditions using exclusive ranges (that exclude the eq
ual case). Or you can adjust your constants and bge $t0, '9'+1
or whatever, which can be useful right at the end of what fits into a 16-bit immediate.
# 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 this wasn't at the end of a function, you could j end_of_else
from the end of the if
body to jump over the else
block. Or in this case, we could have put the li $v0, 0
ahead of the first blt
, to fill the load delay slot instead of stalling the pipeline. (Of course a real MIPS also has branch-delay slots, and you can't have back-to-back branches. But bgt
is a pseudo-instruction anyway so there aren't wouldn't really be back-to-back branches.)
Also, instead of jumping to a common jr $ra
, I simply duplicated the jr $ra
into the other return path. If you had more cleanup to do, you might jump to one common return path. Otherwise tail duplication is a good thing to simplify the branching.
In this specific case, your conditions are related: you're doing a range-check so you only need 1 sub
and then 1 unsigned-compare against the length of the range. See What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa? for more about range-checks on ASCII characters.
And since you're returning a boolean 0/1, you don't want to branch at all, but rather use sltu
to turn a condition into a 0 or 1 in a registers. (This is what MIPS uses instead of a FLAGS register like x86 or ARM). Instructions like ble
between two registers are pseudo-instructions for slt
+ bne
anyway; MIPS does have blez
and bltz
in hardware, as well as bne
and beq
between two registers.
And BTW, the comments on your IsDigit
don't match the code: they say that $a0
is a character, but actually you're using $a0
as a pointer to load a character. So you're passing a char
by reference for no apparent reason, or passing a string and taking the first character.
# 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's assembler refuses to assemble -'0'
as an immediate, you have to write it as -48
or -0x30
. clang's assembler has no problem with addiu $v0, $a0, -'0'
.
If you write subiu $v0, $a0, '0'
, MARS constructs '0'
using a braindead lui+ori, because it's very simplistic for extended pseudo-instructions that most assemblers don't support. (MIPS doesn't have a subi
instruction, only addi
/addiu
, both of which take sign-extended immediates.)
这篇关于如何在MIPS中实现if(condition1&& condition2)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!