В общем случае вы используете 2 ветви, которые идут до тела if()
.Если один из них взят, тело if
не работает.В сборке вы обычно хотите использовать отрицание условия C, потому что вы перепрыгиваете через тело цикла, чтобы оно не запускалось.Ваша более поздняя версия делает это в обратном направлении, поэтому также необходимы безусловные инструкции j
, что делает ваш код более сложным.
Противоположностью <=
(le) является >
(GT).Для C, написанного для использования включающих диапазонов (le и ge), 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
Если это не было в концефункции, вы можете j end_of_else
с конца тела if
, чтобы перепрыгнуть через блок else
.Или в этом случае мы могли бы поставить li $v0, 0
перед первым blt
, чтобы заполнить слот задержки загрузки вместо остановки конвейера.(Конечно, настоящий MIPS также имеет слоты задержки ветвления, и у вас не может быть параллельных ветвей. Но bgt
- это в любом случае псевдоинструкция, так что на самом деле нет обратной связи.назад разветвляется.)
Кроме того, вместо того, чтобы перейти к общему jr $ra
, я просто продублировал jr $ra
в другом пути возврата.Если вам нужно больше очистить, вы можете перейти к одному общему пути возврата.В противном случае дублирование хвоста - хорошая вещь для упрощения ветвления.
В этом конкретном случае ваши условия связаны: вы делаете проверку диапазона, поэтому вам нужно только 1 sub
и затем 1 без знака - сравнение с длиной диапазона. См. В чем идея ^ = 32, которая преобразует строчные буквы в верхние и наоборот? для получения дополнительной информации о проверках диапазона в ASCIIсимволов.
и , так как вы возвращаете логическое 0/1, вы вообще не хотите переходить , а вместо этого используйте sltu
, чтобы превратить условие в 0 или1 в регистрах.(Это то, что MIPS использует вместо регистра FLAGS, такого как x86 или ARM).Инструкции типа 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 создаст '0'
, используя braindead lui + ori, потому что это очень упрощенно для расширенных псевдоинструкций, которые большинство ассемблеров не делаютслужба поддержки.(MIPS не имеет инструкции subi
, только addi
/ addiu
, обе из которых требуют немедленного расширения знака).