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

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

Edited 6 Years Ago by Schol-R-LEA: n/a

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

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.)

Edited 6 Years Ago by Schol-R-LEA: n/a

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

Edited 6 Years Ago by Schol-R-LEA: n/a

This article has been dead for over six months. Start a new discussion instead.