I am trying to convert binary to decimal in the MIPS language, using the MARS simulator. The program accepts a binary number, and then does the conversion, by multiplying (shift left by the number's place $t9). Another way of saying that is multiplying each 1 digit by 2 raised to the power of the place, and summing that result.
I do not have a very good understanding of how values are stored and communicated between ascii, decimal, and the problem is that the "sum" is coming out to be 40,000 something rather than the value of the binary number in decimal. What am I doing wrong here?
.data
msg1:
.asciiz "Enter a number in base 2 (-2 to quit): "
msg2:
.asciiz "\nResult: "
allOnes:
.asciiz "1111111111111111"
empty:
.space 16
newLine:
.asciiz "\n"
sum:
.space 16
sumMsg:
.asciiz "\nSUM: "
oneFound:
.asciiz "\nOne found\n"
zeroFound:
.asciiz "\nZero found\n"
.text
.globl main
main:
getNum:
li $v0,4 # Print string system call
la $a0,msg1 #"Please insert value (A > 0) : "
syscall
la $a0, empty
li $a1, 16 # load 16 as max length to read into $a1
li $v0,8 # 8 is string system call
syscall
la $a0, empty
li $v0, 4 # print string
syscall
li $t4, 0 # initialize sum to 0
startConvert:
la $t1, empty
li $t9, 16 # initialize counter to 16
firstByte:
lb $a0, ($t1) # load the first byte
blt $a0, 48, printSum
addi $t1, $t1, 1 # increment offset
subi $a0, $a0, 48 # subtract 48 to convert to int value
beq $a0, 0, isZero
beq $a0, 1, isOne
j convert #
isZero:
subi $t9, $t9, 1 # decrement counter
j firstByte
isOne: # do 2^counter
li $t8, 1 # load 1
sllv $t5, $t8, $t9 # shift left by counter = 1 * 2^counter, store in $t5
add $t4, $t4, $t5 # add sum to previous sum
move $a0, $t4 # load sum
li $v0, 1 # print int
syscall
subi $t9, $t9, 1 # decrement counter
j firstByte
convert:
printSum:
srlv $t4, $t4, $t9
la $a0, sumMsg
li $v0, 4
syscall
move $a0, $t4 # load sum
li $v0, 1 # print int
syscall
exit:
li $v0, 10 # exit system call
syscall
One issue is that you're decrementing $t9
twice per iteration. You should leave the first decrement (the one after subtracting 48) where it is, and remove the other two.
Another issue is that if you are parsing the string from left-to-right, then you need to take the length of the string into account at some point. Ideally, you'd be setting $t9
to the length of the string instead of 0 (on the second line after startConvert
), but we don't yet know the length of the string.
One option is to parse through the string beforehand to determine its length, and set $t9
to that value.
A more elegant way would be to do the whole thing in one pass: assume that the string is 16 characters long, including the delimiter (so, assign 16
to $t9
first). Suppose that the string is actually 5 characters long. Then after your loop, the sum will be too high by a factor of 2^(16-5) = 2^11
. But observe that the final value of $t9
is 11
. So you can fix the error by shifting the sum register to the right by $t9
many bits.
Finally, it looks like MARS is putting a character of 0x0A
(this is from pressing Enter) onto the end of the string (except for when you use all 15 characters; then it uses 0x00
instead, because it automatically stops after the 15th character before giving you a chance to press Enter). So on the beqz
line, instead of comparing to 0
, instead check if $a0 < 48
. This will take care of both cases, whether the delimiter is 0x00
or 0x0A
.
Here's my version with the fixes:
.data
msg1:
.asciiz "Enter a number in base 2 (-2 to quit): "
msg2:
.asciiz "\nResult: "
allOnes:
.asciiz "1111111111111111"
empty:
.space 16
newLine:
.asciiz "\n"
sum:
.space 16
sumMsg:
.asciiz "\nSUM: "
oneFound:
.asciiz "\nOne found\n"
zeroFound:
.asciiz "\nZero found\n"
.text
.globl main
main:
getNum:
li $v0,4 # Print string system call
la $a0,msg1 #"Please insert value (A > 0) : "
syscall
la $a0, empty
li $a1, 16 # load 16 as max length to read into $a1
li $v0,8 # 8 is string system call
syscall
la $a0, empty
li $v0, 4 # print string
syscall
li $t4, 0 # initialize sum to 0
startConvert:
la $t1, empty
li $t9, 16 # initialize counter to 16
firstByte:
lb $a0, ($t1) # load the first byte
blt $a0, 48, printSum # I don't think this line works
addi $t1, $t1, 1 # increment offset
subi $a0, $a0, 48 # subtract 48 to convert to int value
subi $t9, $t9, 1 # decrement counter
beq $a0, 0, isZero
beq $a0, 1, isOne
j convert #
isZero:
j firstByte
isOne: # do 2^counter
li $t8, 1 # load 1
sllv $t5, $t8, $t9 # shift left by counter = 1 * 2^counter, store in $t5
add $t4, $t4, $t5 # add sum to previous sum
move $a0, $t4 # load sum
li $v0, 1 # print int
syscall
j firstByte
convert:
printSum:
srlv $t4, $t4, $t9
la $a0, sumMsg
li $v0, 4
syscall
move $a0, $t4 # load sum
li $v0, 1 # print int
syscall
exit:
li $v0, 10 # exit system call
syscall