; Voice generators used as entry points
; only used when Sound_InstallVoice is called under the JIT
; NOTE: R14 cannot be used as code is called in IRQ mode

;Needs to call the Install entry point at offset 20
;when the voice is first installed


STRUCT sound_voice_handler
{
 .fill			DCD 0
 .update		DCD 0
 .gateon		DCD 0
 .gateoff		DCD 0
 .instantiate		DCD 0
 .free			DCD 0
 .install
 .voicegenerator	DCD 0
 .voicename		DCD 0
 .name 			DBB 32
 .Size
}

#set sound_voice_handler_shift = 6 ; 1<<6 = 64 (ie size of sound_voice_handler)




.hv_voice_generators
 gen_Voice_Generator 0
 gen_Voice_Generator 1
 gen_Voice_Generator 2
 gen_Voice_Generator 3
 gen_Voice_Generator 4
 gen_Voice_Generator 5
 gen_Voice_Generator 6
 gen_Voice_Generator 7
 gen_Voice_Generator 8
 gen_Voice_Generator 9
 gen_Voice_Generator 10
 gen_Voice_Generator 11
 gen_Voice_Generator 12
 gen_Voice_Generator 13
 gen_Voice_Generator 14
 gen_Voice_Generator 15
 gen_Voice_Generator 16
 gen_Voice_Generator 17
 gen_Voice_Generator 18
 gen_Voice_Generator 19
 gen_Voice_Generator 20
 gen_Voice_Generator 21
 gen_Voice_Generator 22
 gen_Voice_Generator 23
 gen_Voice_Generator 24
 gen_Voice_Generator 25
 gen_Voice_Generator 26
 gen_Voice_Generator 27
 gen_Voice_Generator 28
 gen_Voice_Generator 29
 gen_Voice_Generator 30
 gen_Voice_Generator 31


.hv_VG_entries
 gen_Voice_Generator_Entry 0
 ._size
 #set hv_VG_size = _size - hv_VG_entries
 gen_Voice_Generator_Entry 1
 gen_Voice_Generator_Entry 2
 gen_Voice_Generator_Entry 3
 gen_Voice_Generator_Entry 4
 gen_Voice_Generator_Entry 5
 gen_Voice_Generator_Entry 6
 gen_Voice_Generator_Entry 7
 gen_Voice_Generator_Entry 8
 gen_Voice_Generator_Entry 9
 gen_Voice_Generator_Entry 10
 gen_Voice_Generator_Entry 11
 gen_Voice_Generator_Entry 12
 gen_Voice_Generator_Entry 13
 gen_Voice_Generator_Entry 14
 gen_Voice_Generator_Entry 15
 gen_Voice_Generator_Entry 16
 gen_Voice_Generator_Entry 17
 gen_Voice_Generator_Entry 18
 gen_Voice_Generator_Entry 19
 gen_Voice_Generator_Entry 20
 gen_Voice_Generator_Entry 21
 gen_Voice_Generator_Entry 22
 gen_Voice_Generator_Entry 23
 gen_Voice_Generator_Entry 24
 gen_Voice_Generator_Entry 25
 gen_Voice_Generator_Entry 26
 gen_Voice_Generator_Entry 27
 gen_Voice_Generator_Entry 28
 gen_Voice_Generator_Entry 29
 gen_Voice_Generator_Entry 30
 gen_Voice_Generator_Entry 31


;hv_call_voice_handler
;---------------------
;calls a voice generator Fill, Update, GateOn or GateOff entry point
;
;Entry: in IRQ mode IRQ enabled
;stack - R14
; R5 - bits 4..0 - Voice Generator #
;      bits 7..5 - Entry point
;
;Passed to the Voice Generator:
; R0     - Voice Generator address
; R1     - return address
; R2     - Voice Generator entry point
; R3-R4  - Preserved from SoundChannels
; R5     - Corrupted
; R6-R12 - Preserved from SoundChannels

.hv_call_voice_handler
 LDR     R0, terminate_all_voices
 TEQ     R0, #0				;are we terminating?
 MOVNE   R0, #1<<1			;YES, F2 set, flush and go dormant
 LDRNE   PC, [R13], #4

 #if JIT_WIMP_modules == 1
