;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

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

.page_zero_abort_OS
 LDR     R14, DAV_abort
 CMP     R14, #page_zero_Scratch_Space	;branch into Page Zero?
 MOVLO   R12, #&10			;ADFF5010
 BLO     report_unimplemented		;report it

 BIC     R9, R14, #%11			;R14 may be corrupted by PMP
 ADR     R8, DAV_r0			;R8=register stack
 MOV     R10, R1			;R10=aborting address
 LDR     R0, [R9], #4			;R0=faulting instruction
 STR     R9, [R8, #15 * 4]		;update PC

 ANDS    R1, R0, #%111 << 25		;SWP or LDREX/STREX on ARMv5+?
 BEQ     JIT_PZ_SWP			;YES
 CMP     R1,     #%100 << 25		;is it STM?
 BHI     JIT_PZ_STF			;NO, %110 : STF
 BNE     JIT_PZ_STR			;NO, %010 / %011 : STR/LDR

._STM
 AND     R4, R0, #%1111 << 16		;R4 = register
 LDR     R1, Early_Abort_Mode		;R1 = instruction Early Abort Mode
 LDR     R7, [R8, R4, LSR #16 - 2]	;R7 = Rd value (ie address)
 MOV     R12, R7			;R12 = unaligned Abort address
 BIC     R7, R7, #%11			;word align

 TST     R0, #1 << 20			;LDM?
 BNE     JIT_PZ_LDM			;YES

 TST     R1, #%01			;is LDM/STM in Late Abort mode?
 TSTNE   R0, #1 << 21			;YES, write-back?
 BEQ     _STM_early_abort_mode		;NO
					;need to fixup Rn
 MOV     R1, #&33			;11 instructions
 ORR     R1, R1, R1, LSL #8		;assumes R0-PC can't exist
 EOR     R2, R1, R1, LSL #1
 AND     R2, R2, R0, LSR #1
 SUB     R5, R0, R2
 AND     R2, R1, R5, LSR #2
 AND     R5, R5, R1
 ADD     R5, R5, R2
 ADD     R5, R5, R5, LSR #4
 ADD     R5, R5, R5, LSR #8
 AND     R2, R5, #%1111			;R2=<reglist> bit count

 TST     R0, #1 << 23			;Ix or Dx
 ADDEQ   R7, R7, R2, LSL #2		;Dx
 SUBNE   R7, R7, R2, LSL #2		;Ix
 ._STM_early_abort_mode
					;now need to perform:
					;STM/LDM<mode> R7,{<reglist>}
 TST     R0, #1 << 23			;Ix or Dx
 BNE     _STM_Ix

._STM_Dx
 TST     R0, #1 << 24			;DB or DA
 SUBNE   R7, R7, #4			;DB

 MOV     R6, #15 * 4
 MOVS    R1, R0, LSL #16
 BPL     _STM_Dx_L1_P1			;skip to 1st register
 ._STM_Dx_L1_P2
   LDR     R2, [R8, R6]			;load register value
   BL      translate_page_zero_write
   SUB     R7, R7, #4			;go to next address

   ._STM_Dx_L1_P1
     SUB     R6, R6, #4			;go to next register
     MOVS    R1, R1, LSL #1		;is it in reglist?
   BGT     _STM_Dx_L1_P1		;NO
 BMI     _STM_Dx_L1_P2			;YES

 TST     R0, #1 << 24			;DB or DA
 ADDNE   R7, R7, #4			;DB
 TST     R0, #1 << 21			;write-back?
 ANDNE   R12, R12, #%11			; |   get misalignment
 ORRNE   R7, R7, R12			; |   put back into Rd
 STRNE   R7, [R8, R4, LSR #16 - 2]	;YES
B       JIT_AbortI_decoded




._STM_Ix
 TST     R0, #1 << 24			;IB or IA
 ADDNE   R7, R7, #4			;IB

 MOV     R6, #0
 MOV     R1, R0, LSL #16
 MOVS    R1, R1, LSR #17
 ._STM_Ix_L1
   BCC     _STM_Ix_L1_P1
   ._STM_Ix_L1_P2
   LDR     R2, [R8, R6]			;load register value
   BL      translate_page_zero_write
   ADD     R7, R7, #4			;go to next address

   ._STM_Ix_L1_P1
   ADD     R6, R6, #4			;go to next register
   MOVS    R1, R1, LSR #1
 BNE     _STM_Ix_L1
 BCS     _STM_Ix_L1_P2

 TST     R0, #1 << 24			;IB or IA
 SUBNE   R7, R7, #4			;IB
 TST     R0, #1 << 21			;write-back?
 ANDNE   R12, R12, #%11			; |   get misalignment
 ORRNE   R7, R7, R12			; |   put back into Rd
 STRNE   R7, [R8, R4, LSR #16 - 2]	;YES
B       JIT_AbortI_decoded




.JIT_PZ_LDM
 TST     R1, #%01			;is LDM/STM in Late Abort mode?
 TSTNE   R0, #1 << 21			;YES, write-back?
 BEQ     _LDM_decoded			;NO

; MOV     R2, #0				;need to fixup Rn
; MOVS    R1, R0, LSL #17
; ._LDM_L1
;   ADC     R2, R2, R1, LSR #31
;   MOVS    R1, R1, LSL #2
; BNE     _LDM_L1
; ADC     R2, R2, R1, LSR #31
 MOV     R1, #&33			;11 instructions
 ORR     R1, R1, R1, LSL #8		;assumes R0-PC can't exist
 EOR     R2, R1, R1, LSL #1
 AND     R2, R2, R0, LSR #1
 SUB     R5, R0, R2
 AND     R2, R1, R5, LSR #2
 AND     R5, R5, R1
 ADD     R5, R5, R2
 ADD     R5, R5, R5, LSR #4
 ADD     R5, R5, R5, LSR #8
 AND     R2, R5, #%1111			;R2=<reglist> bit count

 TST     R0, #1 << 23			;Ix or Dx
 ADDEQ   R7, R7, R2, LSL #2		;Dx
 SUBNE   R7, R7, R2, LSL #2		;Ix

._LDM_decoded
					;now need to perform:
					;LDM<mode> R7,{<reglist>}
 TST     R0, #1 << 23			;Ix or Dx
 BNE     _LDM_Ix

._LDM_Dx
 TST     R0, #1 << 24			;DB or DA
 SUBNE   R7, R7, #4			;DB

 MOV     R6, #15 * 4
 MOVS    R1, R0, LSL #16
 ._LDM_Dx_L1
   BPL     _skip_Dx
   BL      translate_page_zero_read
   STR     R2, [R8, R6]			;store result
   SUB     R7, R7, #4			;go to next address
   ._skip_Dx
   SUB     R6, R6, #4			;go to next register
   MOVS    R1, R1, LSL #1
 BNE     _LDM_Dx_L1

 TST     R0, #1 << 24			;DB or DA
 ADDNE   R7, R7, #4			;DB
 TST     R0, #1 << 21			;write-back?
 ANDNE   R12, R12, #%11			;YES, get misalignment
 ORRNE   R7, R7, R12			; |   put back into Rd
 STRNE   R7, [R8, R4, LSR #16 - 2]	; |+  update register
B       JIT_AbortI_decoded


._LDM_Ix
 TST     R0, #1 << 24			;IB or IA
 ADDNE   R7, R7, #4			;IB

 MOV     R6, #0
 MOV     R1, R0, LSL #16
 MOVS    R1, R1, LSR #17
 ._LDM_Ix_L1
   BCC     _skip_Ix
   BL      translate_page_zero_read
   STR     R2, [R8, R6]			;store result
   ADD     R7, R7, #4			;go to next address
   ._skip_Ix
   ADD     R6, R6, #4			;go to next register
   MOVS    R1, R1, LSR #1
 BNE     _LDM_Ix_L1
 BCS     _LDM_Ix_L1

 TST     R0, #1 << 24			;IB or IA
 SUBNE   R7, R7, #4			;IB
 TST     R0, #1 << 21			;write-back?
 ANDNE   R12, R12, #%11			;YES, get misalignment
 ORRNE   R7, R7, R12			; |   put back into Rd
 STRNE   R7, [R8, R4, LSR #16 - 2]	; |+  update register
B       JIT_AbortI_decoded




.JIT_PZ_STR			;ARM710VE-36
 LDR     R7, abort_address
 AND     R1, R0, #%1111 << 12		;R1 = Rd
 TST     R0, #1 << 20			;LDR?
 BNE     JIT_PZ_LDR			;YES

 TST     R0, #1 << 22			;word or byte?
 LDREQ   R2, [R8, R1, LSR #12 - 2]	;word, load value being written
 LDRNEB  R2, [R8, R1, LSR #12 - 2]	;bytes, load value being written
 BL      translate_page_zero_write
.JIT_PZ_STR_memory_unchanged
					;--------- Now correct Rn ----------
                 ;%IPUBW
 AND     R1, R0, #%01001 << 21
 TEQ     R1,     #%01001 << 21		;STR/LDR Rd, [Rn, xx]! ?
 TEQNE   R1,     #%00000 << 21		;STR/LDR Rd, [Rn], xx?
 BNE     JIT_AbortI_decoded		;NO

 LDR     R1, Early_Abort_Mode		;R1 = instruction Early Abort Mode
 TST     R1, #%10			;is LDR/STR in Late Abort mode?
 BNE     JIT_AbortI_decoded		;NO

				;Reference: ARM710VE-36
 AND     R1, R0, #%1111 << 16		;R1 = Rn
 LDR     R12, [R8, R1, LSR #16 - 2]	;R12 = value of Rn

 TST     R0, #1 << 25			;STR Rd, [Rn, #{+/-}<immed_12>] ?
 BNE     _xTR_shift			;NO, STR Rd, [Rn, {+/-}Rm{,<shift>}]!

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

 ANDS    R7, R0, #%11 << 5		;R7 = shift type
 MOVEQ   R4, R2, LSL R6			;R4=Rm, LSL #<immed_5>
 BEQ     _xTR_shift_decoded
 CMP     R7, #%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
				;:STR 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
				;:STR Rd, [Rn, {+/-}Rm, RRX]!
 MRS     R4, SPSR			;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   R12, R12, R4			;Rm, <shift> #<immed_5>
 SUBEQ   R12, R12, R4			;-Rm, <shift> #<immed_5>
 STR     R12, [R8, R1, LSR #16 - 2]	;store it back
B       JIT_AbortI_decoded





.JIT_PZ_LDR				;R7=address R1=Rd
 BL      translate_page_zero_read
 STR     R2, [R8, R1, LSR #12 - 2]	;store in register
B       JIT_PZ_STR_memory_unchanged




.JIT_PZ_SWP				;SWP Rd, Rm, [Rn] or LDR/STREX
#if High_Vectors == 1 AND compile_RO == 500
.JIT_PZ_LDREX				;fall-thru to report
#endif
.JIT_PZ_STF				;fall-thru to report
B       JIT_Abort_report		;ADFF5009 unimplemented




;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
; R7 - address being read
; R8 - register dump
;
; R3, R5, R9-R11 free to use
;
;Exit:
;
; R2 - value
; R3, R5 - corrupted

ALIGN   32, 4
.translate_page_zero_read
 BICS    R5, R7, #%11			;word align and check for...
 #if report_null_dereference == 1
   MOVEQ   R12, #&1F			;Null dereference, ADFF501F
   BEQ     report_unimplemented_PC_plus_4
 #endif

 MOV     R9, R14			;PMP will corrupt R14
 #if High_Vectors == 1 AND ZeroPain == 1
   CMP     R5, #page_zero_IRQ1V		;is it a hardware vector
   BLS     _hardware_vector_read
 #endif
 #if High_Vectors == 1 AND ZeroPain == 0
   TEQ     R5, #page_zero_Undefined
   TEQNE   R5, #page_zero_IRQ
   TEQNE   R5, #page_zero_IRQ1V
   BEQ     _hardware_vector_read
 #endif

; TEQ     R5, #&105			;LatchBSoftCopy (Hamsters Mode120B)

 EORS    R3, R5, #page_zero_ScreenStart	;VDU screen start address (VDU var 148)
 BEQ     _VDU_screenstart

 EORS    R3, R5, #page_zero_VIDCControlCopy
 LDREQ   R2, VIDC1_CR
 MOVEQ   PC, R9

 ;**** these checks must be mirrored in Data_Abort_Vector
 LDR     R3, page_zero_UpCall		;could be HIGH
 SUB     R3, R7, R3
 CMP     R3, #&100			;UpCall variable
 MOVLO   R3, #0				;YES,
 BLO     _proxy_read			;proxy the read
 ;****

 #if High_Vectors == 1 AND ZeroPain == 0
   EORS    R2, R5, #page_zero_VertAdjust
   EORNES  R2, R5, #page_zero_HSWRSoftCopy
   EORNES  R2, R5, #page_zero_CursorFudgeFactor
   EORNES  R2, R5, #page_zero_RISCOSLibWord
   EORNES  R2, R5, #page_zero_CLibWord
   MOVNE   R12, #&11				;ADFF5011
   BNE     report_unimplemented_PC_plus_4	;report unknown address
 #endif
._hardware_vector_read
   BL      task_page_zero_R3		;redirect to our page zero
._proxy_read
   TST     R0, #1 << 22			;word or byte?
   LDRNEB  R2, [R3, R7]			;byte
   LDREQ   R2, [R3, R5]			;word R5 is word aligned
 MOV     PC, R9


._VDU_screenstart
 STMFD   R13!, {R0-R1, R3, R12, R14}
 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_c, R12
 #if compile_IOMD == 1
   NOP
 #endif
 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_c, R3			;switch back to ABT
 #if compile_IOMD == 1
   NOP
 #endif
 LDR     R2, [R13], #8
 LDMFD   R13!, {R0-R1, R3, R12, PC}




;translate_page_zero_write
;Entry:
; R0 - instruction
; R2 - value to write
; R7 - aborting address (word aligned if STM or STR)
; R8 - register dump
;
; R3, R5, R9-R11 free to use
;
;Exit:
; R2, R3, R5 - corrupted

ALIGN   32, 4
.translate_page_zero_write
 #if report_null_dereference == 1
   BICS    R5, R7, #%11			;;null dereference bug?
   MOVEQ   R12, #&1F			;YES, ADFF501F
   BEQ     report_unimplemented_PC_plus_4
 #endif

 MOV     R9, R14			;PMP will corrupt R14
 #if High_Vectors == 1 AND ZeroPain == 1
   CMP     R7, #page_zero_IRQ1V		;is it a hardware vector
   BLS     _hardware_vector_write	;FIQ routine or hardware vector
 #endif
 #if High_Vectors == 1 AND ZeroPain == 0
   TEQ     R7, #page_zero_Undefined
   TEQNE   R7, #page_zero_IRQ
   TEQNE   R7, #page_zero_IRQ1V
   BEQ     _hardware_vector_write
 #endif

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

 LDR     R3, page_zero_module_cmdline	;will be HIGH, so must come before
 CMP     R3, #0				;..scratch space check.  is it set?
 SUBNE   R3, R7, R3			;YES,
 CMPNE   R3, #&100			; |   Module commandline?
 BLO     _proxy_write			; |+  YES
 LOCK    R5

 #if High_Vectors == 1 AND ZeroPain == 0
   CMP     R7, #page_zero_Scratch_Space	;scratch space
   BHS     _proxy_write_JITspace

   EORS    R5, R7, #page_zero_RISCOSLibWord
   EORNES  R5, R7, #page_zero_CLibWord
   MOVNE   R12, #&15				;ADFF5015
   BNE     report_unimplemented_PC_plus_4	;report unknown address
 #endif
B       _proxy_write_JITspace


._proxy_write
 MOV     R3, #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_PC_plus_4	;YES, report it ADFF5016
 ; fall through to _proxy_write_JITspace


._proxy_write_JITspace
 BL      task_page_zero_R3		;R3=Page Zero pointer
._proxy_write_P1
 TST     R0, #1 << 22			;word or byte?
 STRNEB  R2, [R3, R7]			;byte, proxy the write
 BICEQ   R7, R7, #%11
 STREQ   R2, [R3, R7]			;word, proxy the write

 CMP     R7, #jit_appspace_end		;in JIT space?
 LDRLO   R2, branch_to_jit		;YES, Hypervisor entry instruction
 ADDLO   R3, R7, #jit_code_start	; |
 BICLO   R3, R3, #%11			; | align write
 STRLO   R2, [R3, #0]			; |+ reset JIT space
MOV   PC, R9


._illegal_vector_value
 MOV     R12, #1
 B       report_unimplemented_PC_plus_4


._IRQ1V
 CMP     R2, #jit_appspace_end		;valid address?
 BHS     _illegal_vector_value		;NO, ADFF5001

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