+1 (315) 557-6473 

Reading and playing notes encoded in LilyPond using MIPS assembly assignment help

The assignment deals with reading a file that contains a series of notes with pitch and duration and plays them using the syscall 33 in the Mars MIPS simulator. The program written by our MIPS assembly assignment help doers implements several subroutines to read notes, get the pitch, rhythm and length and play the song. The main program is provided and calls the implemented functions to play a given song.
Table Of Contents
  • Reading and Playing Notes in Syscall 33 

Reading and Playing Notes in Syscall 33 

#----------------------------------------------------------------------- # Lab6.asm # # Subroutines to interpret and play a song that is encoded in a string. # # # REGISTER USAGE: # $a0 - $a3: used to give the arguments for subroutines and syscall # $v0, $v1: used to save the return values for subroutines # $t0: used as a temporary register in the subroutines # $s0 - $s2: used to save subroutine data in subroutines that call other # subroutines, their initial value is preserved # In play_song: # $s0 = used to save initial string pointer and to save # read rhythm # $s1 = used to save the number of notes # $s2 = used to save the constant for converting beats to ms # In get_song_length: # $s0 = used to count the number of notes in the song # In read_note: # $s0 = used to save the read pitch #----------------------------------------------------------------------- .text .globl play_song # declare all subroutines as global so they can .globl get_song_length # be accessed from other asm files .globl play_note .globl read_note .globl get_pitch .globl get_rhythm #----------------------------------------------------------------------- # play_song # # input: $a0 - address of first character in string containing song # $a1 - tempo of song in beats per minute #----------------------------------------------------------------------- play_song: addi $sp, $sp, -16 # allocate space in stack for 4 words sw $ra, 0($sp) # save return address in stack sw $s0, 4($sp) # save s0 in stack sw $s1, 8($sp) # save s1 in stack sw $s2, 12($sp) # save s2 in stack move $s0, $a0 # save string pointer in $s0 li $t0,240000 # 4* 60s * 1000 ms in a second div $t0, $a1 # divide by tempo mflo $s2 # save result in $s2, this will be used to convert beats to ms jal get_song_length # get number of notes to play move $s1, $v0 # save note count in $s1 move $a0, $s0 # restore initial string pointer in $a0 li $a1, 1 # default rhythm of 4 beats loop: jal read_note # read a note srl $s0, $v0, 16 # get rhythm of last note and save it in s0 andi $a0, $v0, 0xFFFF # get pitch from bits 15:0 of returned note div $a1, $s2, $s0 # convert rhythm beats to milliseconds jal play_note # play the note move $a0, $v1 # set last position as start of string for next loop move $a1, $s0 # get rhythm of last note and set it as default rhythm for next one addi $s1, $s1, -1 # decrement number of notes to play bnez $s1, loop # repeat while there are more notes lw $ra, 0($sp) # load return address from stack lw $s0, 4($sp) # load s0 from stack lw $s1, 8($sp) # load s1 from stack lw $s2,12($sp) # load s2 from stack addi $sp, $sp, 16 # restore stack pointer jr $ra #----------------------------------------------------------------------- # get_song_length # # input: $a0 - address of first character in string containing song # # output: $v0 - number of notes in song #----------------------------------------------------------------------- get_song_length: addi $sp, $sp, -8 # allocate space in stack for 2 words sw $ra, 0($sp) # save return address in stack sw $s0, 4($sp) # save $s0 in stack li $s0, 1 # $s0 will count the number of notes, initialize to 1 count: jal read_note # read a note lb $t0, 0($v1) # load last character beqz $t0, endcnt # if we reached end of string, exit move $a0, $v1 # else, set last position as start of string for next loop addi $s0, $s0, 1 # increment number of notes j count endcnt: move $v0, $s0 # return number of notes lw $ra, 0($sp) # load return address from stack lw $s0, 4($sp) # load $s0 from stack addi $sp, $sp, 8 # restore stack pointer jr $ra #----------------------------------------------------------------------- # play_note # # input: $a0 - pitch # $a1 - note duration in milliseconds # # output: no output arguments, play a one-note using syscall system service 33 #----------------------------------------------------------------------- play_note: li $a2, 0 # use piano as the MIDI instrument li $a3, 127 # use maximum volume bnez $a0, skip # if the pitch is zero, is no sound li $a0, 0 # use 0 as volume skip: li $v0, 33 # call the system service 33, MIDI out syscall jr $ra #----------------------------------------------------------------------- # read_note # # input: $a0 - address of first character in string containing note encoding # $a1 - rhythm of previous note # # output: $v0 - note rhythm in bits [31:16], note pitch in bits [15:0] # note rhythm (1 = 4 beats, 2 = 2 beats, 4 = 1 beat, # 8 = 1/2 beat, 16 = 1/4 beat) # note pitch: (MIDI value, 0-127) # $v1 - address of first character of what would be next note #----------------------------------------------------------------------- read_note: addi $sp, $sp, -8 # allocate space in stack for 2 words sw $ra, 0($sp) # save return address in stack sw $s0, 4($sp) # save $s0 in stack spc1: # remove initial spaces lb $t0,0($a0) # load character from song beqz $t0,rnend # if it's end of string exit, no more notes bne $t0,' ', rpitch # it it's not space, read pitch addi $a0, $a0, 1 # advance to next char in song j spc1 # read another char rpitch: jal get_pitch # get pitch move $s0, $v0 # save pitch in $s0 move $a0, $v1 # use position after pitch as input to find rhythm jal get_rhythm sll $v0, $v0, 16 # shift rhythm to bits 31:16 add $v0, $v0, $s0 # add pitch in low part of $v0, bits 15:0 rnend: lw $ra, 0($sp) # load return address from stack lw $s0, 4($sp) # load $s0 from stack addi $sp, $sp, 8 # restore stack pointer jr $ra #----------------------------------------------------------------------- # get_pitch # # input: $a0 - address of first character in string containing note encoding # # output: $v0 - MIDI pitch value # $v1 - address of character after pitch is determined # e.g. if a portion of the song looks like c'' f4 # $v1 will point to ---------------------^ #----------------------------------------------------------------------- get_pitch: lb $t0, 0($a0) # load character from song addi $a0, $a0, 1 # advance to next character ifa: bne $t0, 'a', ifb # if it's a li $v0, 57 b getmod # get modifiers ifb: bne $t0, 'b', ifc # if it's b li $v0, 59 b getmod # get modifiers ifc: bne $t0, 'c', ifd # if it's c li $v0, 60 b getmod # get modifiers ifd: bne $t0, 'd', ife # if it's d li $v0, 62 b getmod # get modifiers ife: bne $t0, 'e', iff # if it's e li $v0, 64 b getmod # get modifiers iff: bne $t0, 'f', ifg # if it's f li $v0, 65 b getmod # get modifiers ifg: bne $t0, 'g', ifr # if it's g li $v0, 67 b getmod # get modifiers ifr: # if it's r li $v0, 0 # we will use pitch 0 as no sound getmod: lb $t0, 0($a0) # load character from song ifes: bne $t0, 'e', ifis # if it's e, we will assume 'es' add $v0, $v0, -1 # is a flat, decrease pitch add $a0, $a0, 2 # advance to next char skipping 'es' lb $t0, 0($a0) # load character from song j getoct # see if there are octave modifiers ifis: bne $t0, 'i', getoct # if it's i, we will assume 'is' add $v0, $v0, 1 # is a sharp, increase pitch add $a0, $a0, 2 # advance to next char skipping 'is' lb $t0, 0($a0) # load character from song getoct: bne $t0, 0x2c, ifapo # if it's , add $v0, $v0, -12 # decrease pitch by 12 add $a0, $a0, 1 # advance to next char in song lb $t0, 0($a0) # load character from song j getoct # repeat to check for more octave modifiers ifapo: bne $t0, 0x27, gpend # if it's ' add $v0, $v0, 12 # increase pitch by 12 add $a0, $a0, 1 # advance to next char in song lb $t0, 0($a0) # load character from song j getoct # repeat to check for more octave modifiers gpend: move $v1, $a0 # save last song position in $v1 jr $ra #----------------------------------------------------------------------- # get_rhythm # # input: $a0 - address of character in string containing song encoding # after pitch is determined # --> e.g. if song portion looks like: c'' f4 # $a0 will point to ------------------^ # --> e.g. if song portion looks like: aes,8 f16 # $a0 will point to -------------------^ # $a1 - previous note rhythm # # output: $v0 - note rhythm, default to previous note rhythm if no number # is present in note encoding # (1 = 4 beats, 2 = 2 beats, 4 = 1 beat, 8 = 1/2 beat, 16 = 1/4 beat) # $v1 - address of first character of next note #----------------------------------------------------------------------- get_rhythm: move $v0, $a1 # by default return previous note rhythm lb $t0, 0($a0) # load current character from song if1: bne $t0, '1', if2 # if it's 1 li $v0, 1 # set rhythm to 4 beats add $a0, $a0, 1 # advance to next char in song lb $t0, 0($a0) # load current character from song if16: bne $t0, '6', grend # if it's 16 li $v0, 16 # set rhythm to 1/4 beat j grnxt if2: bne $t0, '2', if4 # if it's 2 li $v0, 2 # set rhythm to 2 beats j grnxt if4: bne $t0, '4', if8 # if it's 4 li $v0, 4 # set rhythm to 1 beat j grnxt if8: bne $t0, '8', grend # if it's 8 li $v0, 8 # set rhythm to 1/2 beat grnxt: add $a0, $a0, 1 # advance to next char in song grend: move $v1, $a0 # return current position in song jr $ra