STRUCT EnvHandler
{
  .entry		DCD 0
  .r12_value		DCD 0
  .buffer		DCD 0
  .user_entry		DCD 0
  .user_r12_value	DCD 0
  .user_buffer		DCD 0
  .our_managed_entry	DCD 0
  DCD 0
  .size
}




;claim_ChangeEnvironmentV
;------------------------
;claims handlers the JIT needs to Hypervise and tracks changes by other
;software

.claim_ChangeEnvironmentV
 STMFD   R13!, {R0-R3, R5-R6, R14}

 LDR     R6, managed_EnvironmentHandlers	;managed entry to previous
 LDR     R5, environment_handlers	;store current handlers

 MOV     R0, #6				;Error
 ADR     R1, our_env_handler6_entry
 LDR     R3, env_handler_buffer
 BL      _claim

 MOV     R0, #7				;CallBack
 ADR     R1, our_env_handler7_entry
 LDR     R3, env_handler_registers
 BL      _claim

 MOV     R0, #8				;Breakpoint
 ADR     R1, our_env_handler8_entry
 LDR     R3, env_handler_registers
 BL      _claim

 MOV     R0, #9				;Escape
 ADR     R1, our_env_handler9_entry
 BL      _claim

 MOV     R0, #10			;Event
 ADR     R1, our_env_handler10_entry
 BL      _claim

 MOV     R0, #11			;Exit
 ADR     R1, our_env_handler11_entry
 BL      _claim

 MOV     R0, #12			;Unused SWI
 ADR     R1, our_env_handler12_entry
 BL      _claim

 MOV     R0, #16			;UpCall
 ADR     R1, our_env_handler16_entry
 BL      _claim
