0

I'm writing a program that asks the user which temperature they have and then takes that input and converts and outputs all four temperatures. I need help getting my user's input read-in so that it will work in my branch, beq. I can't get it to recogize the input 'f' as equal to the stored version.

.data    
temptype: .asciiz "Enter temperature type i.e. f, c, k, r: "   
tempdegree: .asciiz "\n Enter degrees: "   
space: .space 2
tempx: .asciiz "Your temperature in celsius is: "
tempc: .asciiz "\nYour temperature in celsius is: "
tempf: .asciiz "\nYour temperature in fahrenheit is: "
tempk: .asciiz "\nYour temperature in kelvin is: "
tempr: .asciiz "\nYour temperature in rankine is: :"
kr: .float 459.67

.globl main

.text   
    main:   

        li $v0, 4   
        la $a0, temptype   
        syscall

        li $v0, 8
        la $a0, space
        #li $a1, 2
        move $t0, $a0
        syscall

        li $t1, 102
        #li $t1, 99
        #li $t1, 107
        #li $t1, 114
        syscall

        beq $t0, $t1, fahrenheit
        #beq $t0, $t1, celsius
        #beq $t0, $t1, kelvin
        #beq $t0, $t1, rankine
        syscall

        li $v0,10
        syscall 

    fahrenheit:

        li $v0, 4   
        la $a0, tempdegree   
        syscall

        li $v0, 5
        syscall

        move $t0, $v0

        li $v0, 4 
        la $a0, tempf
        syscall

        move $a0, $t0 
        li $v0, 1 
        syscall 
1
  • 1
    Did you single-step through your code in the built-in debugger? Did you check the docs for the syscall you're using to see whether it returns a character in a register, or whether it stores it in memory? You might be comparing a pointer against a number or something. (You didn't comment your code, and I don't know the MIPS simulator syscall numbers off the top of my head.) Anyway use the debugger, it will make it so much easier to be able to check what's actually in each register that beq is looking at. Commented Oct 14, 2017 at 4:14

1 Answer 1

3

MIPS CPU (and none of any other common one) has no "compare strings" instruction, string is not native type of CPU and the instructions deal only with native types, like words and bytes.

"String" is certain amount (either defined somewhere, or using terminator character at end of data) of consecutive characters. What is "one character" depends on used encoding, in your case (MARS simulator, and simple practice of asm programming) you can stick with the old ASCII encoding, where single character is exactly one single byte. (JFYI: with modern SW you will mostly work in UTF8 encoding, like this web page does, where single character can have different amount of bytes, depending on which glyph you encode, which makes programming any string algorithm over UTF8 encoded string lot more fun than your current task. Quite often too much fun.)

Now as the CPU registers are of "word" size, that means they are 32 bits "wide", i.e. they can hold at most 4 ASCII characters at once (4 bytes), so using registers to store whole string would allow only for very sho. str. and_ noth else. You can do that, but it's not practical (except the beq would work, because you can compare word value 0x30303030 = "0000" against 0x31313131 = "1111" with beq).

Thus most of the time while programming in MIPS beginners assembly the "strings" are following pattern: some register contains memory address pointing to the first letter of string (first byte of string), and the very last "character" of string is not any letter, but value zero, the so called "null terminator".

When you want to compare strings then, you create loop, which starts with two pointers (to the two strings = to the two first letters). Load byte from both addresses into some temporary register (i.e. loading first letter of both), compare that, against, if they differ, the strings differ. If equal, check for zero (both strings ended = they are equal). If not zero, advance both addresses by one, so they will point to the next letter, and loop to the beginning.

But in your case the user can enter only single letter, and you want to compare only single letter, so writing whole loop is sort of abundant effort, you can just load that single letter and compare that.

So reading your source from top, these lines will get my comments:

#li $a1, 2

