
        GET     h.RegNames


        AREA    |C$$Code|, CODE, READONLY


        EXPORT  linear_handler
        EXPORT  midi_generate_samples
        EXPORT  stop_note
	EXPORT	call_voice_generator
	EXPORT  call_event_code


BYTESPERNOTE	  	* 84
NT_SAMPLEDATA	  	* 0
NT_SAMPLELENGTH	  	* 4
NT_RESAMPLERATE	  	* 8
NT_OFFSET	  	* 12
NT_VOLUME	  	* 16
NT_MAX_FRAMES	  	* 20
NT_VOLUMESTEP	  	* 24
NT_NOTE		  	* 28
NT_CHANNEL	  	* 29
NT_STATE	  	* 30
NT_FLAGS	  	* 31
NT_FRAMEPOS	  	* 32
NT_NOTEINDEX	  	* 33
NT_SOURCE_FORMAT	* 34
NT_HOLD_MAX_FRAMES 	* 36
NT_DECAY_MAX_FRAMES 	* 40
NT_DECAY_VOLUMESTEP 	* 44
NT_SUSTAIN_MAX_FRAMES 	* 48
NT_SUSTAIN_VOLUMESTEP 	* 52
NT_RELEASE_MAX_FRAMES 	* 56
NT_RELEASE_VOLUMESTEP 	* 60
NT_BASE_RESAMPLERATE    * 64
NT_CHANNEL_PTR		* 68
NT_LOOPPOINT		* 72
NT_SAMPLEINFO		* 76
NT_PRIVATE_WORD		* 80

NT_FLAGS_SILENCE  	* 1
NT_FLAGS_FORCE_RELEASE  * 2
NT_FLAGS_ABORT		* 4

NT_SOURCE_16BIT		* 0
NT_SOURCE_8BIT	 	* 1
NT_SOURCE_VOICE_GEN	* 2

NT_STATE_FINISHED 	* 0
NT_STATE_RELEASE  	* 1
NT_STATE_SUSTAIN  	* 2
NT_STATE_DECAY   	* 3
NT_STATE_HOLD   	* 4
NT_STATE_ATTACK   	* 5

CHN_LEFT_VOLUME		* 0
CHN_RIGHT_VOLUME	* 4

SI_NOTEUSAGE		* 34

MI_POLYPHONY	  	* 0
MI_ACTIVENOTES	  	* 4
MI_NOTES	  	* 8
MI_SINEWAVE    	  	* 12
MI_VOLUMETABLE	  	* 16
MI_MASTERVOLUME	  	* 20
MI_BIGBUFFER	  	* 24
MI_FLAGS	  	* 28
MI_DEFAULTS	  	* 32
MI_MTC0			* 36
MI_MTC1			* 40

EXTSND_FLAGS		* 0
EXTSND_CODE		* 4
EXTSND_R12		* 8
EXTSND_FREQ		* 12

FLAG_MONO	  	* 2_0000100
FLAG_CLEARBUFFER  	* 2_0001000
FLAG_REDUCE16  		* 2_0010000
FLAG_INTERPOLATE  	* 2_0100000

RESAMPLE_BITS	  	* 12


; ---------------------------------------------------------------
linear_handler
; on entry      r0 => midiport structure
;               r1 => buffer to fill
;               r2 => end of buffer
;               r3 =  buffer flags
	STMFD	r13!,{r10-r12,r14}

	MOV     r10, r0
	MOV     r7, r1
	MOV     r8, r2
	AND     r9, r3, #2_111

	SUB     r11, r8, r7
	MOV     r11, r11, LSR#2		; samples to output

	CMP	r9, #0			; is buffer contents valid?
	BNE	%F10			; yes, so don't clear the buffer

	MOV	r0, #0
	MOV	r1, #0
	MOV	r2, #0
	MOV	r3, #0
	MOV	r4, r11			; words to clear
	MOV	r5, r7
02
	  CMP	r4, #8
	  BLT	%F03
	  STMIA	r5!, {r0-r3}		; write 8 words
	  STMIA	r5!, {r0-r3}
	SUBS	r4, r4, #8
	BGT	%B02
