;NOTE: IOC addressing is as follows
; 3200000 - IOC internal reg 0
; 3200004 - IOC internal reg 1
; ...
; 3220000 - Bank 2 slow
; 32A0000 - Bank 2 medium
; 3320000 - Bank 2 fast
; 33A0000 - Bank 2 synchronous
; ...
; 3250000 - Bank 5 slow
; 32D0000 - Bank 5 medium
; 3350000 - Bank 5 fast
; 33D0000 - Bank 5 synchronous
;                    3         2         1
;                   10987654321098765432109876543210
;to decode address %00000011001ttbbb000000000aaaaa00
;where:
;
; tt is access type:
;   00 slow
;   01 medium
;   10 fast
;   11 synchronous
;
; bbb is the bank:
;    0 IOC control registers
;    1 FDC (fast only)
;    2 Econet (sync)                 33A0000..33A000C
;    3 Serial port (sync)
;    4 Expansions cards
;    5 HD controller (medium)
;    6 Printer (fast)                IOC=3350010 / 18     711=30109E0 / E4
;    7 Expansion cards (slow)
;
; aaaaa is the offset in the device
;
;references:
; www.heyrick.co.uk/assembler/podule.html
;
; KerBang! - reads/writes to 3310000 (FDC)


;IOC device numbers:
;0-7  correspond to bits 0-7 of IRQ A register
;8-15 correspond to bits 0-7 of IRQ B register

;IOC device numbers (PRM1-121):
#set IOC_VSync_Pulse	= 3
#set IOC_Timer_0	= 5		;System timer, default value: 20000 (Zelanites, Labryinth)
#set IOC_Timer_1	= 6
#set IOC_SSBC		= 9		;Sound System Buffer Change (No Excuses, Rotor)
					;not available on IOMD
;#set IOMD_fake_SSBC	= 7		;fake SSBC using always active IRQ

#set IOC_CR		= &00
#set IOC_floppy_ready	= 1 << 2
#set IOC_IF_pin		= 1 << 6	;PACK
#set IOC_IR_pin		= 1 << 7	;VFLYBK (high during flyback,
					;        low during display)
#set IOC_CR_init	= %11111111	;all CR bits are initialized to 1

#set IOC_IRQA_status	= &10
#set IOC_IRQA_request	= &14
#set IOC_IRQA_mask	= &18
#set IOC_IRQA_init	= %10101100	;initial IRQA value

#set IOC_IRQB_status	= &20
#set IOC_IRQB_request	= &24
#set IOC_IRQB_mask	= &28
#set IOC_IRQB_init	= %00000010	;initial IRQB value

#set IOC_FIQ_status	= &30
#set IOC_FIQ_request	= &34
#set IOC_FIQ_mask	= &38

#set IOC_T0_latch_low	= &40
#set IOC_T0_latch_high	= &44

#set IOC_T1_latch_low	= &50
#set IOC_T1_latch_high	= &54

#set IOMD_IRQC_status	= &60
#set IOMD_IRQC_request	= &64
#set IOMD_IRQC_mask	= &68

#set IOMD_IRQD_status	= &70
#set IOMD_IRQD_request	= &74
#set IOMD_IRQD_mask	= &78


;IOC_abort
;---------
;An attempt was made to write to the IOC memory space
;
;Entry:
; DAV_r0 - memory block, containing aborting registers (R0-PC)
; R1 - aborting address
;
;Exit:
; R0-R12 corrupt

