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

#if JIT_debug == 1 AND JIT_display_actual_PC == 1
.IRQ_debug_info
 STMFD   R13!, {R14}
 LDR     R14, HAL_vidc_list_dirty
 TEQ     R14, #0			;are we being re-entered?
 LDRNE   PC, [R13], #4			;YES, exit
 BL      our_VDU30
 our_writeS  "PC:"
 our_writeD R0
 #if JIT_debug == 1 AND JIT_display_actual_PC == 1 AND MMUControl2_support == 1 AND Cached_DA2 == 1
   LDR     R0, MMUControl_ARMop1
   MOV     R14, PC
   MOV     PC, R0			;flush CPU L2 cache
 #endif
LDMFD     R13!, {PC}
#endif


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

;Entry: SVC32

.IRQ_Vector_Handler
 SUB     R14, R14, #4
 #if compile_RO == 400
   STMFD   R13!, {R0, R14}
 #endif
 #if compile_RO > 400
   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
 #endif
;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     _in_codelet				;YES
 ._unsupported_task
 #if JIT_debug == 1 AND JIT_display_actual_PC == 1
   MOV     R0, R14
   BL      IRQ_debug_info
 #endif
 #if JIT_debug == 1 AND JIT_display_actual_PC == 0
   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

 ._terminate_task_return2
 #if compile_IOMD == 1
   BL      _check_IRQ				;trigger hypervised IRQs
   TEQ     R0, #0				;pass IRQ to RISCOS?
   #if compile_IOMD == 1 AND compile_RO == 400
     LDMEQFD R13!, {R0, PC}^			;NO, exit
     LDMFD   R13!, {R0, R14}			;YES, restore stack
   #endif
   #if compile_IOMD == 1 AND compile_RO == 450
     LDMEQFD R13, {R0, R13, PC}^		;NO, exit
   #endif
 #endif
 #if compile_RO > 400
   LDMFD   R13, {R0, R13, R14}			;YES, restore stack
 #endif
 ADD     R14, R14, #4
 LDR     PC, existing_IRQVector			;pass to RISCOS

;STACK: R0,{R13},R14
 ._in_codelet
 BL      check_taskID				;task we support?
 BNE     _unsupported_task			;NO
 ADD     R14, R0, #jit_appspace_end

#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]!
;STACK: R1,R0,{R13},R14
 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
   CMP     R14, #jit_appspace_end		;check for codelet find fail
   BLO     _report_IRQ
   TEQ     R0, R1				;EQ if codelet header
 BNE     _L1
 SUB     R14, R14, #codelet_struct.validation
 LDR     R1, [R13], #4
#endif

