Convert Binary to Decimal in MIPS, Assembly MARS

user2827214 picture user2827214 · Apr 25, 2014 · Viewed 11.6k times · Source

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

Answer

Jeff picture Jeff · Apr 25, 2014

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