;@@@ NEEDS to page in the voice handler
   STMFD   R13!, {R0-R1}
   MRS     R1, CPSR
   ORR     R0, R1, #%10 << 6		;disable IRQ
   MSR     CPSR_c, R0
   BL      check_JIT_and_taskID		;is the task supported?
   BEQ     _retain
   MSR     CPSR_c, R1			;restore CPSR
   MOV     R0, #1<<3			;NO, A set, voice is active
   LDMFD   R13!, {R0-R1, PC}		; |+ and exit

   ._retain
   MSR     CPSR_c, R1			;restore CPSR
   LDMFD   R13!, {R0-R1}
 #endif

 AND     R2, R5, #%11111		;R2 = Voice Generator #
 MOV     R0, R5, LSR #5			;R2=entry point
 CMP     R0, #2				;GateOn or higher entry?
 BLO     _check_GateOn			;NO, Fill or Update

 ._toggle_GateOn
 LDR     R0, GateOn_required
 MOV     R1, #1
 BICHI   R0, R0, R1, LSL R2		;flag GateOn entry as being required
 ORREQ   R0, R0, R1, LSL R2		;flag GateOn entry as not required
 STR     R0, GateOn_required

 ._call_entry
 ADR     R0, hv_voice_generators + sound_voice_handler.voicegenerator
 MOV     R5, R5, LSR #5			;R5 = entry point
 LDR     R0, [R0, R2, LSL #sound_voice_handler_shift] ;R0 = address of Voice Generator
 LDR     R1, jit_managed_ldr_pc_r13	;return inside JIT space
 MRS     R2, CPSR
 AND     R2, R2, #%11			;add in CPU mode
 ORR     R1, R1, R2
 MRS     R2, CPSR
 AND     R2, R2, #%11 << 6
 ORR     R1, R1, R2, LSL #20		;add in IRQ/FIQ status
 ADD     R2, R0, R5, LSL #2		;R2 = Voice Generator entry
 STR     R1, [R13, #-4]!		;stack return
ADD     PC, R2, #jit_code_start		;call Voice Generator


 ._check_GateOn
 LDR     R0, GateOn_required
 MOV     R1, #1
 TST     R0, R1, LSL R2			;GateOn call required?
 BNE     _call_entry			;NO
					;YES, call GateOn, then actual entry
 ADR     R0, _GateOn_return
 STMFD   R13!, {R0, R3-R12}		;stack return to us and all regs
 BIC     R5, R5, #%111 << 5
 ORR     R5, R5, #2 << 5		;call GateOn
 B       _toggle_GateOn			;with EQ set

 ._GateOn_return			;will return here
 LDMFD   R13!, {R3-R12}			;pull regs from stack
 AND     R2, R5, #%11111		;R2 = Voice Generator #
 B       _call_entry			;and call actual entry point




;hv_call_voice_handler_setup
;---------------------------
;calls a voice generator Instantiate or Free entry point
;
;Entry:
; R5 - bits 4..0 - Voice Generator #
;      bits 7..5 - Entry point
;
;Passed to the Voice Generator (as per RISCOS 3.71 source code):
; R5            - Voice Generator address
; R0-R4, R6-R12 - Preserved from SoundChannels

.hv_call_voice_handler_setup
 LDR     R14, terminate_all_voices
 TEQ     R14, #0			;are we terminating?
 LDRNE   PC, [R13], #4			;YES

 #if JIT_WIMP_modules == 1
;@@@ NEEDS to page in the voice handler
   STMFD   R13!, {R0-R1}
   MRS     R1, CPSR
   ORR     R0, R1, #%10 << 6		;disable IRQ
   MSR     CPSR_c, R0
   BL      check_JIT_and_taskID		;is the task supported?
   BEQ     _retain
   MSR     CPSR_c, R1			;restore CPSR
   LDMFD   R13!, {R0-R1, PC}		; |+ and exit

   ._retain
   MSR     CPSR_c, R1			;restore CPSR
   LDMFD   R13!, {R0-R1}
 #endif

 STMFD   R13!, {R9}			;preserve R9
 ADR     R14, _return
 STMFD   R13!, {R14}
 LDR     R14, jit_managed_ldr_pc_r13	;return inside JIT space
 STMFD   R13!, {R0-R1, R2, R14}		;stack return and preserve R0-R1
 ADR     R14, hv_voice_generators + sound_voice_handler.voicegenerator
 AND     R0, R5, #%11111		;R0 = Voice Generator #
 MOV     R1, R5, LSR #5			;R1 = entry point
 LDR     R5, [R14, R0, LSL #sound_voice_handler_shift] ;R5 = address of Voice Generator
 ADD     R1, R5, R1, LSL #2		;R1 = Voice Generator entry
 ADD     R1, R1, #jit_code_start	;correct entry address
 STR     R1, [R13, #2 * 4]		;overwrite R2 on stack

 SUB     R9, R10, #1			;point R9 to the channel SCCB
LDMFD   R13!, {R0-R1, PC}		;call Voice Generator

 ._return
 LDMFD   R13!, {R9, PC}			;preserve R9


 .GateOn_required	DCD 0		;1 bit per voice, if 0 the GateOn entry
					;needs calling
 .terminate_all_voices	DCD 0		;<>0 if forcibly terminating voices

.our_temp_voice_generator
 LDR     PC, [R13], #4
 LDR     PC, [R13], #4
 LDR     PC, [R13], #4
 LDR     PC, [R13], #4
 LDR     PC, [R13], #4
 LDR     PC, [R13], #4
 LDR     PC, [R13], #4
 DCD     _name - our_temp_voice_generator
 ._name  DCB "ADFFS",0
 ALIGN




;kill_voices_in_range
;--------------------
;removes any voices that have entries within an address range

;Entry:
; R0 - 0=allow code to run   1=don't allow code to run
; R8 - start address
; R9 - end address

;@@@ needs to only kill voices in the current taskID

.kill_voices_in_range
 STMFD   R13!, {R0-R7, R14}

 STR     R0, terminate_all_voices
 ADR     R5, hv_voice_generators - sound_voice_handler.Size	;our Voice Generator table
 MOV     R2, #32			;remove voices
 ._L1
   ADD     R4, R5, R2, LSL #sound_voice_handler_shift	;R4 = our Voice Generator
   LDR     R1, [R4, #sound_voice_handler.voicegenerator]
   CMP     R1, R8			;within address range?
   CMPHS   R9, R1
   BLO     _P1				;NO

   MOV     R1, R2			;remove it
   SWI     XSound_RemoveVoice		;PRM4-35
   MOV     R1, #0			;clear our Voice Generator slot
   STR     R1, [R4, #sound_voice_handler.voicegenerator]

   ._P1
   SUBS    R2, R2, #1
 BNE     _L1
LDMFD   R13!, {R0-R7, PC}