03
	  CMP	r4,#0
	  STRGT	r0,[r5],#4
	SUBS	r4, r4, #1
	BGT	%B03


10
	MOV	r8, #0			; flags for midi_generate_samples
 	LDR	r3,[r10, #MI_DEFAULTS]
	TST	r3, #FLAG_INTERPOLATE
	ORRNE	r8, r8, #FLAG_INTERPOLATE

	ORR	r8, r8, #FLAG_CLEARBUFFER

	LDR	r0, [r10, #MI_BIGBUFFER] ; get temp buffer
	MOV	r6, #0
	STR	r6, [r0,#0]		; clear first word
	ADD	r0, r0, #4		; skip first word
	MOV	r6, #&ff00		; useful later on
	ORR	r6, r6, #&00ff
20
	  MOV   r1, r11
	  CMP	r1, #256
	  MOVGT r1, #256		; max 256 samples at a time

	  MOV	r2, r8
	  MOV	r3, r10
	  BL	midi_generate_samples

	  MOV	r2, r1			; samples to mix
	  MOV   r3, r0			; input
30
	    LDMIA r3!, {r4, r5}		; get stereo sample pair
	    LDR	  r9,[r7]		; get contents of SoundDMA buffer
	    MOV   r12, r9, LSL#16
	    ADD   r4, r4, r12, ASR#16	; mix left
	    CMP	  r4, r6, LSR#1		; clamp to legal range
	    MOVGT r4, r6, LSR#1
	    CMN   r4, r6, LSR#1
	    MVNLT r4, r6, LSR#1
	    ADD   r5, r5, r9, ASR#16	; mix right
	    CMP	  r5, r6, LSR#1
	    MOVGT r5, r6, LSR#1
	    CMN   r5, r6, LSR#1
	    MVNLT r5, r6, LSR#1
	    AND   r4, r4, r6
	    ORR   r4, r4, r5, LSL#16	; join l+r
	    STR   r4, [r7],#4		; write
	  SUBS	r2, r2, #1
	  BGT	%B30

	  SUBS	r11, r11, r1
	BGT	%B20
	LDMFD	r13!,{r10-r12,pc}^


; ---------------------------------------------------------------

midi_generate_samples ROUT
;on entry	r0 -> buffer (word-aligned, stereo, 32 bits/sample)
;		r1 =  no. of samples to read
;		r2 =  flags
;		      bit 2 set   reduce to mono
;		      bit 3 set   clear buffer first
;		      bit 4 set   reduce to 16 bits
;		      bit 5 set   interpolate
;		r3 -> midiport structure
	CMP     r1, #0
	MOVLES  pc, r14			; no samples

	STMFD	r13!,{r0-r12,r14}

	MOV     r10, r0
	MOV     r11, r1
	MOV     r12, r2
	MOV	r9, r3
	STR	r12, [r9, #MI_FLAGS]    ; save for later
	MOV     r8, r12

; clear buffer to 0 if necessary
	TST     r12, #FLAG_CLEARBUFFER	; clear buffer?
	MOVNE   r0, r11, LSL#1		; words to write
	MOVNE	r1, r10
	BLNE	clear_buffer
10

; for each note, mix the note with the previous contents of the buffer
	LDR     r6,[r9,#MI_POLYPHONY]
	ADD	r6, r6, #7
	MOV	r6, r6, LSR#3		; polyphony in multiples of 8
	LDR     r7,[r9,#MI_ACTIVENOTES]
20
  	  SUBS  r6,r6,#1		;
	  BLT   %F30
	  LDRB  r5,[r7,r6]     		; get active-flags for 8 notes
	  CMP   r5,#0
	  BEQ   %B20

	  MOV   r4, r6, LSL#3		; index for first note

	  TST   r5,#2_00000001
          BLNE  fill_note
	  ADD   r4, r4, #1
	  TST   r5,#2_00000010
          BLNE  fill_note
	  ADD   r4, r4, #1
	  TST   r5,#2_00000100
          BLNE  fill_note
	  ADD   r4, r4, #1
	  TST   r5,#2_00001000
          BLNE  fill_note
	  ADD   r4, r4, #1
	  TST   r5,#2_00010000
          BLNE  fill_note
	  ADD   r4, r4, #1
	  TST   r5,#2_00100000
          BLNE  fill_note
	  ADD   r4, r4, #1
	  TST   r5,#2_01000000
          BLNE  fill_note
	  ADD   r4, r4, #1
	  TST   r5,#2_10000000
          BLNE  fill_note
	B 	%B20
30
	TST	r8, #FLAG_MONO
	BNE	reduce_to_mono

	TST	r8, #FLAG_REDUCE16
	LDMEQFD	r13!,{r0-r12,pc}^	; don't reduce, so we're done

reduce_to_16bit
	MOV 	r0, r10			; output
	MOV	r1, #&ff00
	ORR	r1, r1, #&00ff
40
	  LDMIA r10!,{r2,r3}		; get stereo sample
	  CMP	r2, r1,LSR#1		; clamp to legal value
	  MOVGT r2, r1,LSR#1
	  CMN   r2, r1,LSR#1
	  MVNLT r2, r1,LSR#1
	  CMP	r3, r1,LSR#1
	  MOVGT r3, r1,LSR#1
	  CMN   r3, r1,LSR#1
	  MVNLT r3, r1,LSR#1
          AND   r2, r2, r1
	  ORR   r2, r2, r3, LSL#16
	  STR   r2, [r0],#4       	; write
	SUBS	r11, r11, #1
	BGT	%B40
	LDMFD	r13!,{r0-r12,pc}^

reduce_to_mono
	MOV 	r0, r10			; output
	MOV	r1, #&7f00		; max value
	ORR	r1, r1, #&00ff
50
	  LDMIA r10!,{r2,r3}		; get stereo sample
	  ADD   r2, r2, r3
	  MOV   r2, r2, ASR#1		; mix down to mono
	  TST   r8, #FLAG_REDUCE16
	  STREQ r2, [r0], #4            ; don't reduce, just write...
	  BEQ   %F51			; and that's it

	  CMP   r2, r1			; clamp to legal value
	  MOVGT r2, r1
	  CMN   r2, r1
	  MVNLT r2, r1
	  STRB  r2, [r0],#1		; write 16 bit sample
	  MOV   r2, r2, LSR#8
	  STRB  r2, [r0],#1
51
	SUBS	r11, r11, #1
	BGT	%B50
	LDMFD	r13!,{r0-r12,pc}^



copy_data
;on entry	r0  =  no. of words
;		r1  -> dest buffer
;		r2  -> src buffer
	STMFD	r13!,{r0-r12,r14}
	LDMFD	r13!,{r0-r12,pc}^



clear_buffer	ROUT
;on entry	r0  =  no. of words to clear
;		r1  -> buffer
	STMFD	r13!,{r0-r12,r14}
	MOV	r10, r1
	MOV     r1, #0
	MOV     r2, #0
	MOV     r3, #0
	MOV     r4, #0
	MOV     r5, #0
	MOV     r6, #0
	MOV     r7, #0
	MOV     r8, #0
01
	  CMP   r0, #16			; clear 16 words at a time
          BLT   %F02
	  STMIA r10!,{r1-r8}
	  STMIA r10!,{r1-r8}
	  SUB   r0, r0, #16
	  B     %B01
02
  	  CMP   r0, #2			; clear 2 words at a time
	  LDMLTFD r13!,{r0-r12,pc}^
	  STMIA r10!,{r1-r2}
	  SUB   r0, r0, #2
	  B     %B02
03


; ---------------------------------------------------------------

fill_note	ROUT
;on entry       r4  =  note index
;		r9  -> midiport
;		r10 -> output buffer, stereo, 32 bit samples
;		r11 =  no. samples to generate
	STMFD	r13!,{r0-r12,r14}

	LDR     r8, [r9, #MI_NOTES]	; get pointer to notes
	MOV	r0, #BYTESPERNOTE
	MLA	r8, r4, r0, r8

	LDRB    r0, [r8, #NT_FLAGS]
	TST     r0, #NT_FLAGS_SILENCE
	LDMNEFD	r13!,{r0-r12,pc}^

	LDRB    r0, [r8,#NT_STATE]
	CMP	r0, #NT_STATE_FINISHED
	LDMEQFD	r13!,{r0-r12,pc}^

	LDR     r0, [r8, #NT_SAMPLEDATA]   ; sample data
	LDR     r1, [r8, #NT_RESAMPLERATE] ; resample rate
	LDR     r2, [r8, #NT_OFFSET]       ; sample position
	LDR     r3, [r8, #NT_SAMPLELENGTH] ; sample length

        BL	calculate_lr_volume

	LDRB    r12, [r8, #NT_FRAMEPOS] ; framepos in bits 0..7
	LDR     r5, [r8, #NT_MAX_FRAMES]
	ORR     r4, r12, r5, LSL#8     	; framecount in bits 8..31
	CMP	r12, #64
	BLT	do_one_frame

new_frame
	; recalculate volume every frame
	  BL	update_note_volume
	  BL	calculate_lr_volume

	  LDRB  r5, [r8, #NT_FLAGS]
	  TST   r5, #NT_FLAGS_ABORT
	  BNE	abort_note
	  TST   r5, #NT_FLAGS_FORCE_RELEASE
	  BNE	force_release

 	  SUB   r4, r4, #256		; one less frame left
	  BICS  r4, r4, #255		; clear frame pos bits
	  BNE   do_one_frame		; not end of frames, so continue

state_done
	;end of frames for this state
	  LDRB	r5, [r8, #NT_STATE] 	; end of frames, so switch state
	  SUBS	r5, r5, #1
	  STRB 	r5, [r8, #NT_STATE]	; new state
	  BEQ   abort_note		; 0 -> finished

	  CMP   r5, #NT_STATE_HOLD
	  MOVEQ	r12, #0
	  LDREQ	r4, [r8, #NT_HOLD_MAX_FRAMES]
	  CMP   r5, #NT_STATE_DECAY
	  LDREQ	r12, [r8, #NT_DECAY_VOLUMESTEP]
	  LDREQ	r4, [r8, #NT_DECAY_MAX_FRAMES]
	  CMP   r5, #NT_STATE_SUSTAIN
	  LDREQ	r12, [r8, #NT_SUSTAIN_VOLUMESTEP]
	  LDREQ	r4, [r8, #NT_SUSTAIN_MAX_FRAMES]
	  CMP   r5, #NT_STATE_RELEASE
	  LDREQ	r12, [r8, #NT_RELEASE_VOLUMESTEP]
	  LDREQ	r4, [r8, #NT_RELEASE_MAX_FRAMES]
	  CMP	r4, #0
	  BEQ   state_done
	  STR   r12, [r8, #NT_VOLUMESTEP]
	  STR   r4, [r8, #NT_MAX_FRAMES]
	  MOV   r4, r4, LSL#8		; framecount in bits 8..31
	  B	do_one_frame

force_release
	  BIC   r5, r5, #NT_FLAGS_FORCE_RELEASE
	  STRB  r5, [r8, #NT_FLAGS]	; clear 'force release' flag

	  LDRB	r5, [r8, #NT_STATE]
	  CMP	r5, #NT_STATE_RELEASE
	  BEQ	abort_note              ; already in release state...

	  MOV 	r5, #NT_STATE_RELEASE	; force release state
	  STRB 	r5, [r8, #NT_STATE]	; new state

	  LDR 	r5, [r8, #NT_RELEASE_VOLUMESTEP]
	  STR 	r5, [r8, #NT_VOLUMESTEP]
	  LDR   r5, [r8, #NT_RELEASE_MAX_FRAMES]
	  STR   r5, [r8, #NT_MAX_FRAMES]
	  MOV   r4, r5, LSL#8		; framecount in bits 8..31

do_one_frame
	  MOV	r12, r11
	  CMP	r12, #64
	  MOVGT	r12, #64		; do max 64 samples
	  AND	r14, r4, #255
	  RSB	r14, r14, #64		; samples left in frame
	  CMP	r12, r14
	  MOVGT	r12, r14

	  BL	fill_frame
	  ADD	r4, r4, r12  		; update
	  SUBS	r11, r11, r12
	  BLE	buffer_filled
	  B	new_frame

buffer_filled
	STRB  	r4, [r8, #NT_FRAMEPOS]
	MOV   	r4, r4, LSR#8
	STR   	r4, [r8, #NT_MAX_FRAMES]
	STR   	r2, [r8, #NT_OFFSET]	; save sample-position for next time
   	LDMFD 	r13!,{r0-r12,pc}^

abort_note
	MOV	r0, r8
	MOV	r1, r9
	BL	stop_note
   	LDMFD 	r13!,{r0-r12,pc}^


fill_frame
;on entry	r0  -> sampledata
;		r1  =  step
;		r2  =  position
;		r3  =  sample length
;		r6,r7  volume
;		r8  -> note
;		r9 ->  midiport
;		r10 -> output buffer
;		r12 =  samples to generate
;scrap regs 	r5
;on exit	r2,r10 updated
;		all other registers must be preserved
	LDRB	r5, [r8, #NT_SOURCE_FORMAT]
	CMP	r5, #3
	MOVGES  pc, r14
	STMFD	r13!,{r4,r9,r11-r12,r14}
	ADD	pc, pc, r5, LSL #2
	MOV	r0, r0			; NOP
	B	fill_frame_16bit
	B	fill_frame_8bit
	B	fill_frame_voice_generator
        LDMFD	r13!,{r4,r9,r11-r12,pc}^


fill_frame_8bit
	LDR	r5, [r9, #MI_FLAGS]
	TST	r5, #FLAG_INTERPOLATE
	BNE	fill_frame_8bit_interpolate
fill_frame_loop_8bit
	LDRB	r5, [r0, r2, LSR#RESAMPLE_BITS] ; get note-sample
	MOV	r5, r5, LSL#24
	MOV	r5, r5, ASR#16		; convert to 16 bit signed
	MUL	r9, r6, r5		; left
	MUL	r5, r7, r5		; right

	LDMIA	r10, {r11, r14}
	ADD	r11, r11, r9, ASR#8     ; mix right
	ADD	r14, r14, r5, ASR#8	; mix left
 	STMIA	r10!, {r11, r14}

	ADD	r2, r2, r1		; increment read position
	CMP	r2, r3, LSL#RESAMPLE_BITS
	BLGE	rewind_sample

	SUBS	r12, r12, #1
	BGT	fill_frame_loop_8bit
        LDMFD	r13!,{r4,r9,r11-r12,pc}^
fill_frame_8bit_interpolate
	MOV	r4, #1<<RESAMPLE_BITS
	SUB	r4, r4, #1		; mask
fill_frame_loop_8bit_interpolate
	MOV	r11, r2, LSR#RESAMPLE_BITS
	LDRB	r5, [r0, r11]		; get note-sample#1
	MOV	r5, r5, LSL#24
	MOV	r5, r5, ASR#16		; convert to 16 bit signed

	ADD	r11, r11, #1
	CMP	r11, r3
	LDRGE	r11, [r8, #NT_LOOPPOINT]
	LDRB	r11, [r0, r11]		; get note-sample#2
	MOV	r11, r11, LSL#24
	MOV	r11, r11, ASR#16	; convert to 16 bit signed

	AND	r14, r2, r4
	MUL	r11, r14, r11
	RSB	r14, r14, #1<<RESAMPLE_BITS
	MLA	r5, r14, r5, r11
	MOV	r5, r5, ASR#RESAMPLE_BITS

	MUL	r9, r6, r5		; left
	MUL	r5, r7, r5		; right

	LDMIA	r10, {r11, r14}
	ADD	r11, r11, r9, ASR#8     ; mix right
	ADD	r14, r14, r5, ASR#8	; mix left
 	STMIA	r10!, {r11, r14}

	ADD	r2, r2, r1		; increment read position
	CMP	r2, r3, LSL#RESAMPLE_BITS
	BLGE	rewind_sample

	SUBS	r12, r12, #1
	BGT	fill_frame_loop_8bit_interpolate
        LDMFD	r13!,{r4,r9,r11-r12,pc}^


fill_frame_voice_generator
	STMFD	r13!,{r0-r1,r3,r6-r8}
	MOV	r5, r1			; resample rate
	LDR	r1, [r8, #NT_SAMPLEDATA] ; pointer to voicegenerator
	MOV	r4, r12			; no. of samples
	LDR	r12, [r1, #EXTSND_R12]
	LDR	r11, [r1, #EXTSND_CODE]
	ADD	r8, r8, #NT_PRIVATE_WORD ; private word
	MOV	r3, r10			; buffer
	MOV	r0, #3                  ; reason code
	MOV	r14, pc
	MOV	pc, r11
fill_frame_voice_generator_return
	ADD	r10, r10, r4, LSL#3
	LDMFD	r13!,{r0-r1,r3,r6-r8}
        LDMFD	r13!,{r4,r9,r11-r12,pc}^


fill_frame_16bit
	LDR	r5, [r9, #MI_FLAGS]
	TST	r5, #FLAG_INTERPOLATE
	BNE	fill_frame_16bit_interpolate
fill_frame_loop_16bit
        MOV	r5, r2, LSR#RESAMPLE_BITS ; get integer part of sampleindex
	LDR	r5, [r0, r5, LSL#1]	; get note-sample
	MOV	r5, r5, LSL#16
	MOV	r5, r5, ASR#16		; convert to 16 bit signed
	MUL	r9, r6, r5		; left
	MUL	r5, r7, r5		; right

	LDMIA	r10, {r11, r14}
	ADD	r11, r11, r9, ASR#8     ; mix right
	ADD	r14, r14, r5, ASR#8	; mix left
 	STMIA	r10!, {r11, r14}

	ADD	r2, r2, r1		; increment read position
	CMP	r2, r3, LSL#RESAMPLE_BITS
	BLGE	rewind_sample

	SUBS	r12, r12, #1
	BGT	fill_frame_loop_16bit
        LDMFD	r13!,{r4,r9,r11-r12,pc}^
fill_frame_16bit_interpolate
	MOV	r4, #1<<RESAMPLE_BITS
	SUB	r4, r4, #1		; mask
fill_frame_loop_16bit_interpolate
        MOV	r11, r2, LSR#RESAMPLE_BITS ; get integer part of sampleindex
	LDR	r5, [r0, r11, LSL#1]	; get note-sample#1
	MOV	r5, r5, LSL#16
	MOV	r5, r5, ASR#16		; convert to 16 bit signed

	ADD	r11, r11, #1
	CMP	r11, r3
	LDRGE	r11, [r8, #NT_LOOPPOINT]
	LDR	r11, [r0, r11, LSL#1]	; get note-sample#2
	MOV	r11, r11, LSL#16
	MOV	r11, r11, ASR#16	; convert to 16 bit signed

	AND	r14, r2, r4
	MUL	r11, r14, r11
	RSB	r14, r14, #1<<RESAMPLE_BITS
	MLA	r5, r14, r5, r11
	MOV	r5, r5, ASR#RESAMPLE_BITS

	MUL	r9, r6, r5		; left
	MUL	r5, r7, r5		; right

	LDMIA	r10, {r11, r14}
	ADD	r11, r11, r9, ASR#8     ; mix right
	ADD	r14, r14, r5, ASR#8	; mix left
 	STMIA	r10!, {r11, r14}

	ADD	r2, r2, r1		; increment read position
	CMP	r2, r3, LSL#RESAMPLE_BITS
	BLGE	rewind_sample

	SUBS	r12, r12, #1
	BGT	fill_frame_loop_16bit_interpolate
        LDMFD	r13!,{r4,r9,r11-r12,pc}^



rewind_sample	ROUT
;on entry	r8  -> note
;		r9  -> midiport
;		r12 =  no. of samples left
;scrap regs 	r5
;on exit	r2  =  sample-position
	LDR	r2, [r8, #NT_LOOPPOINT]
 	MOV	r2, r2, LSL#RESAMPLE_BITS
	MOVS	pc, r14


update_note_volume
;on entry	r8 -> note
;		r9 -> midiport
;scrap regs 	r5,r6,r12
 	LDR	r6, [r8, #NT_VOLUME]	; log volume (0..1<<24
	LDR	r5, [r8, #NT_VOLUMESTEP]
	ADDS	r6, r6, r5		; new volume
	MOVLT	r6, #0    		; clamp to 0..(1<<24)
	CMP	r6, #1<<24
	MOVGE	r6, #1<<24
	SUBGE	r6, r6, #1
	STR	r6, [r8, #NT_VOLUME]  	; save for next time
	MOVS	pc, r14


calculate_lr_volume
;on entry	r8 -> note
;		r9 -> midiport
;scrap regs 	r5,r12
;on exit	r6 =  left volume
;		r7 =  right volume
 	LDR	r6, [r8, #NT_VOLUME]	; log volume (0..1<<24
	MOV	r6, r6, LSR#14		; log volume (0..1023)
	LDR 	r5, [r9, #MI_VOLUMETABLE] ; log2lin volume table
	LDR	r6, [r5, r6, LSL#1]	; lin volume (0..65535)
	MOV	r6, r6, LSL#16
	MOV	r6, r6, LSR#16
	LDR	r5, [r9, #MI_MASTERVOLUME]
	MUL	r6, r5, r6             	; 28 bits
	MOV	r6, r6, LSR#12		; 16 bits
	LDR	r5, [r8, #NT_CHANNEL_PTR]
 	LDR	r7, [r5, #CHN_RIGHT_VOLUME] ; 14 bits
	MUL	r7, r6, r7  		; 30 bits
	MOV	r7, r7, LSR#22		; 8 bits
	LDR	r5, [r5, #CHN_LEFT_VOLUME] ; 14 bits
	MUL	r6, r5, r6  		; 30 bits
	MOV	r6, r6, LSR#22		; 8 bits
	MOVS	pc, r14


stop_note
;on entry	r0 -> note structure
;		r1 -> midiport
	STMFD	r13!,{r0-r12,r14}
	MOV	r8, r0
	MOV	r9, r1

	LDRB	r0, [r8, #NT_SOURCE_FORMAT]
	CMP	r0, #NT_SOURCE_VOICE_GEN
        ; inform voice-generator that the note has stopped
	MOVEQ	r0, #2
	LDREQ	r1, [r8, #NT_SAMPLEDATA]
	ADDEQ	r3, r8, #NT_PRIVATE_WORD
	BLEQ	call_voice_generator

        ; mark note as inactive
	LDRB 	r0, [r8, #NT_NOTEINDEX] ;
	LDR  	r1, [r9, #MI_ACTIVENOTES]
	LDRB 	r2, [r1, r0, LSR#3]
	AND     r3, r0, #7
	MOV     r4, #1
	BIC     r2, r2, r4, LSL r3
	STRB 	r2, [r1, r0, LSR#3]

        ; decrement sample usage
	LDR	r0, [r8, #NT_SAMPLEINFO]
	LDR	r1, [r0, #SI_NOTEUSAGE]
	MOV	r1, r1, LSL#16
	MOV	r1, r1, LSR#16
	SUB	r1, r1, #1
	STRB	r1, [r0, #SI_NOTEUSAGE+0]
	MOV	r1, r1, LSR#8
	STRB	r1, [r0, #SI_NOTEUSAGE+1]

	LDMFD	r13!,{r0-r12,pc}^



call_voice_generator
;on entry	r0  =  reason code
;		r1  -> control block
;		r2  =  value to pass in r2
;		r3  =  value to pass in r8
	STMFD	r13!,{r0-r12,r14}
	LDR	r12, [r1, #EXTSND_R12]
	LDR	r11, [r1, #EXTSND_CODE]
	MOV	r8, r3
	MOV	r14, pc
	MOV	pc, r11
call_voice_generator_return
	LDMFD	r13!,{r0-r12,pc}^



call_event_code
;on entry	r0  =  event
;		r1  =  arg1
;		r2  =  arg2
;		r3  =  arg3
;	      sp+0  =  address to call
;	      sp+4  =  r12 value
	STMFD	r13!,{r1-r12,r14}
	LDR	r11, [r13, #13*4+0]
	LDR	r12, [r13, #13*4+4]
	MOV	r14, pc
	MOV	pc, r11
call_event_code_return
	LDMFD	r13!,{r1-r12,pc}^


        END
