954,479 Members — Technology Publication meets Social Media
Username:
Password:
Lost login information?
Have something to say? Contribute New Article Reply to this Article

MIPS ASSEMBLY Problem with branch being bypassed

I've done this to play a song that allows you to select instruments. Can anyone tell me why this is hanging?
.data
instrumentPrompt:.asciiz "Select instrument (piano,strings,or guitar): "
octavePrompt: .asciiz "Select octave (lo,med,or hi): "
msg2:.asciiz "\nInvalid instrument"
msg3:.asciiz "\nInvalid octave"
instrumentspace: .space 20
octavespace: .space 20
instrpiano: .asciiz "piano\n"
instrguitar: .asciiz "guitar\n"
instrstrings: .asciiz "strings\n"
instrquit: .asciiz "quit\n"
octavelow: .asciiz "lo\n"
octavemed: .asciiz "med\n"
octavehigh: .asciiz "hi\n"
octavequit: .asciiz "quit\n"

.text
.globl main
main:

li $s1,0 # Probably not needed but set the default instrument to piano
li $s2,0 # Probably not needed but set the default tone mutator to 0

setinstrumentprompt:
li $v0,4
la $a0,instrumentPrompt
syscall
li $v0,8
la $a0,instrumentspace
addi $a1,$zero,20
syscall #got string 1

la $a0,instrumentspace #pass address of instrumentspace
la $a1,instrpiano #pass address of octave1
jal isvalidinput #call isvalidinput
beq $v0,$zero,setpiano #check result and branch if valid
beq $v0,$zero,getoctave

la $a1,instrguitar #pass address of octave2
jal isvalidinput #call isvalidinput
beq $v0,$zero,setguitar #check result and branch if valid
beq $v0,$zero,getoctave

la $a1,instrstrings #pass address of octave3
jal isvalidinput #call isvalidinput
beq $v0,$zero,setstrings #check result and branch if valid
beq $v0,$zero,getoctave

la $a1,instrquit #pass address of octavespace
jal isvalidinput #call isvalidinput
beq $v0,$zero,endsong #check result and quit because user typed quit

bne $v0,$zero,setinstrumentprompt # Since they didn't quit and no match go back to prompt


getoctave:
li $v0,4
la $a0,octavePrompt
syscall
li $v0,8
la $a0,octavespace
addi $a1,$zero,20
syscall #got string 1

la $a0,octavespace #pass address of instrumentspace
la $a1,octavelow #pass address of octave1
jal isvalidinput #call isvalidinput
beq $v0,$zero,setlo #check result and branch if valid
beq $v0,$zero,playsong

la $a1,octavemed #pass address of octave2
jal isvalidinput #call isvalidinput
beq $v0,$zero,setmed #check result and branch if valid
beq $v0,$zero,playsong

la $a1,octavehigh #pass address of octave3
jal isvalidinput #call isvalidinput
beq $v0,$zero,sethigh #check result and branch if valid
beq $v0,$zero,playsong

la $a1,octavequit #pass address of octavespace
jal isvalidinput #call isvalidinput
beq $v0,$zero,endsong #check result and quit because user typed quit

bne $v0,$zero,getoctave # Since they didn't quit and no match go back to prompt


setpiano:
li $s1,0
j getoctave

setguitar:
li $s1,24
j getoctave

setstrings:
li $s1,40
j getoctave

setlo:
li $s2,-12
jr $ra

setmed:
li $s2,0
jr $ra

sethigh:
li $s2,12
jr $ra

ok:
li $v0,4
la $a0,msg3
syscall
exit:
li $v0,10
syscall

isvalidinput:
#The address of the each member of the option list is stored in a1 for comparison
#With input input from user
add $t0,$zero,$zero
add $t1,$zero,$a0
add $t2,$zero,$a1
bytecheck:
lb $t3($t1) #load a byte from each string
lb $t4($t2)
beqz $t3,checkt2 #instrumentspace end
beqz $t4,giveup
slt $t5,$t3,$t4 #compare two bytes
bnez $t5,giveup

addi $t1,$t1,1 #t1 points to the next byte of instrumentspace
addi $t2,$t2,1
j bytecheck

giveup:
addi $v0,$zero,1
jr $ra

checkt2:
bnez $t4,giveup
add $v0,$zero,$zero



playsong:

li $v0,33 #system service for MIDI output
add $a2,$zero,$s1
li $a3,127 #max volume
syscall

# The first sequence of notes. Repeated to play the last stanza


startsong:

li $t0,0 #60 is middle C
jal play_short

li $t0,7 #67 is G above middle C
jal play_short

li $t0,9 #69 is A above middle C
jal play_short

li $t0,7 #67 is G
jal play_long

li $t0,5 #69 is A above middle C
jal play_short

li $t0,4 #69 is A above middle C
jal play_short