;STACK: R0,{R13},R14
 LDR     R0, [R14, #codelet_struct.tmp3]	;stack local variables
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.tmp2]
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.tmp1]
 STR     R0, [R13, #-4]!
;STACK: <tmp1>,<tmp2>,<tmp3>,R0,{R13},R14
#if JIT_instruction_trace == 1
 LDR     R0, [R14, #codelet_struct.tmp3 + JIT_IT_codelet_size]	;stack local variables
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.tmp2 + JIT_IT_codelet_size]
 STR     R0, [R13, #-4]!
 LDR     R0, [R14, #codelet_struct.tmp1 + JIT_IT_codelet_size]
 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
;STACK: <codelet>,<source>,<tmp1>,<tmp2>,<tmp3>,R0,{R13},R14
 #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

;STACK: SPSR,<codelet>,<source>,<tmp1>,<tmp2>,<tmp3>,R0,{R13},R14
 #if compile_RO == 400
   MSR     SPSR_all, #%10000010			;IRQ26
 #endif
 #if compile_RO > 400
   MRS     R0, CPSR
   MSR     SPSR_all, R0				;force return in current state
 #endif

 #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
   STR     R13, IRQ_vector_stack		;store private stack
   #if JIT_instruction_trace == 0 AND compile_RO > 400
     LDR     R13, [R13, #7 * 4]			;get original R13 from stack
   #endif
   #if JIT_instruction_trace == 1 AND compile_RO > 400
     LDR     R13, [R13, #10 * 4]		;get original R13 from stack
   #endif
 #endif
 LDR     PC, existing_IRQVector			;pass to RISCOS

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

 #if compile_RO > 400
   LDR     R13, IRQ_vector_stack
 #endif
 LDR     R0, [R13], #4				;pull SPSR
;STACK: <codelet>,<source>,<tmp1>,<tmp2>,<tmp3>,R0,{R13},R14
 MSR     SPSR_all, R0

 LDR     R14, [R13], #4				;pull codelet from stack
 LDR     R0, [R13], #4				;pull source address
;STACK: <tmp1>,<tmp2>,<tmp3>,R0,{R13},R14
 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, #3 * 4			;YES, drop the local vars
 #endif
 #if JIT_instruction_trace == 1
   ADDNE   R13, R13, #3 * 4 * 2			;YES, drop the local vars
   LDREQ   R0, [R13], #4
   STREQ   R0, [R14, #codelet_struct.tmp1 + JIT_IT_codelet_size]
   LDREQ   R0, [R13], #4
   STREQ   R0, [R14, #codelet_struct.tmp2 + JIT_IT_codelet_size]
   LDREQ   R0, [R13], #4
   STREQ   R0, [R14, #codelet_struct.tmp3 + JIT_IT_codelet_size]
 #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]
;STACK: R0,{R13},R14
 #if compile_RO == 400
   LDMFD   R13!, {R0, PC}^
 #endif
 #if compile_RO > 400
   ADD     R0, R13, #3 * 4			;update private stack
   STR     R0, IRQ_vector_stack
   LDMFD   R13, {R0, R13, PC}^
 #endif


#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
 BL      check_taskID			;task we support?
 LDMNEFD R13!, {R12, PC}

 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?
 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: SVC32
; R0 - task ID to terminate
;STACK: R0,{R13},R14

._return_via_terminate_current_task
MOV R14, #0
STR R14, terminate_task		;mark task as killed
 LDR     R14, domainID			;address of current task ID
 LDR     R14, [R14, #0]			;get current task ID
 TEQ     R0, R14			;is it the current task?
 LDREQ   R14, IRQsema			;YES,
 LDREQ   R14, [R14, #0]			; |+  check we're not threaded
 TEQEQ   R14, #0
 #if compile_RO == 400
   LDRNE   R14, [R13, #4]		;NO, restore R14 from stack
 #endif
 #if compile_RO > 400
   LDRNE   R14, [R13, #2 * 4]		;NO, restore R14 from stack
 #endif
 BNE     _terminate_task_return		;return

 ADR     R14, _terminate_current_task
 MRS     R0, SPSR
 BIC     R0, R0, #%11001111
 ORR     R0, R0, #%11000011		;SVCxx IRQ/FIQ disabled
; #if compile_RO == 400
;   ORR     R14, R14, #&0C000003		;need to call it in SVC with IRQ/FIQ off
; #endif
 MSR     SPSR_all, R0			;update return SPSR
 #if compile_RO == 400
   STR     R14, [R13, #4]		;overwrite stacked return
 #endif
 #if compile_RO > 400
   STR     R14, [R13, #2 * 4]		;overwrite stacked return
 #endif
 B       _terminate_task_return2


#if JIT_codelet_reuse == 1
;STACK: R1,R0,{R13},R14
._report_IRQ
 LDR     R1, [R13], #4			;pull R1
 LDR     R0, [R13], #4			;pull R0
 ADR     R14, DAV_r0
 STMIA   R14, {R0-R12}
 #if JIT_codelet_reuse == 1 AND compile_RO == 400
   ADD     R12, R13, #4			;correct R13
 #endif
 #if JIT_codelet_reuse == 1 AND compile_RO > 400
   LDR     R12, [R13], #4		;pull R13
 #endif
 STR     R12, [R14, #13*4]
 LDMFD   R13!, {R12}			;pull R14
 STR     R12, [R14, #14*4]
 STR     R12, [R14, #15*4]		;set PC as R14
 MOV     R12, #5			;adff5005
 B       report_unimplemented
#endif


; Entered in correct SVCxx mode

._terminate_current_task
 TEMP    R2
 LDR     R13, SVC_stack_init		;collapse SVCxx stack
 LDR     R0, IRQsema
 MOV     R1, #0
 STR     R1, [R0, #0]			;reset IRQsema
 STR     R1, terminate_task		;mark task as killed
 MRS     R0, CPSR
 #if compile_RO == 400
   BIC     R0, R0, #%11011111
 #endif
 #if compile_RO > 400
   BIC     R0, R0, #%11001111
 #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

; #if compile_RO == 400
;   BIC     R1, R0, #%11001111
;   ORR     R1, R1, #%11010011		;SVC32 IRQ/FIQ disabled
;   MSR     CPSR_all, R1
;   NOP
;   LDR     R13, SVC32_stack_init	;reset IRQ stack base
;  
;   BIC     R1, R0, #%11001111
;   ORR     R1, R1, #%11010010		;IRQ32 IRQ/FIQ disabled
;   MSR     CPSR_all, R1
;   NOP
;   LDR     R13, IRQ32_stack_init	;reset IRQ stack base
; #endif

 BIC     R0, R0, #%11001111
 ORR     R0, R0, #%11000011		;SVCxx IRQ/FIQ disabled
 MSR     CPSR_all, R0
 NOP

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

   MRS     R0, CPSR
   #if compile_RO == 400 AND JIT_debug == 0
     BIC     R0, R0, #%11011111
   #endif
   #if compile_RO > 400 AND JIT_debug == 0
     BIC     R0, R0, #%11001111
   #endif
   MSR     CPSR_all, R0			;USRxx IRQ/FIQ enabled
   #if compile_IOMD == 1 AND JIT_debug == 0
     NOP
   #endif
;   MOV     R0, #CAOPointer		;set CAO
;   MOV     R1, #0
;   SWI     XOS_ChangeEnvironment	;PRM1-320
   B       call_OS_Exit
 #endif
 #if compile_RO > 311 AND JIT_debug == 1
   MOV     R12, #&D			;ADFF500D
   B       report_unimplemented
 #endif
 LOCK    R2


#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