;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-R2, R14}
 #endif
 #if compile_RO > 400
   STR     R0, [R13, #-4]			;push R0
   LDR     R0, IRQ_vector_stack
   STMFD   R0!, {R1-R2, R13, R14}		;stack R1,R2 IRQ R13,R14
   LDR     R13, [R13, #-4]			;pull R0
   STR     R13, [R0, #-4]!			;stack R0
   MOV     R13, R0				;R13=private stack
 #endif
;STACK: R0-R2,{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
 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    R1
   STRLO   R14, user_PC				;YES
   MRSLO   R14, SPSR
   STRLO   R14, user_SPSR
   LOCK    R1
 #endif
 #if JIT_report_immediate == 1
   #if JIT_report_immediate == 1 AND  JIT_report_registers == 1
;STACK: R0-R2,{R13},R14
     ADR     R14, DAV_r0
     LDMIA   R13, {R0-R2}
     STMIA   R14!, {R0-R12}
     STMIA   R14, {R13, R14}^
     #if JIT_report_immediate == 1 AND  JIT_report_registers == 1 AND compile_RO == 400
       LDR     R0, [R13, #3 * 4]
     #endif
     #if JIT_report_immediate == 1 AND  JIT_report_registers == 1 AND compile_RO > 400
       LDR     R0, [R13, #4 * 4]
     #endif
     STR     R0, [R14, #2 * 4]
   #endif
   BL      report_JIT				;display JIT information
 #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-R2, PC}^			;NO, exit
     LDMFD   R13!, {R0-R2, R14}			;YES, restore stack
   #endif
   #if compile_IOMD == 1 AND compile_RO == 450
     LDMEQFD R13, {R0-R2, R13, PC}^		;NO, exit
   #endif
 #endif
 #if compile_RO > 400
   LDMFD   R13, {R0-R2, R13, R14}		;YES, restore stack
 #endif
 ADD     R14, R14, #4
 LDR     PC, existing_IRQVector			;pass to RISCOS

;STACK: R0-R2,{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 AND Unaligned_Support == 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
 ADD     R14, R14. #codelet_struct.tmp3
#endif
#if JIT_codelet_reuse == 1 OR Unaligned_Support == 1
 LDR     R1, first_codelet
 CMP     R14, #lastfree_codelet			;above last codelet?
 CMPHS   R1, R14				;possibly, below first 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 value
   CMP     R14, #jit_appspace_end		;check for codelet find fail
   BLO     _report_IRQ
   TEQ     R0, R1				;EQ if codelet header
 BNE     _L1
 ADD     R14, R14, #codelet_struct.tmp3 - codelet_struct.validation
#endif
						;R14=pointer to tmp3

;STACK: R0-R2,{R13},R14
 #if JIT_instruction_trace == 1
   ADD     R0, R14, #JIT_IT_codelet_size
   LDMIA   R0, {R0-R2}				;stack {<tmp3>-<tmp1>}
   STMFD   R13!, {R0-R2}
 #endif
 LDMIA   R14, {R0-R2}				;stack <tmp3>-<tmp1>
 STMFD   R13!, {R0-R2}
 LDR     R0, [R14, #codelet_struct.source - codelet_struct.tmp3]	;stack the source address
 MRS     R2, SPSR
 STR     R0, [R13, #-4]!
;STACK: <source>,{<tmp3>-<tmp1>},<tmp3>-<tmp1>,R0-R2,{R13},R14

 #if JIT_debug == 1
   TEMP    R1
   SUB     R0, R0, #8
   STR     R2, user_SPSR
   STR     R0, user_PC
   LOCK    R1
 #endif
 STMFD   R13!, {R2, R14}			;stack SPSR, codelet

;STACK: SPSR,<codelet>,<source>,{<tmp3>-<tmp1>},<tmp3>-<tmp1>,R0-R2,{R13},R14
 #if compile_RO == 400
   MSR     SPSR_all, #%10000010			;IRQ26, IRQ disabled
 #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			;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, #9 * 4]			;get original R13 from stack
   #endif
   #if JIT_instruction_trace == 1 AND compile_RO > 400
     LDR     R13, [R13, #12 * 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
;STACK: SPSR,<codelet>,<source>,{<tmp3>-<tmp1>},<tmp3>-<tmp1>,R0-R2,{R13},R14
 LDMFD   R13!, {R0, R1, R14}			;pull SPSR, codelet, source
;STACK: {<tmp3>-<tmp1>},<tmp3>-<tmp1>,R0-R2,{R13},R14
 MSR     SPSR_all, R0
 LDR     R2, [R1, #codelet_struct.source - codelet_struct.tmp3]
 TEQ     R2, R14				;has the codelet changed?
 LDMEQFD R13!, {R0, R2, R14}			;NO, restore tmp3-tmp1
 STMEQIA R1, {R0, R2, R14}
 #if JIT_instruction_trace == 0
   ADDNE   R13, R13, #3 * 4			;YES, drop <tmp3>-<tmp1>
 #endif
 #if JIT_instruction_trace == 1
   ADDNE   R13, R13, #3 * 4 * 2			;YES, drop {<tmp3>-<tmp1>},<tmp3>-<tmp1>
   ADDEQ   R1, R1, #JIT_IT_codelet_size
   LDMEQFD R13!, {R0, R2, R14}			;NO, restore {<tmp3>-<tmp1>}
   STMEQIA R1, {R0, R2, R14}
 #endif
;STACK: R0-R2,{R13},R14
 #if compile_RO == 400
   LDMFD   R13!, {R0-R2, PC}^
 #endif
 #if compile_RO > 400
   ADD     R0, R13, #5 * 4			;update private stack
   STR     R0, IRQ_vector_stack
   LDMFD   R13, {R0-R2, 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}		;NO, exit

 STMFD   R13!, {R3-R6}
 MOV     R3, #IOC_addr			;R3 points to base of IOC
 BL      call_IRQv
 LDR     R12, IOMD_addr
 LDRB    R0, [R12, #IOC_IRQA_status]

 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?
 MOVNE   R2, #page_zero_IRQ1V		;NO, is there an IRQ1V claim?
 BLNE    read_page_zero_value_R2	; |
 LDRNE   R3, hypervised_IRQ1V		; YES
 TEQNE   R2, R3				;   |
 MOV     R3, #IOC_addr			;R3 points to base of IOC
 MOVNE   R12, #0			;   | R12=0 to match RO2
 ADDNE   R2, R2, #jit_code_start	;   |
 BLNE    call_device_claimant		;   + 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!, {R3-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-R2,{R13},R14

._return_via_terminate_current_task
 MOV     R1, #0
 LDR     R2, domainID			;address of current task ID
 LDR     R2, [R2, #0]			;get current task ID
 TEQ     R0, R2				;is it the current task?
 LDREQ   R2, IRQsema			;YES,
 LDREQ   R2, [R2, #0]			; |+  check we're not threaded
 TEQEQ   R2, #0
 BNE     _terminate_task_return		;return

 STR     R1, terminate_task		;mark task as killed
 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, #3 * 4]		;overwrite stacked return
 #endif
 #if compile_RO > 400
   STR     R14, [R13, #4 * 4]		;overwrite stacked return
 #endif
 B       _terminate_task_return2


#if JIT_codelet_reuse == 1 OR Unaligned_Support == 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
 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

 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