li $t0,2 #69 is A above middle C
jal play_short

li $t0,0
jal play_long

# Skip to the endsong label if you've already played this twice

bgtz $t1,endsong

# This is the bridge

bridge:
bgt $t2,$t3,repeatt
add $t2,$t2,$t3

li $t0,7
jal play_short

li $t0,5
jal play_short

li $t0,4
jal play_short

li $t0,2
jal play_long

b bridge

# Increments the cycle counter and jumps to the beginning

repeatt:
add $t1,$t1,$t3
b startsong

# From the first stanza on second play through

endsong:

li $v0,10 #returning control to OS
syscall

play_short:
addu $a0,$t0,$t4
addu $a0,$a0,$s2
li $a1,400 #duration in milliseconds
syscall
syscall
jr $ra

play_long:
addu $a0,$t0,$t4
addu $a0,$a0,$s2
li $a1,800 #duration in milliseconds
syscall
jr $ra

phillytennisguy
Newbie Poster
3 posts since Oct 2010
Reputation Points: 10
Solved Threads: 0
 

First off, what hardware and/or emulator is this for (PS2, SPIM, MARS, MIPSsym, etc.)? Keep in mind that, if this is for actual MIPS hardware and not SPIM, then you have to take the branch and load delay slots into account (unless the assembler is doing that for you automatically, but even then it's something you need to be aware of).

Second, where is it hanging? Does the program do anything unusual before it hangs?

Third, were you aware that you syscall twice in a row in play_short?

And some advice: if your assembler allows them (I know that MARS doesn't), you might find it worthwhile to define equates which name the various system calls, as well as for the standard stack offsets. Here are the ones for SPIM which I have used in the past, based on those provided by my old assembly language professor:

################################################################################
#stack offsets
fp.ra = 4
fp.a0 = 8
fp.a1 = 12
fp.a2 = 16
fp.a3 = 20
fp.s0 = -4
fp.s1 = -8
fp.s2 = -12
################################################################################
#System calls
print_int   = 01 #$a0  = integer
print_float = 02 #$f12 = float
print_double= 03 #$f12 = double
print_string= 04 #$a0  = string
read_int    = 05 #returns integer in $v0
read_float  = 06 #returns float   in $f0
read_double = 07 #returns double  in $f0
read_string = 08 #$a0 = buffer, $a1 = length
malloc      = 09 #$a0 = size of request returns pointer in $v0
Exit        = 10
print_char  = 11 #$a0 = char
read_char   = 12 #returns char (in $v0)
open        = 13 #$a0 = filename (string), $a1 =flags, $a2 = mode 0=read 1= wr 2=rw 8=append
                 #         returns file descriptor (in $v0)
read        = 14 #$a0 = file descriptor, $a1 = buffer, $a2 = length
                 #         returns num chars read (in $v0 )
write       = 15 #$a0 = file descriptor, $a1 = buffer,$a2 = length
                 #         returns num chars written (in $v0 )
close       = 16 #$a0 = file descriptor
exit2       = 17 #$a0 = result
################################################################################

You will probably need to adjust them somewhat, but it looks to me as if at least the syscall to exit to the OS is the same, so to use that as an example:

li    $v0, exit    #returning control to OS
    syscall

The stack offset would be used in function calls, for instances where a call function then calls another in turn, and you need to save the function's activation record on the stack.

addi $sp, $sp -8    # set aside stack space for the frame pointer and the return address
sw $fp, 0($sp)            # store the old frame pointer
addi $fp, $sp, 0          # set the frame pointer - in this case to the SP, but not always
sw $ra, fp.ra($fp)        # store the return address to the standard offset
Schol-R-LEA
Posting Pro
556 posts since Oct 2010
Reputation Points: 254
Solved Threads: 85
 

I've made some progress. It doesn't hang anymore, but will only accept piano and low for instrument and octave, respectively. It only gets through the first full stanza of the song and then exits, but that, I think will be a relatively easy problem to solve. My problem is understanding the basics of how to

1. Given two inputs ($v0 = 8), accept each input comparing them to a predefined
list of matches, find a match and store some value in a memory location so
that it can be used later by a routine that, in this case, sets the pitch and
instrument for the midi service ($v0 = 33)

2. in the midi-play routine read the values from memory and assign then to
$a0 and $a2 to set instrument and tone.

3. control $ra so that jr returns to the place intended

Most of my programming experience has been with higher level languages so I'm struggling a bit

.data
instrumentPrompt: .asciiz "Select instrument (piano,strings,or guitar): "
octavePrompt: .asciiz "Select octave (lo,med,or hi): "
msg2:.asciiz "\nInvalid instrument"
msg3:.asciiz "\nInvalid octave"
instrumentspace: .space 8
octavespace: .space 8
instrpiano: .asciiz "piano\n"
instrguitar: .asciiz "guitar\n"
instrstrings: .asciiz "strings\n"
instrquit: .asciiz "quit\n"
octavelow: .asciiz "low\n"
octavemed: .asciiz "med\n"
octavehigh: .asciiz "high\n"
octavequit: .asciiz "quit\n"

.text
.globl main
main:

addi $6,$zero,0 # Probably not needed but set the default instrument to piano
addi $7,$zero,0 # Probably not needed but set the default tone mutator to 0

getinstrument:
bgtz $t6,getoctave
li $v0,4
la $a0,instrumentPrompt
syscall
li $v0,8
la $a0,instrumentspace
addi $a1,$zero,20
syscall #got string 1

# la $a0,instrumentspace #pass address of instrumentspace
la $a1,instrpiano #pass address of octave1
jal checkinsinput #call isvalidinput
beq $v0,$zero,setpiano #check result and branch if valid
bgt $t6,$zero,getoctave

la $a0,instrumentspace
addi $a1,$zero,8
la $a1,instrguitar #pass address of guitar
jal checkinsinput #call isvalidinput
beq $v0,$zero,setguitar #check result and branch if valid
bgt $t6,$zero,getoctave

# la $a0,instrumentspace
# addi $a1,$zero,20
la $a1,instrstrings #pass address of strings
jal checkinsinput #call isvalidinput
beq $v0,$zero,setstrings #check result and branch if valid
bgt $t6,$zero,getoctave

beqz $t6,getinstrument # Since they didn't quit and no match go back to prompt


getoctave:
add $a0, $zero, $zero
add $a1, $zero, $zero
li $v0,4
la $a0,octavePrompt
syscall
li $v0,8
la $a0,octavespace
addi $a1,$zero,8
syscall #got string 1

la $a1,octavelow #pass address of octave1
jal checkoctinput #call isvalidinput
beq $v0,$zero,setlo #check result and branch if valid
# beq $v0,$zero,playsong

la $a1,octavemed #pass address of octave2
jal checkoctinput #call isvalidinput
beq $v0,$zero,setmed #check result and branch if valid
# beq $v0,$zero,playsong

la $a1,octavehigh #pass address of octave3
jal checkoctinput #call isvalidinput
beq $v0,$zero,sethigh #check result and branch if valid
# beq $v0,$zero,playsong

la $a0,octavespace #pass address of instrumentspace
la $a1,octavequit #pass address of octavespace
jal checkoctinput #call isvalidinput
beq $v0,$zero,endsong #check result and quit because user typed quit

bne $v0,$zero,getoctave # Since they didn't quit and no match go back to prompt


setpiano:
addi $t6,$zero,2
j getoctave

setguitar:
addi $t6,$zero,24
j getoctave

setstrings:
addi $t6,$zero,40
j getoctave

setlo:
li $t7,48
j playsong

setmed:
li $t7,60
j playsong

sethigh:
li $t7,72
j playsong


# li $v0,10
# syscall

checkinsinput:

#The address of the each member of the option list is stored in a1 for comparison
#With input input from user
add $t0,$zero,$zero
add $t1,$zero,$a0
add $t2,$zero,$a1
iloop:
lb $t3($t1) #load a byte from each string
lb $t4($t2)
beqz $t3,icheck #instrumentspace end
beqz $t4,icheck
slt $t5,$t3,$t4 #compare two bytes
bnez $t5,getinstrument

addi $t1,$t1,1 #t1 points to the next byte of instrumentspace
addi $t2,$t2,1
j iloop

icheck:
add $v0,$zero,$zero
jr $ra

checkoctinput:

#The address of the each member of the option list is stored in a1 for comparison
#With input input from user
add $t0,$zero,$zero
add $t1,$zero,$a0
add $t2,$zero,$a1
oloop:
lb $t3($t1) #load a byte from each string
lb $t4($t2)
beqz $t3,ocheck #instrumentspace end
beqz $t4,ocheck
slt $t5,$t3,$t4 #compare two bytes
bnez $t5,getoctave

addi $t1,$t1,1 #t1 points to the next byte of instrumentspace
addi $t2,$t2,1
j oloop

ocheck:
add $v0,$zero,$zero
jr $ra

playsong:

li $t4,60
li $v0,33 #system service for MIDI output
addu $a2,$zero,$t6
li $a3,127 #max volume
# syscall

# The first sequence of notes. Repeated to play the last stanza


startsong:

li $t0,0 #60 is middle C
jal play_short

li $t0,7 #67 is G above middle C
jal play_short

li $t0,9 #69 is A above middle C
jal play_short

li $t0,7 #67 is G
jal play_long

li $t0,5 #69 is A above middle C
jal play_short

li $t0,4 #69 is A above middle C
jal play_short

li $t0,2 #69 is A above middle C
jal play_short

li $t0,0
jal play_long

# Skip to the endsong label if you've already played this twice

bgtz $t1,endsong

# This is the bridge

bridge:
bgt $t2,$t3,repeatt
add $t2,$t2,$t3

li $t0,7
jal play_short

li $t0,5
jal play_short

li $t0,4
jal play_short

li $t0,2
jal play_long

b bridge

# Increments the cycle counter and jumps to the beginning

repeatt:
add $t1,$t1,$t3
b startsong

# From the first stanza on second play through


play_short:
addu $a0,$t0,$t7
li $a1,400 #duration in milliseconds
syscall
syscall
jr $ra

play_long:
addu $a0,$t0,$t7
li $a1,800 #duration in milliseconds
syscall
jr $ra

endsong:

li $v0,10 #returning control to OS
syscall

phillytennisguy
Newbie Poster
3 posts since Oct 2010
Reputation Points: 10
Solved Threads: 0
 

To control $ra, what you to do is use the system stack to store it (as well as the frame pointer and any s registers which are used in a subroutine) and then restore it back to it's previous value when the routine ends. There is a convention for doing this established by the MIPS designers and while you don't need to follow it, it makes it much easier to use a routine with other existing ones if you do.

The overall approach is the same as the one I discussed in my previous post. What you do is, at the beginning of a function that will need to call other functions in turn, you increment the stack by an amount sufficient to hold the frame pointer, the return address pointer, and any s registers (the s stands for 'saved') which the function uses (to ensure that the previous functions which used them don't lose them), and any argument registers which the function may need to change when calling other functions. Before the function exits, you need to reverse this, restoring the saved registers and then resetting the stack pointer.

As I said earlier, if you can, it is helpful to define some equates which hold the offsets for the frame pointer; the offsets in questions are

#stack offsets
fp.ra = 4
fp.a0 = 8
fp.a1 = 12
fp.a2 = 16
fp.a3 = 20
fp.s0 = -4
fp.s1 = -8
fp.s2 = -12
fp.s3 = -16
fp.s4 = -20
fp.s5 = -24
fp.s6 = -28
fp.s7 = -32

These are the version that I use with SPIM; depending on the assembler you're using, you may need to change these.

Let's apply these to a read example. Here is a function I wrote for a program, which reads in a line of text and then calls a function which parses that text:

###############################
# (char*, object*) read_line(char*) 
# Read a line of text into the input buffer and parse it
###############################

read_line:
    addi $sp, $sp, -12  # set aside 12 bytes on the stack
    sw $fp, 0($sp)      # save the old frame pointer
    addi $fp, $sp, 0    # set the frame pointer to the stack, plus the number of s-regs
    sw $ra, fp.ra($fp)  # save the current return address
    sw $a0, fp.a0($fp)  # a0 == pointer to current position in input buffer

    li $v0, read_string # read_string == 8 - the equates are easier to remember 
    syscall             # than the system call numbers, as a rule

    lw $a0, fp.a0($fp)
    jal parse_object

    lw $a0, fp.a0($fp)  # restore the values of the saved registers
    lw $ra, fp.ra($fp)
    lw $fp, 0($sp)
    addi $sp, $sp, 12   # reset the stack
    jr $ra

(The version I use is slightly different, as I am writing the code with the branch and load delays.)

Schol-R-LEA
Posting Pro
556 posts since Oct 2010
Reputation Points: 254
Solved Threads: 85
 

I've done some checking, and it seems that under the MARS program (which appears to be what you're using) the equates I recommended don't work. This is unfortunate, and I am sorry if I misled you in any way. Without them, the function above would look like this:

###############################
# (char*, object*) read_line(char*) 
# Read a line of text into the input buffer and parse it
###############################

read_line:
    addi $sp, $sp, -12  # set aside 12 bytes on the stack
    sw $fp, 0($sp)      # save the old frame pointer
    addi $fp, $sp, 0    # set the frame pointer to the stack, plus the number of s-regs
    sw $ra, 4($fp)      # save the current return address
    sw $a0, 8($fp)      # a0 == pointer to current position in input buffer

    li $v0, 8 
    syscall

    lw $a0, 8($fp)
    jal parse_object

    lw $a0, 8($fp)      # restore the values of the saved registers
    lw $ra, 4($fp)
    lw $fp, 0($sp)
    addi $sp, $sp, 12   # reset the stack
    jr $ra
Schol-R-LEA
Posting Pro
556 posts since Oct 2010
Reputation Points: 254
Solved Threads: 85
 

Oh I, forgot to mention, I'm using MARS

phillytennisguy
Newbie Poster
3 posts since Oct 2010
Reputation Points: 10
Solved Threads: 0
 

This article has been dead for over three months

Post: Markdown Syntax: Formatting Help
You