is commented out why? You should use that to limit the syscall (I think without setting anything the default value may be zero, so no input happens). Also you may be interested into syscall(v0=12) "read character" instead of "read string", but I'm not sure how that is presented toward user in MARS (user experience related), but let's stick with service v0=8 "read string" and 2 byte long buffer.

Now after syscall returns (user did enter the letter "f"), the memory at address space will contain two bytes set by syscall: 102, 0.

li $t1, 102

Looks familiar, but difficult to read for other programmer, with MARS assembler you can use also this way of writing that number: li $t1, 'f' - the simple apostrophes tells assembler you want value of single ASCII character ('ab' is error in MARS, only single char can be used, some other assemblers may translate the 'ab' as two byte value)

Next uncommented instruction is:

syscall

And here you are asking for which MARS service? You didn't set any value in v0, nor you need any service, so if you would single step over your code in debugger, this should make no sense to you, if you reason what is going happen with each instruction.

Then comes beq $t0, $t1, fahrenheit.

At that point the t1 is equal to 'f', and t0 is equal to address of first byte of that buffer, also aliased as space symbol during compilation, which is equal to some 32 bit value, probably similar to something like 0x100000c. The values 0x100000c vs 102 certainly doesn't equal, so that beq will never jump to label fahrenheit.

To compare that first letter inside buffer, first fetch its value from memory, like lb $t2, ($t0), that loads byte value from address in t0 (advanced info: lb will sign-extend the 8 bit value to 32 bit value. Basic printable characters of ASCII are all smaller than 128, so you will not need to deal with negative values, but if letter 'f' would encode as 140, using lb to load that value into t2 would produce 32 bit value -116, not 140 ... as I wrote, basic ASCII is only 7 bit, so only positive values, which work as expected, 102 is loaded as 102).

Then you can have more success with beq $t2, $t1, fahrenheit, as now it will compare ASCII character against ASCII character.

You can also use MARS MIPS assembly pseudo instruction beq $t2, 'f', fahrenheit. MARS will compile that as two native instructions:

addi $at, $zero, 102 # 102 = 'f', $at = $1, $zero = $0
beq  $at, $t2, fahrenheit

Saving you some typing, which is good in programming, as long as it makes sense while reading (once you will start to shorten your source code just for the purpose of short writing, you are doing it wrong, in programming the source code is written to be being read, the writing cost is negligible compared to the reading costs). In this case beq $t2, 'f', label looks quite readable to me, so I would prefer that.

And that should be enough to answer both your questions, the explicit one (how to compare strings = in a loop, character by character), and the implicit one (how to compare that single letter from user against 'f').

Sign up to request clarification or add additional context in comments.

5 Comments

minor quibble: x86 has repe cmpsb, which does compare strings, and x86 is pretty common. It's often not the highest-performance way to compare strings, and you have to know the length of one of the strings otherwise it will run past the 0 byte at the end of both if they're equal. stos/movs/lods/cmps/scas are called string instructions.
@PeterCordes fair quibble... but by the context of this question and expected skill level of the OP I think keeping it here in comments is probably best, I already tried to put the extra info into parentheses with disclaimer, still I'm somewhat worried the OP will be like TLDR even over important bits.... OP: if TLDR, here is summary: use debugger
@Ped7g Thank you! I got it working. Your response was very informative.
@Ped7g How would you recommend changing 459 to 459.67 and 273 to 273.15. I added, lwc1 $f1, number1 lwc1 $f2, number2 and use mtc1 $t0, $f4 cvt.s.w $f4, $f4 But would I have to change the code in each time or is there a better way?
@HarmonyKunzler I'm not sure what you are asking, your code would load f1 with value in memory at number1, f2 with number2, and f4 with converted integer from t0, all of that looks good. Are you asking how to use either number1 or number2? In original post you planned to branch according to user input, you may in the branch-code load particular fX with desired constant for conversion, and then jump back to main branch, where the conversion calculation will be done and output printed. No idea which part is puzzling you, try something, watch it in debugger, post new question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.