LDMFD   R13!, {R0-R3, R5-R6, PC}

 ._claim
 STMFD   R13!, {R1, R3, R14}

 MOV     R1, #0
 MOV     R2, #0
 MOV     R3, #0
 SWI     XOS_ChangeEnvironment
 LDMFD   R13!, {R2, R3}
 TEQ     R2, R1				;do we have it?
 BEQ     _already_claimed		;YES
 MOV     R1, R2
 MOV     R2, #0
 SWI     XOS_ChangeEnvironment		;PRM1-320
 STMIA   R5, {R1, R2, R3}		;store existing values
 STR     R6, [R5, #EnvHandler.our_managed_entry]
 STR     R1, [R5, #EnvHandler.entry]
 STR     R2, [R5, #EnvHandler.r12_value]
 STR     R2, [R5, #EnvHandler.user_r12_value]
 STR     R3, [R5, #EnvHandler.user_buffer]
 STR     R3, [R5, #EnvHandler.buffer]
 MOV     R1, #0
 STR     R1, [R5, #EnvHandler.user_entry]	;unclaimed initially

 ._already_claimed
 ADD     R5, R5, #EnvHandler.size
 ADD     R6, R6, #4			;next managed previous handler entry
 LDR     PC, [R13], #4




;release_ChangeEnvironmentV
;--------------------------
;releases all environment handlers

.release_ChangeEnvironmentV
 STMFD   R13!, {R0-R5, R14}

 LDR     R5, environment_handlers
 MOV     R4, #0
 MOV     R0, #6
 ._L1
   LDR     R1, [R5, #EnvHandler.our_managed_entry]
   TEQ     R1, #0			;is it claimed?
   BEQ     _skip			;NO
   LDMIA   R5, {R1-R3}
   SWI     XOS_ChangeEnvironment	;PRM1-320
   STR     R4, [R5, #EnvHandler.our_managed_entry]

   ._skip
   ADD     R5, R5, #EnvHandler.size
   ADD     R0, R0, #1
   TEQ     R0, #13			;skip 13..15
   MOVEQ   R0, #16
   TEQ     R0, #17
 BNE     _L1
LDMFD   R13!, {R0-R5, PC}




;reset_ChangeEnvironmentV
;--------------------------
;resets all environment handlers

.reset_ChangeEnvironmentV
 STMFD   R13!, {R0-R5, R14}

 LDR     R5, environment_handlers
 MOV     R1, #0
 MOV     R0, #6
 ._L1
   STR     R1, [R5, #EnvHandler.our_managed_entry]
   ADD     R5, R5, #EnvHandler.size
   ADD     R0, R0, #1
   TEQ     R0, #13			;skip 13..15
   MOVEQ   R0, #16
   TEQ     R0, #17
 BNE     _L1
LDMFD   R13!, {R0-R5, PC}




;change_environment_handler
;--------------------------
;tracks user environment handlers
;
;Entry:
; R0 - handler #
; R1 - handler address
; R2 - value of R12
; R3 - buffer address (optional)

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

 CMP     R0, #17
 ADDLO   PC, PC, R0, LSL #2
 B       _report_handler
 B       _env_handler0			;0 - Memory limit
 B       _read_only			;1 - Undefined instruction (used in Overload Paradise)
 B       _read_only			;2 - Prefetch abort
 B       _read_only			;3 - Data abort
 B       _read_only			;4 - Address exception
 B       _read_only			;5 - Other exceptions
#if JIT_prevent_Exit_Handlers == 0 AND JIT_prevent_Error_Handler == 0 AND compile_RO > 311
 B       _ok				;6 - Error (used in Chequered Flag)
#endif
#if (JIT_prevent_Exit_Handlers <> 0 OR JIT_prevent_Error_Handler <> 0) AND compile_RO > 311
 B       _read_only			;6 - Error
#endif
 B       _ok				;7 - CallBack (used in Chequered Flag)
 B       _read_only			;8 - BreakPoint
 B       _ok				;9 - Escape
 B       _ok				;10 - Event (used in Ballarena)
#if JIT_prevent_Exit_Handlers == 0 AND compile_RO > 311
 B       _ok				;11 - Exit (used in Gribblys Day Out)
#endif
#if JIT_prevent_Exit_Handlers <> 0 AND compile_RO > 311
 B       _read_only			;11 - Exit
#endif
 B       _ok				;12 - Unused SWI (used in Overload Paradise)
 B       _read_only			;13 - Exception registers (used in Overload Paradise)
 B       _env_handler14			;14 - Application space
 B       _read_only			;15 - Currently active object
; B       _ok				;16 - Upcall (used by MiG-29 Fulcrum)
 ._ok
 BL      pre_SWI_veneer			;call SWI that follows
 SWI     XADFFS_DoNothing		;switch to SVC
 BL      claim_ChangeEnvironmentV	;ensure we've claimed environment handlers
 BL      dontexit_SWI_veneer

 STMFD   R13!, {R0-R3, R4-R5}
 CMP     R0, #16			;only interested in 6..12 / 16
 MOVEQ   R0, #7
 SUBLO   R0, R0, #6
 LDR     R4, environment_handlers
 ADD     R4, R4, R0, LSL #5		;*32
 LDR     R5, [R4, #EnvHandler.our_managed_entry]	;our RISCOS managed entry
 LDR     R14, [R4, #EnvHandler.user_entry]
 TEQ     R14, #0			;is the handler claimed?
 STRNE   R14, [R13, #4]			;YES, return existing claim
 STREQ   R5, [R13, #4]			;NO, overwrite R1 on stack
 TEQ     R1, #0				;reading?
 BEQ     _read_R1
 CMP     R1, #jit_appspace_end		;are we pointing at JIT space?
 ADDLO   R1, R1, #jit_code_start	;YES, correct address
 MOVHS   R1, #0				;NO, free the handler
 STR     R1, [R4, #EnvHandler.user_entry]
 ._read_R1
 LDR     R5, [R4, #EnvHandler.user_r12_value]
 STR     R5, [R13, #8]			;overwrite R2 on stack
 TEQ     R2, #0				;reading?
 STRNE   R2, [R4, #EnvHandler.user_r12_value]	;NO

 LDR     R0, [R13], #4			;pull R0 from stack
 TEQ     R0, #6				;is there a buffer?
 TEQNE   R0, #7
 TEQNE   R0, #8
 LDMNEFD R13!, {R1-R3, R4-R5, PC}	;NO
 LDR     R5, [R4, #EnvHandler.user_buffer]
 STR     R5, [R13, #8]			;overwrite R3 on stack
 TEQ     R3, #0				;reading?
 STRNE   R3, [R4, #EnvHandler.user_buffer]	;NO
LDMFD   R13!, {R1-R3, R4-R5, PC}


 ._report_handler
 MOV     R12, #&F			;ADFF500F
B       hv_report_unimplemented		;report...yet to be coded


 ._read_only
 MOV     R1, #0				;read the handler, dont change it
 TEQ     R0, #6
 TEQNE   R0, #7
 TEQNE   R0, #8
 MOVEQ   R3, #0				;6..8 need R3 to be read only
 TEQNE   R0, #11
 MOVEQ   R2, #0				;6..12 / 16 need R2 read only
 BL      pre_SWI_veneer			;call SWI that follows
 SWI     XOS_ChangeEnvironment
 BL      post_SWI_veneer
 BL      dontexit_SWI_veneer
LDR     PC, [R13], #4



 ._env_handler0
 TEMP    R0
 TEQ     R1, #0				;read or set?
 SUBNE   R1, R1, #&8000			;set, correct the value
 STRNE   R1, pre_JIT_wimp_slot_size	; |+  update slot size
 LOCK    R0
 MOV     R0, #0
 ._env_handler14
 LDR     R1, pre_JIT_wimp_slot_size
 ADD     R1, R1, #&8000			;correct the value
LDR     PC, [R13], #4




.our_env_handler6_entry			;Error, entered in USR
 CMP     R13, #&8000			;is the stack valid?
 MOVLO   R13, #jit_allocate_memory	;NO, assume no stack
 CMP     R13, #jit_allocate_memory	;is the stack valid?
 MOVHI   R13, #jit_allocate_memory	;NO, assume no stack
 STMFD   R13!, {R1-R2, R4, R12, R14}
 LDR     R4, environment_handlers + (EnvHandler.size * 0)
 LDR     R1, [R4, #EnvHandler.user_entry]
 TEQ     R1, #0				;is it claimed?
 LDMEQFD R13!, {R1-R2, R4, R12, R14}	;NO,
 BEQ     previous_env_handler6		; |+ pass to previous
 STR     R1, [R13, #3 * 4]		;overwrite R12 on stack

 SWI     XOS_EnterOS			;this bit needs elevating
 LDR     R0, env_handler_buffer
 LDR     R1, [R4, #EnvHandler.user_buffer]
 LDR     R2, [R0], #4			;PC error occurred at
 STR     R2, [R1], #4
 LDR     R2, [R0], #4			;error number
 STR     R2, [R1], #4
 ._strcpy
   LDRB    R2, [R0], #1
   STRB    R2, [R1], #1			;copy one char
   TEQ     R2, #0			;until terminator hit
 BNE     _strcpy
 SWI     XOS_LeaveOS

 LDR     R14, jit_managed_ldr_pc_r13	;change to return into appspace
 MRS     R1, CPSR
 AND     R0, R1, #&F0000003		;flags and CPU mode
 AND     R1, R1, #%11 << 6
 ORR     R14, R14, R0
 ORR     R14, R14, R1, LSL #20		;FIQ/IRQ

 LDR     R0, [R4, #EnvHandler.user_r12_value]
 LDMFD   R13!, {R1-R2, R4, PC}		;call handler


.previous_env_handler6			;Error, entered in USR
 SWI     XOS_EnterOS
 BL      release_ChangeEnvironmentV
 SWI     XOS_LeaveOS
 LDR     R0, env_handler_buffer
 ADD     R0, R0, #4
 SWI     OS_GenerateError		;pass error to RISCOS




.our_env_handler7_entry			;CallBack
 STMFD   R13!, {R4, R12, R14}		;stack return
 MOV     R4, #1
 ADR     R12, previous_env_handler7
 B       call_user_env_handler


.previous_env_handler7			;CallBack
 STMFD   R13!, {R0, R4, R14}
 MOV     R4, #1


;Entry:
; R4 - our internal handler number
; stack - R0, R4, R14

.previous_env_handler_entry
 TEQ     R4, #1
 TEQNE   R4, #2				;these need buffers copying
 LDR     R0, environment_handlers
 ADD     R4, R0, R4, LSL #5		;*32
 LDR     R0, [R4, #EnvHandler.entry]	;previous entry
 STR     R0, [R13, #2 * 4]		;write PC to stack
 LDR     R12, [R4, #EnvHandler.buffer]
 LDMNEFD R13!, {R0, R4, PC}		;pass on
 LDMFD   R13!, {R0, R4}
 STMFD   R13!, {R0-R2, R4, R14}
 LDR     R0, env_handler_registers

 MOV     R1, R12			;user buffer
 #if compile_RO == 400
   MOV     R2, #16 * 4
 #endif
 #if compile_RO > 400
   MOV     R2, #17 * 4
 #endif
 BL      MemCopy			;copy buffer
 LDMFD   R13!, {R0-R2, R4, R14, PC}	;pass on




.our_env_handler8_entry			;BreakPoint
 STMFD   R13!, {R4, R12, R14}		;stack return
 MOV     R4, #2
 ADR     R12, previous_env_handler8
 B       call_user_env_handler


.previous_env_handler8			;BreakPoint
 STMFD   R13!, {R0, R4, R14}
 MOV     R4, #2
 B       previous_env_handler_entry




.our_env_handler9_entry			;Escape
 STMFD   R13!, {R4, R12, R14}		;stack return
 MOV     R4, #3
 ADR     R12, previous_env_handler9
 B       call_user_env_handler


.previous_env_handler9			;Escape
 STMFD   R13!, {R0, R4, R14}
 MOV     R4, #3
 B       previous_env_handler_entry




.our_env_handler10_entry		;Event
 STMFD   R13!, {R4, R12, R14}		;stack return
 TEQ     R0, #11			;key event?
 LDREQ   R14, JIT_event11_active	;YES,
 TEQEQ   R14, #0			; |   did the client enable it
 LDMEQFD R13!, {R4, R12, R14}		; |   NO
 BEQ     previous_env_handler10		; |+  pass the call on
 MOV     R4, #4
 ADR     R12, previous_env_handler10
 B       call_user_env_handler


.previous_env_handler10			;Event
 STMFD   R13!, {R0, R4, R14}
 MOV     R4, #4
 B       previous_env_handler_entry




.our_env_handler11_entry		;Exit, entered in USR
 CMP     R13, #&8000			;is the stack valid?
 MOVLO   R13, #jit_allocate_memory	;NO, assume no stack
 CMP     R13, #jit_allocate_memory	;is the stack valid?
 MOVHI   R13, #jit_allocate_memory	;NO, assume no stack
 STMFD   R13!, {R4, R12, R14}		;stack return
 MOV     R4, #5
 ADR     R12, previous_env_handler11
 B       call_user_env_handler


.previous_env_handler11			;Exit, entered in USR
 SWI     XOS_EnterOS
 BL      release_ChangeEnvironmentV
 SWI     XOS_LeaveOS
 SWI     OS_Exit



.our_env_handler12_entry		;Unused SWI
 STMFD   R13!, {R4, R12, R14}		;stack return
 MOV     R4, #6
 ADR     R12, previous_env_handler12
 B       call_user_env_handler


.previous_env_handler12			;Unused SWI
 STMFD   R13!, {R0, R4, R14}
 MOV     R4, #6
 B       previous_env_handler_entry




.our_env_handler16_entry		;UpCall
 STMFD   R13!, {R4, R12, R14}		;stack return
 MOV     R4, #7
 ADR     R12, previous_env_handler16
 B       call_user_env_handler
 

.previous_env_handler16			;UpCall
 STMFD   R13!, {R0, R4, R14}
 MOV     R4, #7
 B       previous_env_handler_entry



;Entry stack: R4, R12, R14	- R12 needs overwriting with entry point
; R4  - our Environment handler number
;	0 Error  (has its own handler above)
;	1 CallBack
;	2 BreakPoint
;	3 Escape
;	4 Event
;	5 Exit
;	6 Unused SWI
;	7 UpCall
; R12 - pointer to code to pass to previous handler

.call_user_env_handler
 LDR     R14, environment_handlers
 ADD     R4, R14, R4, LSL #5		;*32
 LDR     R14, [R4, #EnvHandler.user_entry]
 TEQ     R14, #0			;is it claimed?
 LDREQ   R4, [R13], #4			;NO, pull R4
 ADDEQ   R13, R13, #4			; |  drop R12
 LDREQ   R14, [R13], #4			; |  pull R14
 MOVEQ   PC, R12			; |+ pass to previous
 STR     R14, [R13, #4]			;overwrite R12 on stack

 LDR     R14, environment_handlers	;check for CallBack and Breakpoint
 ADD     R14, R14, #(EnvHandler.size * 3)
 CMP     R4, R14			;1/2 need register buffer copying
 BHS     _P1				;not CallBack or Breakpoint
 STMFD   R13!, {R0-R2}			;CallBack from USER with IRQ/FIQ clear
 LDR     R0, env_handler_registers
 LDR     R1, [R4, #EnvHandler.user_buffer]
 LDR     R2, [R0, #13 * 4]		;get USER R13
 LDR     R14, [R0, #15 * 4]
 STR     R14, [R2, #-4]!		;stack actual return in USER stack
 STR     R2, [R0, #13 * 4]		;update USER R13 in buffer
 ADD     R2, R0, #13 * 4
 LDMIA   R2, {R13}^			;update actual USER R13
					;next instruction can't touch banked reg
					;..fix for errata on ARM6/7 macrocells, StrongARM
 #if compile_RO == 400
   LDR     R2, [R0, #15 * 4]		;26bit, PSR is in PC
 #endif
 #if compile_RO > 400
   LDR     R2, [R0, #16 * 4]		;32bit, PSR is seperate
 #endif
 LDR     R14, jit_managed_ldr_pc_r13
 AND     R2, R2, #&F0000000		;flags, can ignore IRQ/FIQ as they must
 ORR     R14, R14, R2			;..be clear for CallBack to be entered
 STR     R14, [R0, #15 * 4]		;stack new return

 MOV     R2, #16 * 4
 BL      MemCopy			;copy registers
 LDMFD   R13!, {R0-R2}

 ._P1
 STR     R0, [R13, #-4]!		;preserve R0
 LDR     R14, jit_managed_ldr_pc_r13	;change to return into appspace
 MRS     R12, CPSR
 AND     R0, R12, #&F0000003		;flags and CPU mode
 AND     R12, R12, #%11 << 6
 ORR     R14, R14, R0
 ORR     R14, R14, R12, LSL #20		;FIQ/IRQ
 LDR     R12, [R4, #EnvHandler.user_r12_value]
 LDMFD   R13!, {R0, R4, PC}		;call handler, return R14 on stack
