;IRQ handler, corrects for codelet re-entrancy and provides a managed
;entry/exit for the IRQ hardware vector on RO5.x IOMD

#set tmp_irq_stack_size = 4 * 1024		;4K private stack



ALIGN 32, 12
 .IRQ_vector_stack	DCD 0
 .existing_IRQVector	DCD 0
 .terminate_task	DCD 0		;taskid to kill when CTRL-SHIFT-F12 presse

.IRQ_Vector_Handler
 STR     R0, [R13, #-4]				;push R0
 LDR     R0, IRQ_vector_stack
 STMFD   R0!, {R13, R14}			;stack IRQ R13, R14
 LDR     R13, [R13, #-4]			;pull R0
 STR     R13, [R0, #-4]!			;stack R0
 MOV     R13, R0				;R13=private stack
						;stack=R0, R13, R14
 LDR     R0, terminate_task			;should we terminate a task
 TEQ     R0, #0
 BNE     _return_via_terminate_current_task	;YES, check current task
 ._terminate_task_return

 #if compile_RO == 400
   BIC     R14, R14, #&FC000003			;clear flags / CPU on 26bit
 #endif

 SUB     R0, R14, #jit_appspace_end
 CMP     R0, #jit_code_start - jit_appspace_end	;in codelet space?
 BLO     _P1					;YES
 ._unsupported_task
 #if JIT_debug == 1
   CMP     R0, #jit_appspace_end		;in JIT appspace?
   TEMP    R0
   STRLO   R14, user_PC				;YES
   MRSLO   R14, SPSR
   STRLO   R14, user_SPSR
   LOCK    R0
 #endif

 #if compile_IOMD == 1
   BL      _check_IRQ				;trigger hypervised IRQs
   TEQ     R0, #0				;pass IRQ to RISCOS?
   LDMEQFD R13, {R0, R13, PC}^			;NO, exit
 #endif
 LDMFD   R13, {R0, R13, R14}			;NO,  restore stack
 LDR     PC, existing_IRQVector			; |+  pass to RISCOS

 ._P1
 #if JIT_WIMP_support == 1
   BL      check_task_ID			;task we support?
   BNE     _unsupported_task			;NO
   ADD     R14, R0, #jit_appspace_end
 #endif

#if JIT_codelet_reuse == 0
 BIC     R14, R14, #codelet_alignment - 1	;find codelet header
 ._L1
   LDR     R0, [R14, #-codelet_struct.codelet]!		;get .next value
   SUB     R0, R0, #jit_appspace_end
   CMP     R0, #jit_code_start - jit_appspace_end	;in codelet space?
 BHI     _L1
#endif
#if JIT_codelet_reuse == 1
 STR     R1, [R13, #-4]!
 LDR     R1, first_codelet
 CMP     R14, #lastfree_codelet			;above last codelet?
 CMPHS   R1, R14				;possibly, below first codelet?
 LDRLO   R1, [R13], #4				;not a CODELET
 BLO     _unsupported_task			; |+ pass on to OS

 BIC     R14, R14, #codelet_alignment - 1	;find codelet header
 ADD     R14, R14, #codelet_struct.validation
 MOV     R1, #CODELET_validation
 ._L1
   LDR     R0, [R14, #-codelet_struct.codelet]!		;get .next value
   TEQ     R0, R1				;EQ if codelet header
 BNE     _L1
 SUB     R14, R14, #codelet_struct.validation
 LDR     R1, [R13], #4
#endif

 LDR     R0, [R14, #codelet_struct.tmp4]	;stack local variables
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.tmp3]
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.tmp2]
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.tmp1]
 STR     R0, [R13, #-4]!
#if JIT_instruction_trace == 1
 LDR     R0, [R14, #codelet_struct.codelet + JIT_IT_codelet_size - 16]	;stack local variables
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.codelet + JIT_IT_codelet_size - 12]
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.codelet + JIT_IT_codelet_size - 8]
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.codelet + JIT_IT_codelet_size - 4]
 STR     R0, [R13, #-4]!
#endif
 LDR     R0, [R14, #codelet_struct.source]	;stack the source address
 STR     R0, [R13, #-4]!
 STR     R14, [R13, #-4]!			;stack codelet
 #if JIT_debug == 1
   TEMP    R14
   SUB     R0, R0, #8
   STR     R0, user_PC
 #endif
 MRS     R0, SPSR
 #if JIT_debug == 1
   STR     R0, user_SPSR
   LOCK    R14
 #endif
 STR     R0, [R13, #-4]!			;stack SPSR
 MRS     R0, CPSR
 MSR     SPSR_all, R0				;force return in current state

 #if compile_IOMD == 1
   BL      _check_IRQ				;call game IRQ handler
   TEQ     R0, #0				;pass IRQ to RISCOS?
   BEQ     _return				;NO
 #endif

 ADR     R14, _return + 4			;YES, return with pipeline
 #if compile_RO == 400
   ORR     R14, R14, #%10 + (%10 << 26)		;return in IRQ26 IRQ disabled
 #endif
 STR     R13, IRQ_vector_stack			;store private stack
#if JIT_instruction_trace == 0
 LDR     R13, [R13, #8 * 4]			;get original R13 from stack
#endif
#if JIT_instruction_trace == 1
 LDR     R13, [R13, #12 * 4]			;get original R13 from stack
#endif
 LDR     PC, existing_IRQVector			;pass to RISCOS

 ._return					;now in IRQ26 on RO3.5-4.x
 #if compile_RO == 400
   MOV     R0, #%10010010			;IRQ32 with IRQ disabled
   MSR     CPSR_all, R0
   NOP
 #endif

 LDR     R13, IRQ_vector_stack
 LDR     R0, [R13], #4				;pull SPSR
 MSR     SPSR_all, R0

 LDR     R14, [R13], #4				;pull codelet from stack
 LDR     R0, [R13], #4				;pull source address
 STR     R1, [R13, #-4]!
 LDR     R1, [R14, #codelet_struct.source]
 TEQ     R1, R0					;has the codelet changed?
 LDR     R1, [R13], #4
#if JIT_instruction_trace == 0
 ADDNE   R13, R13, #4 * 4			;YES, drop the local vars
#endif
#if JIT_instruction_trace == 1
 ADDNE   R13, R13, #8 * 4			;YES, drop the local vars
 LDREQ   R0, [R13], #4
 STREQ   R0, [R14, #codelet_struct.codelet + JIT_IT_codelet_size - 4]
 LDREQ   R0, [R13], #4
 STREQ   R0, [R14, #codelet_struct.codelet + JIT_IT_codelet_size - 8]
 LDREQ   R0, [R13], #4
 STREQ   R0, [R14, #codelet_struct.codelet + JIT_IT_codelet_size - 12]
 LDREQ   R0, [R13], #4
 STREQ   R0, [R14, #codelet_struct.codelet + JIT_IT_codelet_size - 16]
#endif
 LDREQ   R0, [R13], #4				;NO, restore local variables
 STREQ   R0, [R14, #codelet_struct.tmp1]
 LDREQ   R0, [R13], #4
 STREQ   R0, [R14, #codelet_struct.tmp2]
 LDREQ   R0, [R13], #4
 STREQ   R0, [R14, #codelet_struct.tmp3]
 LDREQ   R0, [R13], #4
 STREQ   R0, [R14, #codelet_struct.tmp4]
 ADD     R0, R13, #3 * 4			;update private stack
 STR     R0, IRQ_vector_stack
LDMFD   R13, {R0, R13, R14}
SUBS    PC, R14, #4



#if compile_IOMD == 1
;_check_IRQ
;----------
;Passes IRQ to virtualized IRQv, Device Vectors and IRQ1V
;
;Exit:
; R0 != 0 Pass the IRQ to RISCOS
; R0 == 0 IRQ has been handled

 ._check_IRQ
 STMFD   R13!, {R12, R14}

 LDR     R12, IOMD_addr
 LDRB    R0, [R12, #IOC_IRQA_status]
 TST     R0, #(1 << IOC_Timer_1) + (1 << IOC_VSync_Pulse)	;IRQ we handle?
 MOVEQ   R0, #1				;NO, pass to OS
 LDMEQFD R13!, {R12, PC}		; |+ exit

 STMFD   R13!, {R1-R6}
 MOV     R3, #IOC_addr			;R3 points to base of IOC
 LDR     R2, hypervised_IRQv
 TEQ     R2, #0				;was IRQv claimed?
 BEQ     _check_IRQ_P1			;NO
 BL      call_device_claimant		;YES, call claimant
 LDRB    R0, [R12, #IOC_IRQA_status]

 ._check_IRQ_P1
 ADR     R5, DeviceVectors		;check Device Vectors

 TST     R0, #1 << IOC_VSync_Pulse	;VSync?
 #if compile_IOMD == 1 AND VSync_at_Key_Scan == 1
   TEMP    R12
   MOVNE   R2, #0
   STRNE   R2, os_byte_19_happened
   LOCK    R12
 #endif
 LDRNE   R2, [R5, #(IOC_VSync_Pulse * device_table.size) + device_table.entry_code]
 TEQNE   R2, #0				;YES, claimed?
 LDRNE   R12, [R5, #(IOC_VSync_Pulse * device_table.size) + device_table.private]
 BLNE    _call_Vector			;raise an IRQ

 TST     R0, #1 << IOC_Timer_1		;T1?
 LDRNE   R2, [R5, #(IOC_Timer_1 * device_table.size) + device_table.entry_code]
 TEQNE   R2, #0				;YES, claimed?
 LDRNE   R12, [R5, #(IOC_Timer_1 * device_table.size) + device_table.private]
 BLNE    _call_Vector			;YES

 TST     R0, #(1 << IOC_Timer_1) + (1 << IOC_VSync_Pulse)	;IRQ handled?
 LDRNE   R2, hypervised_IRQ1V		;NO, is there an IRQ1V claim?
 TEQNE   R2, #0				; |
 MOV     R3, #IOC_addr			;R3 points to base of IOC
 MOVNE   R12, #0			;R12=0 to match RO2
 BLNE    call_device_claimant		;YES, call claimant

 LDR     R3, IOMD_addr
 LDRB    R0, [R3, #IOC_IRQA_status]	;pass to RISCOS IRQ handler
 LDRB    R1, [R3, #IOC_IRQB_status]	;check other IRQ sources
 LDRB    R2, [R3, #IOMD_IRQC_status]
 LDRB    R4, [R3, #IOMD_IRQD_status]
 ORR     R0, R0, R2
 ORR     R1, R1, R4
 ORR     R0, R0, R1
LDMFD   R13!, {R1-R6, R12, PC}


 ._call_Vector
 STMFD   R13!, {R3, R5, R14}
 BL      call_device_claimant		;call claimant
 LDMFD   R13!, {R3, R5, R14}
 LDR     R12, IOMD_addr
 LDRB    R0, [R12, #IOC_IRQA_status]	;re-get the IRQ status
MOV     PC, R14
#endif




;_return_via_terminate_current_task
;----------------------------------
;forces the IRQ vector to return via our task kill process if the current
;task needs to be killed

;Entry:
; R0 - task ID to terminate

._return_via_terminate_current_task
 LDR     R14, domainID			;address of current task ID
 LDR     R14, [R14, #0]			;get current task ID
 TEQ     R0, R14			;is it the current task?
 LDRNE   R14, [R13, #2 * 4]		;NO, restore R14 from stack
 BNE     _terminate_task_return		;return

 MOV     R0, #0
 STR     R0, terminate_task		;task about to be killed
 ADR     R14, _terminate_current_task + 4
 MRS     R0, SPSR
 BIC     R0, R0, #%11011111
 #if compile_RO == 400
   ORR     R0, R0, #%11000011		;SVC26 IRQ/FIQ disabled
   ORR     R14, R14, #&0C000003		;need to call it in SVC with IRQ/FIQ off
 #endif
 #if compile_RO > 400
   ORR     R0, R0, #%11010011		;SVC32 IRQ/FIQ disabled
 #endif
 MSR     SPSR_all, R0			;update return SPSR
 STR     R14, [R13, #2 * 4]		;overwrite stacked return
 B       _terminate_task_return


; Entered in correct SVCxx mode

 ._terminate_current_task
 LDR     R13, SVC_stack_init		;collapse SVCxx stack
 LDR     R0, IRQsema
 MOV     R1, #0
 STR     R1, [R0, #0]			;reset IRQsema
 MRS     R0, CPSR
 #if compile_RO > 400
   BIC     R0, R0, #%11001111
 #endif
 #if compile_RO == 400
   BIC     R0, R0, #%11011111
 #endif
 ORR     R0, R0, #%11000010		;IRQxx IRQ/FIQ disabled
 MSR     CPSR_all, R0
 #if compile_IOMD == 1
   NOP
 #endif
 LDR     R13, IRQ_stack_init		;reset IRQ stack base
 BIC     R0, R0, #%11001111
 ORR     R0, R0, #%10000011		;SVCxx IRQ disabled
 MSR     CPSR_all, R0
 #if compile_IOMD == 1
   NOP
 #endif

 #if compile_RO > 400
   BL      seriousError
 #endif

 #if compile_RO > 311 AND JIT_debug == 0
   BL      shutdown_interrupts		;shutdown all tracked handlers

   MRS     R0, CPSR
   BIC     R0, R0, #%11001111		;USRxx IRQ/FIQ enabled
   MSR     CPSR_all, R0
   NOP
   SWI     OS_Exit
 #endif
 #if compile_RO > 311 AND JIT_debug == 1
   MOV     R12, #&D			;ADFF500D
   B       report_unimplemented
 #endif

#if compile_RO > 400
.seriousError
 STR     R14, [R13, #-4]!
 ADR     R0, _adffs_task_terminated
 MOV     R2, #SeriousErrorV_Recover	;notify system we're recovering
 MOV     R9, #Vector_SeriousErrorV
 SWI     XOS_CallAVector		;PRM1-70
LDR     PC, [R13], #4

 ._adffs_task_terminated DCD 0
 DCB "ADFFS recovery: Task terminated", 0
 ALIGN
#endif