The unsigned compare trick for range checks is good here, getting the job done with only sub/sltiu/beq, and giving you the ASCII->integer digit value as part of it.
x86 example. Like for checking for alphabetic characters, see What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa?
Zero-extend or sign-extend a character (a byte) into a 32-bit register; either one works because the ASCII 0..9 digit range is signed positive, and this range check correctly excludes all other 32-bit numbers.
# lbu $t0, x # for example
# ASCII character in $t0
addiu $t0, $t0, -0x30 # subtract '0' : range shift from '0'..'9' to 0..9
sltiu $t1, $t0, 10 # t1 = c < 10 unsigned
beqz $t1, non_digit # jump if c>=10
# fall through: it was a digit, 0..9 value in $t0
...
non_digit:
Input too low means t0 -= '0'
wraps to a large unsigned value. Input too high means it's (unsigned) above 9
after subtracting.
If you want to keep around the original character value as well as its integer digit value (if it's a digit), pick different registers.
MARS unfortunately doesn't support convenient ASCII character constants as part of numeric expressions, so it can't assemble addiu $t0, $t0, -'0'
to subtract '0'
.
You can write sltiu
/ beqz
as a bgeu $t0, 10, non_digit
pseudo-instruction.
You could write bgtu $t0, 9, non_digit
, but don't because MARS assembles that to 3 machine instructions (including addi
to materialize 9
in a register + sltu
between two regs), instead of 2. You can't save anything by using a register holding 9
or 10
setup before a loop, either: MIPS branch conditions only compare against zero, except for eq / ne. blt $t1, $t9, non_digit
would be a pseudo-instruction, too.
Branching only once is of course very good on a real MIPS with branch-delay slots.
You can of course use this as the loop condition for looping until you encounter a non-digit.