;page_zero_abort
;---------------
;An attempt was made to read/write to <&8000
;
;Entry:
; DAV_r0 - memory block, containing R0, R1 ... R14, PC
; R1 - aborting address
;
; CAUTION: R14 may be corrupt by PMP, so preserve around any LDR/STR
; R8-R12 cannot be used

.page_zero_abort
 #if High_Vectors == 0 AND JIT_debug == 1
   ADR     R7, jit_page_zero_aborts
   LDR     R0, [R7, #0]
   ADD     R0, R0, #1
   LDR     R6, DAV_abort
   STMIA   R7, {R0, R1, R6}
 #endif

.page_zero_abort_OS
 LDR     R0, DAV_abort			;the faulting address
 MOV     R7, R1				;R7=aborting address
 LDR     R0, [R0, #0]			;R0=faulting instruction
 ADR     R6, DAV_r0			;R6=register stack
                 ;%100PUSWL		;STM/LDM instruction
 AND     R1, R0, #%11100001 << 20
 #if High_Vectors == 0 AND ZeroPain == 0
   TEQ     R1,   #%10000001 << 20	;LDMxx Rx, {<reglist>} ?
   BEQ     _pass_to_ZeroPain		;YES
 #endif
 #if High_Vectors == 0 AND ZeroPain == 1
   TEQ     R1,   #%10000001 << 20	;LDMxx Rx, {<reglist>} ?
   BEQ     AbortI_skip			;YES, fail silently
 #endif
 TEQ     R1,     #%10000000 << 20	;STMxx Rx, {<reglist>} ?
 BNE     _not_STM			;YES

._STM
 BIC     R7, R7, #%11			;word align
 BIC     R0, R0, #1 << 22		;force words in translate_page_zero_write
					;R0 bits 15...0 contain registers
					;..written
 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
   LDRCS   R2, [R6]			;C set if register used
   BLCS    translate_page_zero_write	;|+   perform the translation
   ADD     R6, R6, #4			;go to next register
   ADD     R7, R7, #4			;go to next zero page location
   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


#if High_Vectors == 0 AND ZeroPain == 0
._pass_to_ZeroPain
 LDR     R7, [R6, #4 * 7]		;restore R7
 LDR     R6, [R6, #4 * 6]		;restore R6
 B       pass_Abort_to_OS - 4		;pass to ZeroPain
#endif


._not_STM
                 ;%01IPUBWL		;STR/LDR instruction
 AND     R1, R0, #%11000001 << 20
 TEQ     R1,   #%01000001 << 20		;LDR Rd, [Rn ...] ?
 #if High_Vectors == 0 AND ZeroPain == 0
   BEQ     _pass_to_ZeroPain		;YES
 #endif
 #if High_Vectors == 0 AND ZeroPain == 1
   BEQ     AbortI_skip			;YES, fail silently
 #endif
 TEQ     R1,     #%01000000 << 20	;STR Rd, [Rn ...] ?
 MOVNE   R12, #&15			;ADFF5015
 BNE     report_unimplemented		;either SWP or ADFFS bug

 ADR     R14, AbortI_skip		;exit back to user code
._STR					;R0 contains instruction to decode
 AND     R1, R0, #%1111 << 12		;R1=Rd << 12
 LDR     R2, [R6, R1, LSR #12 - 2]	;R0=value to write

 #if High_Vectors == 0 AND Unaligned_Support == 1
   TST     R0, #1 << 22			;word or byte?
   BICEQ   R7, R7, #%11			;word align
 #endif
 B       translate_page_zero_write	;perform the translation




;translate_page_zero_read
;------------------------
;translates reads in page zero to suitable values
;called from the JIT, so needs to exit back via i_instruction_handled

;Entry:
; R0 - original instruction
; R1 - Rd << 12
; R3 - register dump        (if Unaligned_Support == 0)
; R7 - address being read
;
; R2, R4, R6 free to use (if Unaligned_Support == 0)
; R2, R4     free to use (if Unaligned_Support == 1)
;
;Exit: (if Unaligned_Support == 1)
; R2 - value

ALIGN   32, 4
.translate_page_zero_read
 MOV     R1, R1, LSR #12		;shift Rd to lower bits

 LDR     R2, page_zero_EnvString
 SUB     R2, R7, R2
 CMP     R2, #&100			;EnvString?
 BLO     i_copy_instruction		;YES

 LDR     R2, page_zero_EnvTime
 SUB     R2, R7, R2
 CMP     R2, #&5			;EnvTime?
 BLS     i_copy_instruction		;YES

 STR     R14, [R13, #-4]!		;PMP will corrupt R14

 TEMP    R4
 BICS    R6, R7, #%11			;word align
 #if report_null_dereference == 1
   MOVEQ   R12, #&1F			;Null dereference, ADFF501F
   BEQ     report_unimplemented_PC_plus_4
 #endif
 CMP     R6, #page_zero_IRQ1V		;is it a hardware vector
 BLS     _hardware_vector_read

 TEQ     R6, #page_zero_ScreenStart	;VDU screen start address (VDU var 148)
 BEQ     _VDU_screenstart

 TEQ     R6, #page_zero_VIDCControlCopy
 LDREQ   R2, VIDC1_CR
 BEQ     _read_handled
 LOCK    R4

 #if High_Vectors == 0 AND ZeroPain == 0
   EORS    R2, R6, #page_zero_VertAdjust
   EORNES  R2, R6, #page_zero_HSWRSoftCopy
   EORNES  R2, R6, #page_zero_CursorFudgeFactor
   EORNES  R2, R6, #page_zero_RISCOSLibWord
   EORNES  R2, R6, #page_zero_CLibWord
   MOVNE   R12, #&11				;ADFF5011
   BNE     report_unimplemented_PC_plus_4	;report unknown address
 #endif
 
._hardware_vector_read
 BL      task_page_zero_R4		;redirect to our page zero
 TST     R0, #1 << 22			;word or byte?
 LDRNEB  R2, [R4, R7]			;byte
 LDREQ   R2, [R4, R6]
; fall-thru to _read_handled


 ;Entry:
 ; R0 - original instruction
 ; R1 - Rd
 ; R2 - value to return
 ; R7 - address being read
 ; R13 - stacked R0-R7
 ; have to switch CPU mode to alter R8-R14

._read_handled
 LDR     R5, [R13], #4			;preserve R14 from stack
 BL      abort_update_register		;update Rd
 MOV     R14, R5
					;update any post index before skip
 TST     R0, #1 << 21			;LDR Rd, [Rn, xx]! ?
 BNE     _write_back			;YES

 TST     R0, #1 << 24			;LDR Rd, [Rn], xx ?
 BNE     _exit				;NO, pre-indexed
					;YES, post-indexed
 AND     R1, R0, #%1111 << 16		;R1 = Rn
 MOV     R1, R1, LSR #8
					; ***** decode Operand 2 ******
 TST     R0, #1 << 25			;LDR Rd, [Rn, #{+/-}<immed_12>] ?
 BNE     _xTR_shift			;NO, LDR Rd, [Rn, {+/-}Rm{,<shift>}]

				;:LDR Rd, [Rn], #{+/-}<immed_12>
				;:LDR Rd, [Rn, #{+/-}<immed_12>]!
 MOV     R4, R0, LSL #31 - 11		;R4 = offset in bits 31..20
 TST     R0, #1 << 23			;U (Up / Down)
 SUBEQ   R7, R7, R4, LSR #31 - 11	;LDR Rd, [Rn, #-<immed_12>]
 ADDNE   R7, R7, R4, LSR #31 - 11	;LDR Rd, [Rn, #+<immed_12>]
 B       _write_back
				;Reference: ARM710VE-25
				;NOTE: STR Rd, [Rn, {+/-}Rm, <shift_type> Rx]!
				;      is not a valid instruction
				;:LDR Rd, [Rn], {+/-}Rm {, <shift> #<immed_5>}
 ._xTR_shift			;:LDR Rd, [Rn, {+/-}Rm {, <shift> #<immed_5>}]!
 ADR     R3, DAV_r0			;register dump
 AND     R5, R0, #%1111			;R5 = Rm
 LDR     R2, [R3, R5, LSL #2]		;R2 = value of Rm
 AND     R6, R0, #%11111 << 7
 MOV     R6, R6, LSR #7			;R6 = #<immed_5>

 ANDS    R5, R0, #%11 << 5		;R5 = shift type
 MOVEQ   R4, R2, LSL R6			;R4=Rm, LSL #<immed_5>
 BEQ     _xTR_shift_decoded
 CMP     R5, #%10 << 5
 MOVLT   R4, R2, LSR R6			;R4=Rm, LSR #<immed_5>
 MOVEQ   R4, R2, ASR R6			;R4=Rm, ASR #<immed_5>
 BLS     _xTR_shift_decoded
				;Reference: ARM710VE-25
				;:LDR Rd, [Rn, {+/-}Rm, ROR #<immed_5>/RRX]!
 TST     R0, #%11111 << 7		;is it RRX?
 MOVNE   R4, R2, ROR R6			;NO, Rm, ROR #n
 BNE     _xTR_shift_decoded
				;:LDR Rd, [Rn, {+/-}Rm, RRX]!
 LDR     R4, DAV_CPSR			;get CPSR
 MOVS    R4, R4, LSL #3			; |  shift carry into C
 MOV     R4, R2, RRX			; |+ Rm, RRX

 ._xTR_shift_decoded
 TST     R0, #1 << 23			;U (Up / Down)
 ADDNE   R7, R7, R4			;Rm, <shift> #<immed_5>
 SUBEQ   R7, R7, R4			;-Rm, <shift> #<immed_5>
					;fall-thru to _write_back

 ._write_back				;need to set Rn=R7
 AND     R1, R0, #%1111 << 16		;R1 = Rn

 MOV     R5, R14			;preserve R14
 MOV     R1, R1, LSR #8
 MOV     R2, R7				;R2 = value
 MOV     R7, #0				;R7 = no rotation
 BL      abort_update_register
 MOV     R14, R5			;preserve R14

 ._exit
 SUB     R2, R14, #4			;skip instruction
 STR     R2, [R13, #UND_exit_PC]	;update exit PC
B       i_instruction_handled_no_codelet




._VDU_screenstart
 STMFD   R13!, {R0-R1, R3, R12}
 MVN     R0, #0
 STR     R0, [R13, #-4]!
 MOV     R0, #148
 STR     R0, [R13, #-4]!
 MOV     R0, R13
 MOV     R1, R13
 MRS     R3, CPSR
 BIC     R12, R3, #%1111
 ORR     R12, R12, #%11			;switch to SVC
 MSR     CPSR_all, R12
 NOP
 SUB     R13, R13, #SWI_veneer.size
 MOV     R12, R13
 BL      hv_pre_SWI_veneer		;pass next SWI to our vector handler
 SWI     XOS_ReadVduVariables
 BL      dontexit_SWI_veneer
 ADD     R13, R13, #SWI_veneer.size
 MSR     CPSR_all, R3			;switch back to ABT
 #if High_Vectors == 0 AND compile_IOMD == 1
   NOP
 #endif
 LDR     R2, [R13], #8
 LDMFD   R13!, {R0-R1, R3, R12}
B       _read_handled




;abort_update_register
;---------------------
;Updates a register value

;Entry:
; R1 - register
; R2 - value to write
; R7 - rotational offset in lower two bits
;
;Exit:
; R1, R2, R3, R4 - corrupted

.abort_update_register
 ANDS    R3, R7, #%11			;get rotation offset
 MOVNE   R3, R3, LSL #3			; |    *8
 MOVNE   R2, R2, ROR R3			; |    shift value to bottom 8 bits

 TST     R0, #1 << 22			;word or byte?
 ANDNE   R2, R2, #&FF			;word, mask down to byte

 CMP     R1, #8				;is it a register we have stacked?
 STRLO   R2, [R13, R1, LSL #2]		;YES
 MOVLO   PC, R14			; +- and exit

 SUB     R1, R1, #8			;change R8 thru R14

 LDR     R3, DAV_CPSR			;get original CPU state
 TST     R3, #%1111			;is abort in User mode?
 BNE     _not_User			;NO

 STR     R2, _tmp			;YES, change user register
 ADR     R3, _tmp
 #if compile_IOMD == 1
   ADD     PC, PC, R1, LSL #4
 #endif
 #if compile_IOMD == 0
   ADD     PC, PC, R1, LSL #3
 #endif
 NOP
 MOV     R8, R2				;LDR R8, ...
 MOV     PC, R14
 #if compile_IOMD == 1
   NOP
   NOP
 #endif
 MOV     R9, R2				;LDR R9, ...
 MOV     PC, R14
 #if compile_IOMD == 1
   NOP
   NOP
 #endif
 MOV     R10, R2			;LDR R10, ...
 MOV     PC, R14
 #if compile_IOMD == 1
   NOP
   NOP
 #endif
 MOV     R11, R2			;LDR R11, ...
 MOV     PC, R14
 #if compile_IOMD == 1
   NOP
   NOP
 #endif
 MOV     R12, R2			;LDR R12, ...
 MOV     PC, R14
 #if compile_IOMD == 1
   NOP
   NOP
 #endif
 LDMIA   R3, {R13}^			;LDR R13, ...
 #if compile_IOMD == 1
   NOP					;fix for errata on ARM6/7 macrocells, StrongARM
 #endif
 MOV     PC, R14
 #if compile_IOMD == 1
   NOP
 #endif
 LDMIA   R3, {R14}^			;LDR R14, ...
 #if compile_IOMD == 1
   NOP					;fix for errata on ARM6/7 macrocells, StrongARM
 #endif
 MOV     PC, R14
 #if compile_IOMD == 1
   NOP
 #endif
 B       _report			;LDR PC, ... unsupported

 ._tmp DCD 0

 ._not_User
 MRS     R4, CPSR
 ORR     R3, R3, #%11 << 6		;disable IRQ/FIQ
 MSR     CPSR_c, R3			;switch to faulting mode
 #if High_Vectors == 0 AND compile_IOMD == 1
   NOP
 #endif
 ADD     PC, PC, R1, LSL #3		;change non-USER register
 NOP
 MOV     R8, R2				;LDR R8, ...
 B       _P2
 MOV     R9, R2				;LDR R9, ...
 B       _P2
 MOV     R10, R2			;LDR R10, ...
 B       _P2
 MOV     R11, R2			;LDR R11, ...
 B       _P2
 MOV     R12, R2			;LDR R12, ...
 B       _P2
 MOV     R13, R2			;LDR R13, ...
 B       _P2
 MOV     R14, R2			;LDR R14, ...
 B       _P2
 B       _report			;LDR PC, ... unsupported

 ._P2
 MSR     CPSR_all, R4			;switch back to UND32
 #if High_Vectors == 0 AND compile_IOMD == 1
   NOP
 #endif
MOV     PC, R14

 ._report
 MOV     R12, #9			;report unimplemented
B       report_unimplemented_PC_plus_8	;ADFF5009




;translate_page_zero_write
;Entry:
; R0 - instruction
; R2 - value to write
; R7 - aborting address (word aligned if STM or STR)
;
;Exit:
; R1-R4      - corrupt
; R0, R5-R7  - must be preserved
; R8-R14     - preserved

ALIGN   32, 4
.translate_page_zero_write
 MOV     R4, R14			;PMP will corrupt R14
 BICS    R3, R7, #%11			;R3=word aligned
 #if High_Vectors == 0 AND compile_RO == 400
   LDR     R1, DAV_abort		;the faulting instruction address
   CMP     R1, #jit_slotsize		;is it RISCOS?
   BHS     _proxy_write			;YES, proxy the write
 #endif

 #if report_null_dereference == 1
   MOVEQ   R12, #&1F			;Null dereference, ADFF501F
   BEQ     report_unimplemented_PC_plus_4
 #endif

 TEMP    R1
 CMP     R7, #page_zero_IRQ1V		;is it a hardware vector
 BLO     _hardware_vector_write
 BEQ     _IRQ1V

 LDR     R14, page_zero_EnvString	;could be HIGH, so must come before
 SUB     R14, R7, R14			;scratch space check
 CMP     R14, #&100			;EnvString?
 BLO     _proxy_write			;YES

 #if compile_RO > 400
   LDR     R14, page_zero_module_cmdline	;will be HIGH, so must come before
   CMP     R14, #0			;..scratch space check.  is it set?
   SUBNE   R14, R7, R14			;YES,
   CMPNE   R14, #&100			; |   Module commandline?
   BLO     _proxy_write			; |+  YES
 #endif

 CMP     R7, #page_zero_Scratch_Space	;scratch space
 BHS     _proxy_write_JITspace

 TEQ     R7, #&9B2			;VULAcopy (Oh No, More Lemmings!)
 MOVEQ   PC, R4				;ignore it
 LOCK    R1

 #if High_Vectors == 0 AND ZeroPain == 0
   EORS    R1, R7, #page_zero_RISCOSLibWord
   EORNES  R1, R7, #page_zero_CLibWord
   MOVNE   R12, #&15			;ADFF5015
   BNE     report_unimplemented		;report unknown address
 #endif
 #if High_Vectors == 0 AND ZeroPain == 1
MOV   PC, R4				;fail silently
 #endif
B       _proxy_write_JITspace


._proxy_write
 MOV     R1, #0
 B       _proxy_write_P1


._hardware_vector_write
 CMP     R7, #page_zero_Undefined
 CMPNE   R7, #page_zero_IRQ		;hardware vector?
 MOVLO   R12, #&16
 BLO     report_unimplemented		;YES, report it ADFF5016
 ; fall through to _proxy_write


._proxy_write_JITspace
 BL      task_page_zero_R1		;redirect to our page zero
._proxy_write_P1
 TST     R0, #1 << 22			;word or byte?
 STRNEB  R2, [R1, R7]			;byte, proxy the write
 STREQ   R2, [R1, R3]			;word, proxy the write

 CMP     R7, #jit_appspace_end		;in JIT space?
 LDRLO   R2, branch_to_jit		;YES, Hypervisor entry instruction
 MOVLO   R1, #jit_code_start
 STRLO   R2, [R1, R3]			; |+ reset JIT space
MOV   PC, R4




._IRQ1V
 CMP     R2, #jit_appspace_end		;is the address in appspace?
 MOVHS   R12, #1
 BHS     report_unimplemented_PC_plus_4	;NO, ADFF5001

 BL      task_page_zero_R3		;R3=Page Zero pointer
 STR     R2, [R3, #page_zero_IRQ1V]	;our hypervisor will handle it
 MOV     PC, R4