ALIGN   32, 4
.IOC_abort
 ADR     R6, DAV_r0			;R6 = aborting registers (R0-PC)
 LDR     R0, [R6, #15 * 4]		;the faulting address
 ANDS    R3, R1, #%111 << 16		;R3 = bank (ie device)
 LDR     R0, [R0, #0]			;R0 = faulting instruction
 BEQ     IOC_register			;EQ if IOC control registers

 TEQ     R3, #&33A0000 AND &70000	;Econet?  RTFM Joystick?
 BEQ     IOC_Econet			;translate to AEH60 card

 TEQ     R3, #1 << 16			;FDC?
 MOVEQ   R12, #6
 BEQ     report_unimplemented		;report until its coded ADFF5006

 #if JIT_debug == 1
   TEQ     R3, #&3350010 AND &70000	;Parallel Port?
   ADREQ   R2, jit_joysticks		;YES,
   LDREQ   R1, [R2, #0]			; |
   ORREQ   R1, R1, #%10000		; |+ display a P in debug mode
   STREQ   R1, [R2, #0]
 #endif
					;all others:
					;writes - ignore
					;reads - return FFFFFFFF

					;decode instruction
					;------------------------------------

 MVN     R2, #0				;reads return FFFFFFFF
                 ;%100PUSWL		;STM instruction
 AND     R1, R0, #%11100001 << 20
 TEQ     R1,     #%10000001 << 20	;LDMxx Rx, {Rn...Rn} ?
 BEQ     _LDM				;YES
 TEQ     R1,     #%10000000 << 20	;STMxx Rx, {Rn...Rn} ?
 BNE     _not_STM			;NO
B       AbortI_skip			;ignore STM and exit back to user code



 ._LDM
					;R0 bits 15...0 contain registers
					;..written to IOC
 MOV     R5, R0, LSL #17		;reduce R5 to registers being written
 MOVS    R5, R5, LSR #18		;stripping off PC and leaving R0 in CS
 MOV     R1, #0
 ._LDM_loop
   STRCS   R2, [R6, R1, LSR #12 - 2]	;update register

   ADD     R1, R1, #1 << 12		;go to next register
   MOVS    R5, R5, LSR #1		;are there any more registers?
 BNE     _LDM_loop			;loop until we've handled all registers
 BCS     _LDM_loop			;catch the last register in C
B       AbortI_skip			;exit back to user code


 ._not_STM
                 ;%01IPUBWL		;STR instruction
 AND     R1, R0, #%11000001 << 20
 TEQ     R1,     #%01000001 << 20	;LDR Rd, [Rn ...] ?
 BNE     AbortI_skip			;ignore write

 ._LDR					;R0 contains instruction to decode
 AND     R1, R0, #%1111 << 12		;R1 = Rd << 12

 TST     R0, #1 << 22			;byte or word?
 ANDNE   R2, R2, #&FF			;byte
 STR     R2, [R6, R1, LSR #12 - 2]	;R0 = value read from IOC
B       AbortI_skip			;exit back to user code




;IOC_register
;------------
;An abort occurred access an IOC internal register

;Entry:
; R0 - faulting instruction
; R1 - address being accessed
; R6 - aborting registers (R0-PC)

;Exit:
; R0-R12 corrupt

.IOC_register
 AND     R7, R1, #%1111100		;R7=IOC register
					;decode instruction and translate to
					;RO5

                 ;%100PUSWL		;STM instruction
 AND     R1, R0, #%11100001 << 20
 TEQ     R1,     #%10000001 << 20	;LDMxx Rx, {Rn...Rn} ?
 BEQ     _LDM				;YES
 TEQ     R1,     #%10000000 << 20	;STMxx Rx, {Rn...Rn} ?
 BNE     _not_STM			;NO

					;R0 bits 15...0 contain registers
					;..written to IOC
 MOV     R5, R0, LSL #17		;reduce R5 to registers being written
 MOVS    R5, R5, LSR #18		;stripping off PC and leaving R0 in CS
 ._STM_loop
   LDRCSB  R2, [R6]			;C set if register used
   #if compile_RO == 400
     LDRCS   R1, IOC_addr
     BLCS    _IOC_write			;proxy the write on IOMD 26bit
   #endif
   #if compile_RO > 400
     ADRCS   R14, _STM_return		;return address for IOC handler
     ADRCS   R3, _IOC_write		;address of IOC jump table
     ADRCS   R1, IOC_registers
     ADDCS   PC, R3, R7			;jump to IOC handler

     ._STM_return
   #endif
   ADD     R6, R6, #4			;go to next register
   ADD     R7, R7, #4			;increase IOC register
   MOVS    R5, R5, LSR #1		;are there any more registers?
 BNE     _STM_loop			;loop until we've handled all registers
 BCS     _STM_loop			;catch the last register in C

B       AbortI_skip			;exit back to user code


 ._LDM
					;R0 bits 15...0 contain registers
					;..written to IOC
 MOV     R5, R0, LSL #17		;reduce R5 to registers being written
 MOVS    R5, R5, LSR #18		;stripping off PC and leaving R0 in CS
 MOV     R1, #0
 ._LDM_loop
   BLCS    _IOC_read

   ADD     R1, R1, #1 << 12		;go to next register
   ADD     R7, R7, #4			;increase IOC register
   MOVS    R5, R5, LSR #1		;are there any more registers?
 BNE     _LDM_loop			;loop until we've handled all registers
 BCS     _LDM_loop			;catch the last register in C
B       AbortI_skip			;exit back to user code


					;R0 contains instruction to decode
 ._not_STM
 ADR     R14, AbortI_skip		;return address
                 ;%01IPUBWL		;STR instruction
 AND     R2, R0, #%11000001 << 20
 TEQ     R2,     #%01000001 << 20	;LDR Rd, [Rn ...] ?
 AND     R1, R0, #%1111 << 12		;R1 = Rd << 12
 BEQ     _IOC_read			; +-  process read

 TEQ     R2,     #%01000000 << 20	;STR Rd, [Rn ...] ?
 BNE     AbortI_skip			;NO

 LDRB    R2, [R6, R1, LSR #12 - 2]	;R2 = value written to IOC
 #if compile_RO > 400
   ADR     R3, _IOC_write		;address of IOC jump table
   ADR     R1, IOC_registers
   ADD     PC, R3, R7			;jump to IOC handler
 #endif
 #if compile_RO == 400
   B       _IOC_write
 #endif




;_IOC_read
;---------
;read an IOC register
;on IOMD 26bit OS this should never be entered, but should read actual IOC if it is
;on 32bit RO registers are emulated

;Entry:
;R0 - original instruction
;R1 - CPU register to read into << 12
;R6 - pointer to registers
;R7 - IOC register being read
;
;Exit:
;R0-R4 - corrupt
;R5-R12 - must be preserved

._IOC_read
 #if compile_IOMD == 1
   LDR     R2, IOMD_addr		;IOMD proxy the read
   TEQ     R7, #IOC_IRQB_request
   LDRNEB  R2, [R2, R7]			;load the IOC register
   BNE     _IOC_read_done

   ADR     R2, IOC_registers		;emulate IRQB
 #endif
 #if compile_GPU == 1
   ADR     R2, IOC_registers
   TEQ     R7, #IOC_IRQA_request	;is it a request register?
   TEQNE   R7, #IOC_IRQB_request
   TEQNE   R7, #IOC_FIQ_request
   LDRNEB  R2, [R2, R7, LSR #2]		;NO, read our copy
   BNE     _IOC_read_done		;|+  and exit early
 #endif

 ._use_emulation
 SUB     R4, R7, #4			;YES, switch to read status
 LDRB    R3, [R2, R4, LSR #2]		; | load the status
 ADD     R4, R4, #8			; |
 LDRB    R4, [R2, R4, LSR #2]		; | and the mask
 AND     R2, R4, R3			; | R2=request value

 ._IOC_read_done
 STR     R2, [R6, R1, LSR #12 - 2]	;update the processor register
MOV     PC, R14




;Entry:
; R0 = original instruction
; R1 = IOC_registers (32bit builds)
; R2 = value to write
; R7 = IOC register being written too (must be preserved)

;Exit:
; R0-R3  - corrupt
; R4-R12 - preserved

._IOC_write
 #if compile_RO == 400
   LDR     R1, IOMD_addr		;IOMD proxy the write
   CMP     R7, #&40			;Timer
   STRB    R2, [R1, R7]
   MOVLO   PC, R14			;NO
   TST     R7, #%1000			;GO or Latch command?
   MOVEQ   PC, R14			;NO
   BIC     R3, R7, #%1111		;need to add a delay before returning
   LDRB    R3, [R1, R3]
   BIC     R3, R7, #%1111
   LDRB    R3, [R1, R3]
   MOV     PC, R14
 #endif
 #if compile_RO == 450
   B       _IOC_write_00		;Control
   B       _IOC_write_04		;Serial Tx Data
   MOV     PC, R14
   MOV     PC, R14
   MOV     PC, R14
   B       _proxy_write			;IRQA clear
   B       _proxy_write			;IRQA mask
   MOV     PC, R14
   MOV     PC, R14
   B       _IOC_write_24		;IRQB clear
   B       _IOC_write_28		;IRQB mask
   MOV     PC, R14
   MOV     PC, R14
   MOV     PC, R14
   B       _proxy_write			;FIQ mask
   MOV     PC, R14
   B       _proxy_write			;T0 latch Low
   B       _proxy_write			;T0 latch High
   B       _proxy_Twrite			;T0 go
   B       _proxy_Twrite			;T0 latch command
   B       _proxy_write			;T1 latch Low
   B       _proxy_write			;T1 latch High
   B       _proxy_Twrite			;T1 go
   B       _proxy_Twrite			;T1 latch command
   B       _proxy_write			;T2 latch Low
   B       _proxy_write			;T2 latch High
   B       _proxy_Twrite			;T2 go
   B       _proxy_Twrite			;T2 latch command
   B       _proxy_write			;T3 latch Low
   B       _proxy_write			;T3 latch High
   B       _proxy_Twrite			;T3 go
   B       _proxy_Twrite			;T3 latch command

._proxy_Twrite			;need to add a delay after GO / latch command
   LDR     R3, IOMD_addr		;IOMD proxy the write
   STRB    R2, [R3, R7]
   BIC     R2, R7, #%1111
   LDRB    R2, [R3, R2]
   BIC     R2, R7, #%1111
   LDRB    R2, [R3, R2]
   MOV     PC, R14
 #endif
 #if compile_RO > 450
   B       _IOC_write_00		;Control
   B       _IOC_write_04		;Serial Tx Data
   MOV     PC, R14
   MOV     PC, R14
   MOV     PC, R14
   B       _IOC_write_14		;IRQA clear
   B       _IOC_write_18		;IRQA mask
   MOV     PC, R14
   MOV     PC, R14
   B       _IOC_write_24		;IRQB clear
   B       _IOC_write_28		;IRQB mask
   MOV     PC, R14
   MOV     PC, R14
   MOV     PC, R14
   B       _IOC_write_38		;FIQ mask
   MOV     PC, R14
   B       _IOC_write_40		;T0 latch Low
   B       _IOC_write_44		;T0 latch High
   B       go_T0			;T0 go
   B       _IOC_write_4C		;T0 latch command
   B       _IOC_write_50		;T1 latch Low
   B       _IOC_write_54		;T1 latch High
   B       go_T1			;T1 go
   B       _IOC_write_5C		;T1 latch command
   B       _IOC_write_60		;T2 latch Low
   B       _IOC_write_64		;T2 latch High
   B       _IOC_write_68		;T2 go
   B       _IOC_write_6C		;T2 latch command
   B       _IOC_write_70		;T3 latch Low
   B       _IOC_write_74		;T3 latch High
   B       _IOC_write_78		;T3 go
   B       _IOC_write_7C		;T3 latch command
 #endif




._IOC_write_14				;IRQA clear
 SUB     R1, R1, #1			;switch to mask
 LDRB    R3, [R1, R7, LSR #2]		;load the status
 BIC     R3, R3, R2			;clear the IRQ
._proxy_write_update_our_copy
 STRB    R3, [R1, R7, LSR #2]
 #if compile_RO == 450
._proxy_write
   LDR     R3, IOMD_addr		;IOMD proxy the write
   STRB    R2, [R3, R7]
 #endif
MOV     PC, R14


._IOC_write_18				;IRQA mask
 #if compile_RO == 450
   B       _proxy_write_update_our_copy	;IOMD proxy the write
 #endif
 #if compile_GPU == 1
   LDRB    R3, [R1, R7, LSR #2]
   STRB    R2, [R1, R7, LSR #2]
   TST     R2, #1 << IOC_Timer_1	;Timer 1 enabled?
   TSTNE   R3, #1 << IOC_Timer_1	;was Timer 1 previously disabled?
   MOVEQ   PC, R14			;NO
  
   LDRB    R2, [R1, #IOC_T1_latch_low >> 2]
   LDRB    R3, [R1, #IOC_T1_latch_high >> 2]
   ORRS    R2, R2, R3
   STRNE   R2, T1_value
   STRNE   R2, T1_latch
   MOV     PC, R14
 #endif


 #if compile_GPU == 1
._IOC_write_4C				;T0 latch
   ;needs to copy the current T0 value to the T0 count low/high registers
   LDR     R0, T0_value
   STRB    R0, [R1, #IOC_T0_latch_low >> 2]
   MOV     R0, R0, LSR #8
   STRB    R0, [R1, #IOC_T0_latch_high >> 2]
   MOV     PC, R14

._IOC_write_5C				;T1 latch
   ;needs to copy the current T1 value to the T1 count low/high registers
   LDR     R0, T1_value
   STRB    R0, [R1, #IOC_T1_latch_low >> 2]
   MOV     R0, R0, LSR #8
   STRB    R0, [R1, #IOC_T1_latch_high >> 2]
   MOV     PC, R14

._IOC_write_40				;T0 latch low
._IOC_write_44				;T0 latch high
._IOC_write_50				;T1 latch low
._IOC_write_54				;T1 latch high
 #endif

._IOC_write_24				;IRQB clear
; #if compile_RO == 400
;   TST     R2, #1 << (IOC_SSBC - 8)	;SSBC needs faking
;   MOVEQ   PC, R14
;   LDRB    R3, [R1, #IOC_IRQA_status >> 2]	;load the status
;   BIC     R3, R3, #IOC_SSBC		;clear the IRQ
;   STRB    R3, [R1, R7, LSR #2]
;   LDR     R3, IOMD_addr
;   LDRB    R2, [R3, #IOC_IRQA_mask]	;mark out our fake IRQ
;   BIC     R2, R2, #1 << IOMD_fake_SSBC
;   STRB    R2, [R3, #IOC_IRQA_mask]
;   MOV     R2, #1 << IOC_fake_SSBC	;and clear the IRQ
;   STRB    R2, [R3, #IOC_IRQA_request]
;   MOV     PC, R14
; #endif

._IOC_write_28				;IRQB mask
; #if compile_RO == 400
;   TST     R2, #1 << (IOC_SSBC - 8)	;SSBC needs faking
;   LDREQ   R3, IOMD_addr
;   LDREQB  R0, [R3, #IOC_IRQA_mask]
;   BICEQ   R0, R0, #1 << IOMD_fake_SSBC
;   STREQB  R0, [R3, #IOC_IRQA_mask]
;   MOV     PC, R14
; #endif

 ._IOC_write_00
 ._IOC_write_04
 ._IOC_write_38
 ._IOC_write_60
 ._IOC_write_64
 ._IOC_write_68
 ._IOC_write_6C
 ._IOC_write_70
 ._IOC_write_74
 ._IOC_write_78
 ._IOC_write_7C
  STRB    R2, [R1, R7, LSR #2]		;store the IOC register
  MOV     PC, R14


#if compile_GPU == 1
 .T0_value		DCD 20000	;current value of T0 Timer
 .T0_latch		DCD 20000	;value to set when Timer wraps
 .T1_value		DCD &10000000	;current value of T1 Timer
 .T1_latch		DCD &10000000	;value to set when Timer wraps
 .IOC_ticks_flyback	DCD 0		;IOC timer ticks during flyback
 .IOC_ticks_scanline	DCD 0		;IOC timer ticks per scanline



ALIGN   32, 4
;IOC_trigger_vsync
;-----------------
;triggers a virtual VSync
;
;Exit:
; R1-R6, R12 - corrupted

.IOC_trigger_vsync
 STMFD   R13!, {R0, R14}

 ADR     R4, IOC_registers
 LDRB    R0, [R4, #IOC_IRQA_mask >> 2]
 TST     R0, #1 << IOC_VSync_Pulse	;do we need to raise an IRQ?
 BEQ     _T0				;NO, skip

 LDRB    R0, [R4, #IOC_IRQA_status >> 2]
 ORR     R0, R0, #1 << IOC_VSync_Pulse	;flag IRQ
 STRB    R0, [R4, #IOC_IRQA_status >> 2]

 BL      check_JIT_and_taskID
 BNE     _not_JIT_task
 BL      call_IRQv

 ADR     R4, IOC_registers
 LDRB    R2, [R4, #IOC_IRQA_status >> 2]
 TST     R2, #1 << IOC_VSync_Pulse	;was the IRQ handled?
 BLNE    call_IRQ1V			;NO

 ADR     R0, DeviceVectors + (IOC_VSync_Pulse * device_table.size)
 LDR     R2, [R0, #device_table.entry_code]
 TEQ     R2, #0				;was OS_ClaimDeviceVector used?
 LDRNE   R12, [R0, #device_table.private]
 MOVNE   R3, #IOC_addr			;R3 points to base of IOC
 BLNE    call_device_claimant		;YES, call claimant

 ._not_JIT_task
 ADR     R4, IOC_registers		;clear VSync flag
 LDRB    R0, [R4, #IOC_IRQA_status >> 2]
 BIC     R0, R0, #1 << IOC_VSync_Pulse
 STRB    R0, [R4, #IOC_IRQA_status >> 2]

 ._T0
 LDR     R3, IOC_ticks_flyback
 LDR     R1, T0_value			;update T0 to account for flyback
 TEQ     R1, #&10000000			;do we need to reload T0?
 LDREQ   R1, T0_latch			;YES, reload T0
 TEQEQ   R1, #&10000000
 SUBNE   R1, R1, R3			;NO, subtract flyback ticks
 STRNE   R1, T0_value			;update T0

 LDR     R1, T1_value			;update T1 to account for flyback
 TEQ     R1, #&10000000			;do we need to reload T1?
 LDREQ   R1, T1_latch			;YES, reload T0
 TEQEQ   R1, #&10000000
 SUBNE   R1, R1, R3			;NO, subtract flyback ticks
 STRNE   R1, T1_value			;update T1

 BL      IOC_timers			;check if timers triggered
LDMFD   R13!, {R0, PC}




;IOC_VFLYBK_high
;---------------
;The IR pin needs to go high during the flyback period
;used by Ballarena (Eterna) and Tactic (Eterna)

.IOC_VFLYBK_high
 STMFD   R13!, {R0, R14}
 ADR     R14, IOC_registers
 LDRB    R0, [R14, #IOC_CR >> 2]
 ORR     R0, R0, #IOC_IR_pin		;bring IR pin high
 STRB    R0, [R14, #IOC_CR >> 2]
LDMFD   R13!, {R0, PC}




;IOC_VFLYBK_low
;--------------
;The IR pin needs to go low during the display period

;MOVED TO _IOC_FLYBK in vectors.graphicsV




;go_T0
;-----
;reset the T0 timer value
;
;Exit:
; R4 - IOC_registers

.go_T0
 STMFD   R13!, {R1, R14}

 ADR     R4, IOC_registers
 LDRB    R1, [R4, #IOC_T0_latch_low >> 2]
 LDRB    R14, [R4, #IOC_T0_latch_high >> 2]
 ORRS    R1, R1, R14, LSL #8		;R1=T0 counter in 2us
 MOVEQ   R1, #&10000000			;if 0, mark for reload on next update
 STR     R1, T0_value			;store T0 value
 STR     R1, T0_latch
LDMFD   R13!, {R1, PC}




;go_T1
;-----
;reset the T1 timer value
;
;Exit:
; R4 - IOC_registers

.go_T1
 STMFD   R13!, {R1, R14}

 ADR     R4, IOC_registers
 LDRB    R1, [R4, #IOC_T1_latch_low >> 2]
 LDRB    R14, [R4, #IOC_T1_latch_high >> 2]
 ORRS    R1, R1, R14, LSL #8		;R1=T1 counter in 2us
 MOVEQ   R1, #&10000000			;if 0, mark for reload on next update
 STR     R1, T1_value			;store T1 value
 STR     R1, T1_latch
LDMFD   R13!, {R1, PC}




;IOC_timers
;----------
;decrements the IOC timers and triggers an IRQ when they cross zero
;
;Exit:
; R4-R6 - corrupted

ALIGN   32, 4
.IOC_timers
 LDR     R4, T0_value
 TEQ     R4, #&10000000			;is T0 set?
 BEQ     _T1				;NO

 LDR     R6, IOC_ticks_scanline		;R6=ticks per scanline
 SUBS    R4, R4, R6			;reduce T0 by scanline ticks
 STRPL   R4, T0_value			;T0 IRQ not triggered, update
 BPL     _T1				; |+ and check T1

 STR     R14, [R13, #-4]!
 ADR     R4, IOC_registers
 BL      go_T0

 LDRB    R14, [R4, #IOC_IRQA_mask >> 2]
 TST     R14, #1 << IOC_Timer_0		;is T0 IRQ active?
 LDREQ   R14, [R13], #4			;NO,
 BEQ     _T1				; |+ check T1

 STMFD   R13!, {R0-R6, R12}		;YES, need to call claimant code

 LDRB    R6, [R4, #IOC_IRQA_status >> 2]
 ORR     R6, R6, #1 << IOC_Timer_0	;flag IRQ as triggered
 STRB    R6, [R4, #IOC_IRQA_status >> 2]

 BL      check_JIT_and_taskID
 BNE     _not_JIT_task0
 BL      call_IRQv

 ADR     R4, IOC_registers
 LDRB    R2, [R4, #IOC_IRQA_status >> 2]
 TST     R2, #1 << IOC_Timer_0		;was the IRQ handled?
 BLNE    call_IRQ1V			;NO

 ADR     R3, DeviceVectors + (IOC_Timer_0 * device_table.size)
 LDR     R2, [R3, #device_table.entry_code]
 TEQ     R2, #0				;was OS_ClaimDeviceVector used?
 LDRNE   R12, [R3, #device_table.private] ;YES,
 MOVNE   R3, #IOC_addr			; |  R3 points to base of IOC
 BLNE    call_device_claimant		; |+ call claimant

 ._not_JIT_task0
 ADR     R4, IOC_registers		;clear T1 flag
 LDRB    R6, [R4, #IOC_IRQA_status >> 2]
 BIC     R6, R6, #1 << IOC_Timer_0
 STRB    R6, [R4, #IOC_IRQA_status >> 2]

 LDMFD   R13!, {R0-R6, R12, R14}

 ._T1
 LDR     R4, T1_value
 TEQ     R4, #&10000000			;is T1 set?
 MOVEQ   PC, R14			;NO, exit
 LDR     R6, IOC_ticks_scanline		;R6=ticks per scanline
 SUBS    R4, R4, R6			;reduce T1 by scanline ticks
 STRPL   R4, T1_value			;T1 IRQ not triggered, update
 MOVPL   PC, R14			; |+ and exit

 STR     R14, [R13, #-4]!
 ADR     R4, IOC_registers
 BL      go_T1

 LDRB    R14, [R4, #IOC_IRQA_mask >> 2]
 TST     R14, #1 << IOC_Timer_1		;is T1 IRQ active?
 LDREQ   PC, [R13], #4			;NO, exit

 STMFD   R13!, {R0-R6, R12}		;YES, need to call claimant code

 #if JIT_debug == 1 AND compile_GPU == 1
   ADR R5, jit_T1_ticks
   LDR R2, [R5]
   ADD R2, R2, #1			;increase T1 ticks
   STR R2, [R5]
 #endif

 LDRB    R6, [R4, #IOC_IRQA_status >> 2]
 ORR     R6, R6, #1 << IOC_Timer_1	;flag IRQ as triggered
 STRB    R6, [R4, #IOC_IRQA_status >> 2]

 BL      check_JIT_and_taskID
 BNE     _not_JIT_task1
 BL      call_IRQv

 ADR     R4, IOC_registers
 LDRB    R2, [R4, #IOC_IRQA_status >> 2]
 TST     R2, #1 << IOC_Timer_1		;was the IRQ handled?
 BLNE    call_IRQ1V			;NO

 ADR     R3, DeviceVectors + (IOC_Timer_1 * device_table.size)
 LDR     R2, [R3, #device_table.entry_code]
 TEQ     R2, #0				;was OS_ClaimDeviceVector used?
 LDRNE   R12, [R3, #device_table.private] ;YES,
 MOVNE   R3, #IOC_addr			; |  R3 points to base of IOC
 BLNE    call_device_claimant		; |+ call claimant

 ._not_JIT_task1
 ADR     R4, IOC_registers		;clear T1 flag
 LDRB    R6, [R4, #IOC_IRQA_status >> 2]
 BIC     R6, R6, #1 << IOC_Timer_1
 STRB    R6, [R4, #IOC_IRQA_status >> 2]

LDMFD   R13!, {R0-R6, R12, PC}
#endif




;IOC_trigger_device_9
;--------------------
;Calls game's IRQ device 9 handler
;
;Exit:
; registers preserved

#if Managed_ChannelHandler == 1 AND No_device_9_IRQ == 0
ALIGN   32, 4
.IOC_trigger_device_9
 STMFD   R13!, {R0-R6, R10, R12, R14}

 ADR     R4, IOC_registers
 LDRB    R1, [R4, #IOC_IRQB_status >> 2]
 ORR     R1, R1, #1 << (IOC_SSBC - 8)	;set SSBC flag
 STRB    R1, [R4, #IOC_IRQB_status >> 2]

 BL      check_JIT_and_taskID
 BLEQ    call_IRQv

 #if Managed_ChannelHandler == 1 AND No_device_9_IRQ == 0 AND JIT_WIMP_modules == 1
   LDR     R1, hypervised_sound_configure + sound_config_struct.taskID
   TEQ     R1, #0			;does a JIT task have the handler?
   BEQ     _not_JIT_task2
   BL      return_taskID		;R10=taskID
   TEQ     R1, R10			;is it this task?
   LDMNEFD R13!, {R0-R6, R10, R12, PC}	;NO, exit and leave SSBC IRQ triggered
   ._not_JIT_task2
 #endif

 ADR     R4, IOC_registers
 LDRB    R2, [R4, #IOC_IRQB_status >> 2]
 TST     R2, #1 << (IOC_SSBC - 8)	;was the IRQ handled?
 LDMEQFD R13!, {R0-R6, R10, R12, PC}	;YES

 BL      check_JIT_and_taskID
 BLEQ    call_IRQ1V

 ADR     R4, IOC_registers
 LDRB    R2, [R4, #IOC_IRQB_status >> 2]
 TST     R2, #1 << (IOC_SSBC - 8)	;was the IRQ handled?
 LDMEQFD R13!, {R0-R6, R10, R12, PC}	;YES

 ADR     R14, DeviceVectors + (IOC_SSBC * device_table.size)
 LDR     R2, [R14, #device_table.entry_code]
 TEQ     R2, #0				;is device claimed?
 MOVNE   R3, #IOC_addr			;R3 points to base of IOC
 LDRNE   R12, [R14, #device_table.private] ;R2 as passed to OS_ClaimDeviceVector
 BLNE    call_device_claimant		;YES

 ADR     R4, IOC_registers
 LDRB    R0, [R4, #IOC_IRQB_status >> 2]
 BIC     R0, R0, #1 << (IOC_SSBC - 8)	;clear SSBC flag
 STRB    R0, [R4, #IOC_IRQB_status >> 2]
LDMFD   R13!, {R0-R6, R10, R12, PC}
#endif




; exit:
; registers corrupt

.call_IRQv
 STR     R14, [R13, #-4]!
 LDR     R0, hypervised_IRQ
 MOV     R2, #page_zero_IRQ
 BL      read_page_zero_value_R2
 TEQ     R0, R2				;IRQv claimed?
 LDREQ   PC, [R13], #4			;NO

 MOV     R2, #jit_code_start
 ADD     R2, R2, #page_zero_IRQ		;branch to JIT space IRQ vector
 B       call_device_claimant+4		;skip the STR R14




; exit:
; registers corrupt

.call_IRQ1V
 STR     R14, [R13, #-4]!
 LDR     R3, hypervised_IRQ1V
 MOV     R2, #page_zero_IRQ1V
 BL      read_page_zero_value_R2
 TEQ     R2, R3				;was IRQ1V claimed?
 LDREQ   PC, [R13], #4			;NO

 MOV     R3, #IOC_addr			;R3 points to base of IOC
 MOV     R12, #0			;YES, R12=0 to match RO2
 ADD     R2, R2, #jit_code_start
 B       call_device_claimant+4		;skip the STR R14





;call_device_claimant
;--------------------
;call user IRQ handler in USER mode
;
;Entry:
; R2 - address to call
; R3 - base of IOC (if appropriate)
; R12 - 0 if calling IRQ1V
;
;Exit:
; R0-R6, R12 corrupted

.call_device_claimant
 STR     R14, [R13, #-4]!

 #if JIT_WIMP_modules == 1
   BL      check_JIT_and_taskID		;is the task paged in?
   LDRNE   PC, [R13], #4		;NO, exit
 #endif

 MRS     R5, CPSR			;R5=entry CPU mode
 BIC     R5, R5, #%1111 << 28
 MRS     R4, SPSR			;R4=SPSR to restore
 #if compile_RO == 400
   MSR     CPSR_c, #%10000011		;SVC26, IRQ disabled
 #endif
 #if compile_RO > 400
   MSR     CPSR_c, #%10010011		;SVC32, IRQ disabled
 #endif
 #if compile_IOMD == 1
   NOP
 #endif

 MRS     R6, SPSR
 STMFD   R13!, {R4, R5, R6, R14}
; SVC stack: <entry SPSR>, <entry CPSR>, SVC SPSR, SVC R14

 SUB     R13, R13, #2 * 4		;space for USR R13, R14
 #if compile_IOMD == 1
   STR    R0, [R13, #0]			;fix for errata on SA-110 rev.2
 #endif
 STMIA   R13, {R13-R14}^		;preserve USR R13, R14
; SVC stack: USR R13, USR R14, <entry SPSR>, <entry CPSR>, SVC SPSR, SVC R14
 #if compile_IOMD == 1
   NOP
 #endif
 BL      Allocate_Transient_Stack	;allocate a stack
 STMFD   R13!, {R0, R1}			;stack address and transient_stack_no
; SVC stack: <stack address>, <stack no>, USR R13, USR R14, <entry SPSR>, <entry CPSR>, SVC SPSR, SVC R14
 LDMIA   R13, {R13}^			;set USR stack
 #if compile_IOMD == 1
   NOP
 #endif
 ADD     R13, R13, #4
; SVC stack: <stack no>, USR R13, USR R14, <entry SPSR>, <entry CPSR>, SVC SPSR, SVC R14

 #if compile_RO > 400
   MSR     CPSR_c, #%10010000		;USR32 IRQ disabled
 #endif
 #if compile_RO == 400
   MSR     CPSR_c, #%10000000		;USR26 IRQ disabled
 #endif
 #if compile_IOMD == 1
   NOP
 #endif

 ADR     R14, _return
 STR     R14, [R13, #-4]!		;stack our return address
 LDR     R14, jit_managed_ldr_pc_r13	;hypervised LDR PC, [R13], #4
 ORR     R14, R14, #%10 << 26		;return in USER with IRQ disabled
 STR     R14, [R13, #-4]!		;stack IRQ return address
 LDR     R14, jit_managed_ldr_pc_r13_2	;hypervised LDR PC, [R13+4], #4
 ORR     R14, R14, #%10 << 26		;return in USER with IRQ disabled
 MOV     PC, R2				;call claimant

; SVC stack: <stack no>, USR R13, USR R14, <entry SPSR>, <entry CPSR>, SVC SPSR, SVC R14

 ._return
 MRS     R5, CPSR			;R5=exit CPSR
 SWI     XOS_EnterOS			;PRM1-141
 #if compile_RO > 400
   MSR     CPSR_c, #%10010011		;SVC32 IRQ disabled
 #endif
 #if compile_RO == 400
   MSR     CPSR_c, #%10000011		;SVC26 IRQ disabled
 #endif
 #if compile_IOMD == 1
   NOP
 #endif

 LDR     R0, [R13, #4 * 4]		;get entry mode from stack
 AND     R5, R5, #%1111 << 28
 ORR     R0, R0, R5			;copy exit flags
 STR     R0, [R13, #4 * 4]
 LDR     R0, [R13], #4			;stack no
 LDMIA   R13, {R13, R14}^		;preserve USR R13, R14
 #if compile_IOMD == 1
   NOP
 #endif
 ADD     R13, R13, #2 * 4
; SVC stack: <entry SPSR>, <entry CPSR>, SVC SPSR, SVC R14
 BL      Free_Transient_Stack

 LDMIA   R13!, {R0, R4, R5, R14}	;preserve SVC R14
 MSR     SPSR_all, R5			;preserve SVC SPSR
 MSR     CPSR_all, R4			;back into entry CPU mode
 #if compile_IOMD == 1
   NOP
 #endif
 MSR     SPSR_all, R0
LDR     PC, [R13], #4




;IOC_Econet
;----------
;Fake the RTFM Joystick interface

;Entry:
; R0 - faulting instruction
; R1 - address being accessed
; R6 - aborting registers (R0-PC)

;Exit:
; R0-R12 corrupt

.IOC_Econet
 AND     R7, R1, #%1111100		;R7=Econet register
 CMP     R7, #16			;register we can handle?
 BHS     AbortI_skip			;NO

 #if JIT_debug == 1
   ADR     R2, jit_joysticks		;PJSRA
   LDR     R1, [R2, #0]
   ORR     R1, R1, #%0010		;RTFM accessed
   STR     R1, [R2, #0]
 #endif
                 ;%100PUSWL		;STM instruction
 AND     R1, R0, #%11100001 << 20
 TEQ     R1,     #%10000001 << 20	;LDMxx Rx, {Rn...Rn} ?
 BEQ     _LDM				;YES
 TEQ     R1,     #%10000000 << 20	;STMxx Rx, {Rn...Rn} ?
 BNE     _not_STM			;NO
B       AbortI_skip			;ignore STM and exit back to user code



 ._LDM
					;R0 bits 15...0 contain registers
					;..written to IOC
 MOV     R5, R0, LSL #17		;reduce R5 to registers being written
 MOVS    R5, R5, LSR #18		;stripping off PC and leaving R0 in CS
 MOV     R1, #0
 ADR     R0, Econet_Registers
 ._LDM_loop
   LDRCS   R2, [R0, R7]			;read the value
   ADDCS   R7, R7, #4
   STRCS   R2, [R6, R1, LSR #12 - 2]	;update register

   ADD     R1, R1, #1 << 12		;go to next register
   MOVS    R5, R5, LSR #1		;are there any more registers?
 BNE     _LDM_loop			;loop until we've handled all registers
 BCS     _LDM_loop			;catch the last register in C
B       AbortI_skip			;exit back to user code


 ._not_STM
                 ;%01IPUBWL		;STR instruction
 AND     R1, R0, #%11000001 << 20
 TEQ     R1,     #%01000001 << 20	;LDR Rd, [Rn ...] ?
 BNE     AbortI_skip			;write, ignore

 ._LDR					;R0 contains instruction to decode
 AND     R1, R0, #%1111 << 12		;R1 = Rd << 12

 TST     R0, #1 << 22			;byte or word?
 ADR     R0, Econet_Registers
 LDR     R2, [R0, R7]			;read the value
 ANDNE   R2, R2, #&FF			;byte
 STR     R2, [R6, R1, LSR #12 - 2]	;R2 = value read from IOC
B       AbortI_skip			;exit back to user code




#if compile_RO > 400
;reset_IOC
;---------
;resets IOC to its default state

.reset_IOC
 STMFD   R13!, {R0-R2, R7, R14}

 MOV     R0, #0
 ADR     R7, IOC_registers		;initialise IOC registers
 MOV     R1, #&80 >> 2
 ._L1
   SUBS    R1, R1, #4
   STR     R0, [R7, R1]
 BNE     _L1

 MOV     R2, #IOC_IRQA_init		;initialise IOC IRQA
 STRB    R2, [R7, #IOC_IRQA_mask >> 2]
 MOV     R2, #IOC_IRQB_init		;initialise IOC IRQB
 STRB    R2, [R7, #IOC_IRQB_mask >> 2]
 MOV     R2, #IOC_CR_init		;initialise IOC CR
 STRB    R2, [R7, #IOC_CR >> 2]
 #if compile_GPU == 1
   ADR     R1, T0_value
   MOV     R2, #20000			;reset T0/T1 to default values
   STR     R2, [R1, #T0_latch - T0_value]
   STR     R2, [R1, #T0_value - T0_value]
   MOV     R2, #&10000000
   STR     R2, [R1, #T1_latch - T0_value]
   STR     R2, [R1, #T1_value - T0_value]
 #endif
LDMFD   R13!, {R0-R2, R7, PC}
#endif