;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
.actual_PC_countdown DCD 0

.IRQ_debug_info
 STMFD   R13!, {R14}
 #if JIT_debug == 1 AND JIT_display_actual_PC == 1 AND compile_GPU == 1
   LDR     R14, HAL_vidc_list_dirty
   TEQ     R14, #0			;are we being re-entered?
   LDRNE   PC, [R13], #4			;YES, exit
 #endif
 LDR     R14, actual_PC_countdown
 SUBS    R14, R14, #1
 MOVMI   R14, #1024
 STR     R14, actual_PC_countdown
 LDRPL   PC, [R13], #4
 ADR     R2, debug_screen_addr
 LDR     R1, [R2, #0]
 MOV     R14, #0
 STR     R14, [R2, #0]
 our_writeS  "PC:"
 our_writeD R0
 STR     R1, [R2, #0]
LDMFD     R13!, {PC}
#endif

#if compile_IOMD == 1
.flush_screen
 STMFD   R13!, {R14}
 #if compile_IOMD == 1 AND JIT_debug == 1
   BL      report_JIT
 #endif
 LDR     R14, cpu_id
 TEQ     R14, #CPUID_StrongARM
 LDMNEFD R13!, {PC}
 MOV     R14, #1
 STRB    R14, IOMD_vsync + 1
 #if compile_RO == 400
   LDMFD   R13!, {R14}
   B       MMUControl_clean_D_cache
 #endif
 #if compile_RO == 450
   STMFD   R13!, {R0}
   ADR     R0, MMUControl_ARMop1
   MOV     R14, PC
   LDR     PC, [R0, #0]
   LDMFD   R13!, {R0, PC}
 #endif
#endif


ALIGN 32, 4
 .IRQ_vector_stack	DCD 0
 .existing_IRQVector	DCD 0
 .terminate_task	DCD 0		;taskid to kill when CTRL-SHIFT-F12 pressed
#if compile_IOMD == 1
 .IOMD_vsync		DCD 0		;did a Vsync occur?
#endif

;Entry: SVC32
ALIGN 32, 4
.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
   STR     R0, IRQ_vector_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
 #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   R0, user_PC				;YES
   MRSLO   R14, SPSR
   STRLO   R14, user_SPSR
   LOCK    R1
 #endif
 #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}
   #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]			;..fix for errata on SA-110 rev.2
   STMIA   R14, {R13, R14}^
   NOP
 #endif

 ._terminate_task_return2
 #if compile_GPU == 1
  ._unsupported_task
   ADD     R0, R13, #5 * 4
   STR     R0, IRQ_vector_stack			;update private stack
   LDMFD   R13, {R0-R2, R13, R14}		;restore stack
   ADD     R14, R14, #4
   LDR     PC, existing_IRQVector		;pass to RISCOS
 #endif
 #if compile_IOMD == 1
   BL      _check_IRQ				;trigger hypervised IRQs
   TST     R0, #&FF				;pass IRQ to RISCOS?
   BNE     _pass_to_OS				;YES

; we've handled the IRQ
   TST     R0, #(1 << IOC_VSync_Pulse) << 24	;did a Vsync occur?
   BEQ     _done

._flush_cache
; we've handled the IRQ and a VSync just occurred
   LDRB    R0, IOMD_vsync + 1
   TEQ     R0, #0
   BLEQ    flush_screen				;flush the screen cache
   MOV     R1, #0
   STRB    R1, IOMD_vsync + 1

   ._done
   #if compile_RO == 400
     LDMFD   R13!, {R0-R2, PC}^			;exit
   #endif
   #if compile_RO == 450
     ADD     R0, R13, #5 * 4
     STR     R0, IRQ_vector_stack
     LDMFD   R13, {R0-R2, R13, PC}^		;exit
   #endif

; there's an IRQ we didn't handle
   ._pass_to_OS
   MRS     R0, SPSR
   STR     R0, [R13, #-4]!
 ;STACK: SPSR,R0,R1,R2,{R13},R14
   #if compile_RO == 450
     STR     R13, IRQ_vector_stack
     MRS     R0, CPSR
     MSR     SPSR_all, R0			;force return in current state
     LDR     R13, [R13, #4 * 4]			;get original R13 from stack
   #endif
   #if compile_RO == 400
     MSR     SPSR_all, #%10000010		;return in IRQ26, IRQ disabled
   #endif
   ADR     R14, _return_SA + 4			;return with pipeline
   LDR     PC, existing_IRQVector		;pass to RISCOS

   ._return_SA					;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 == 450
     LDR     R13, IRQ_vector_stack
   #endif
   LDR     R0, [R13], #4
   MSR     SPSR_all, R0
   B       _flush_cache


  ._unsupported_task
   #if compile_RO == 400
     LDMFD   R13!, {R0-R2, R14}
   #endif
   #if compile_RO == 450
     ADD     R0, R13, #5 * 4	
     STR     R0, IRQ_vector_stack		;update private stack
     LDMFD   R13, {R0-R2, R13, R14}
   #endif
   ADD     R14, R14, #4
   LDR     PC, existing_IRQVector		;pass to RISCOS
 #endif



;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_IOMD == 1
   BL      _check_IRQ				;call game IRQ handler
   TST     R0, #&FF				;pass IRQ to RISCOS?
   BEQ     _return_no_irq			;NO
 #endif

 #if compile_RO == 400
   MSR     SPSR_all, #%10000010			;return in IRQ26, IRQ disabled
 #endif
 #if compile_RO > 400
   MRS     R0, CPSR
   MSR     SPSR_all, R0				;force return in current state
 #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
   MOV     R0, R13				;stack may be reset
   LDR     R13, IRQ_vector_stack
   #if JIT_instruction_trace == 0 AND compile_RO > 400
     STR     R0, [R13, #9 * 4]
   #endif
   #if JIT_instruction_trace == 1 AND compile_RO > 400
     STR     R0, [R13, #12 * 4]
   #endif
 #endif

 ._return_no_irq
;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_IOMD == 1
   LDRB    R1, IOMD_vsync
   TEQ     R1, #1 << IOC_VSync_Pulse		;did a Vsync occur?
   BEQ     _flush_cache				;YES
   B       _done
 #endif
 #if compile_GPU == 1
   ADD     R0, R13, #5 * 4			;update private stack
   STR     R0, IRQ_vector_stack
   LDMFD   R13, {R0-R2, R13, PC}^		;exit
 #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
;
; R0-R2 can be corrupted, but kept from IRQv calls
; R3-R12 must be kept from IRQv calls, and preserved around all other calls

._check_IRQ
 STMFD   R13!, {R12, R14}

 LDR     R12, IOMD_addr
 LDRB    R0, [R12, #IOC_IRQA_request]
; TST     R0, #(1 << IOC_Timer_0) + (1 << IOC_Timer_1) + (1 << IOC_VSync_Pulse)	;IRQ we handle?
 TST     R0, #(1 << IOC_Timer_1) + (1 << IOC_VSync_Pulse)	;IRQ we handle?
 AND     R0, R0, #1 << IOC_VSync_Pulse
 MOV     R0, R0, LSL #24
 ORR     R0, R0, #1
 LDMEQFD R13!, {R12, PC}		;NO, pass to OS
 BL      check_taskID			;task we support?
 LDMNEFD R13!, {R12, PC}		;NO, pass to OS

 STMFD   R13!, {R3-R6}
 MOV     R0, R0, LSR #24
 STRB    R0, IOMD_vsync

 BL      call_IRQv

 LDR     R3, IOMD_addr			;R3=IOMD address
 LDRB    R0, [R3, #IOC_IRQA_request]
; TST     R0, #(1 << IOC_Timer_0) + (1 << IOC_Timer_1) + (1 << IOC_VSync_Pulse)	;IRQ we handle?
 TST     R0, #(1 << IOC_Timer_1) + (1 << IOC_VSync_Pulse)	;IRQ we handle?
 BLNE    call_IRQ1V			;call IRQ1V

 ADR     R5, DeviceVectors		;check Device Vectors
 LDR     R3, IOMD_addr			;R3=IOMD address
 LDRB    R0, [R3, #IOC_IRQA_request]
 TST     R0, #1 << IOC_VSync_Pulse	;VSync?
;#if compile_IOMD == 1
;LDRNEB  R1, IOMD_vsync + 1
;TEQNE   R1, #19
;BLNE    MMUControl_clean_D_cache	;flush before VSync...just in case
;TST     R0, #1 << IOC_VSync_Pulse
;#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

 LDRB    R0, [R3, #IOC_IRQA_request]
 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

 LDR     R3, IOMD_addr
 LDRB    R0, [R3, #IOC_IRQA_request]	;pass to RISCOS IRQ handler
 LDRB    R1, [R3, #IOC_IRQB_request]	;check other IRQ sources
 LDRB    R2, [R3, #IOMD_IRQC_request]
 LDRB    R4, [R3, #IOMD_IRQD_request]
 ORR     R0, R0, R2
 ORR     R1, R1, R4
 ORR     R0, R0, R1
 LDRB    R4, IOMD_vsync
 ORR     R0, R0, R4, LSL #24		;top byte tracks vsync
LDMFD   R13!, {R3-R6, R12, PC}


._call_Vector
 STMFD   R13!, {R3, R5, R14}
 MOV     R3, #IOC_addr			;R3 points to base of IOC
 BL      call_device_claimant		;call claimant
LDMFD   R13!, {R3, R5, PC}
#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
 #if compile_IOMD == 1
   NOP
 #endif
 
 #if compile_RO > 311 AND compile_DEBUG == 0
   BL      shutdown_interrupts		;shutdown all tracked handlers

   MRS     R0, CPSR
   #if compile_RO == 400 AND compile_DEBUG == 0
     BIC     R0, R0, #%11011111
   #endif
   #if compile_RO > 400 AND compile_DEBUG == 0
     BIC     R0, R0, #%11001111
   #endif
   MSR     CPSR_all, R0			;USRxx IRQ/FIQ enabled
   #if compile_IOMD == 1 AND compile_DEBUG == 0
     NOP
   #endif
   B       call_OS_Exit
 #endif
 #if compile_RO > 311 AND compile_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