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
  .active		DCD 0
  .size
}




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

;Needs to handle Error, Exit, Escape and Event
;Entry:
; R0 - address in JIT space to put 26bit code handlers (full desending)

.claim_ChangeEnvironmentV
 STMFD   R13!, {R0-R4, R14}
 LDR     R4, environment_handlers
 ADD     R4, R4, #EnvHandler.user_entry
 MOV     R0, #0
 ._L1
   MOV     R1, #0
   MOV     R2, #0
   MOV     R3, #0
   MOV     R14, #0
   STMIA   R4, {R1-R3, R14}
   SWI     XOS_ChangeEnvironment
   STMDB   R4, {R1-R3}
   ADD     R4, R4, #EnvHandler.size
   ADD     R0, R0, #1
   TEQ     R0, #17
 BNE     _L1
LDMFD   R13!, {R0-R4, PC}




;release_all_ChangeEnvironmentV
;------------------------------
;releases all environment handlers

.release_all_ChangeEnvironmentV
 STMFD   R13!, {R0-R5, R14}
 LDR     R4, environment_handlers
 ADD     R4, R4, #(EnvHandler.size * 6) + EnvHandler.user_entry
 MOV     R5, #0
 MOV     R0, #6
 ._L1
   LDMDB   R4, {R1-R3}
   LDR     R14, [R4, #EnvHandler.active - EnvHandler.user_entry]
   TEQ     R14, #0							;was it claimed?
   STRNE   R5, [R4, #EnvHandler.active - EnvHandler.user_entry]	;YES
   SWINE   XOS_ChangeEnvironment
   ADD     R4, R4, #EnvHandler.size
   ADD     R0, R0, #1
   TEQ     R0, #13
   MOVEQ   R0, #16
   ADDEQ   R4, R4, #EnvHandler.size * (16-13)
   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       _hardware_vector		;1 - Undefined instruction (used in Overload Paradise / K.V.)
 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, 3D Construction Kit)
 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)


 ._report_handler
 ADD     R13, R13, #4			;clear stack
 MOV     R12, #&F			;ADFF500F
B       hv_report_unimplemented		;report


 ._ok
 STMFD   R13!, {R4}
 LDR     R4, environment_handlers
 MOV     R14, #EnvHandler.size
 MLA     R14, R0, R14, R4
 ADD     R14, R14, #EnvHandler.user_entry
 STMIA   R14, {R1-R3, R14}		;store values for error reporting
 LDMFD   R13!, {R4}

 BL      pre_SWI_veneer			;call SWI that follows
 SWI     XADFFS_DoNothing
 BL      ChangeEnvironmentV_handler
 BL      dontexit_SWI_veneer
LDR     PC, [R13], #4

 ._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      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

 ._hardware_vector
 TEQ     R0, #1				;Undefined vector?
 BNE     _read_only
 BL      task_page_zero_R2
 TEQ     R1, #0				;read
 LDR     R14, [R2, #page_zero_hardware_vectors + page_zero_Undefined]
 STRNE   R1, [R2, #page_zero_hardware_vectors + page_zero_Undefined]
 MOV     R1, R14
LDR     PC, [R13], #4




.ChangeEnvironmentV_handler
 STMFD   R13!, {R4-R6, R14}
 TEQ     R1, #0				;read?
 BEQ     _read

 MOV     R5, #0				;check if we're pointing to a codelet
 SUB     R5, R5, #jit_slotsize - jit_code_start
 CMP     R1, R5
 ADDHI   R1, R1, #jit_code_start	;we are, need to correct the address

 CMP     R1, #jit_appspace_end		;is it within appspace?
 BLO     _needs_pre_veneer		;YES
 CMP     R1, #jit_code_start		;is it a codelet?
 BLO     _remove_handler		;YES

._pass_on
 SWI     XOS_ChangeEnvironment
LDMFD   R13!, {R4-R6, PC}


._read
 SWI     XOS_ChangeEnvironment
 CMP     R1, #jit_appspace_end		;is it within appspace?
 LDMLOFD R13!, {R4-R6, PC}		;YES
 CMP     R1, #jit_code_start		;is it a codelet?
 LDMHIFD R13!, {R4-R6, PC}		;NO

 LDR     R5, [R1, #_verify - _pre_veneer]
 LDR     R4, _verify
 TEQ     R5, R4				;is R1 pointing at a veneer?
 LDREQ   R1, [R1, #_entry - _pre_veneer]	;YES,
 SUBEQ   R1, R1, #jit_code_start	; |+ return the correct address
LDMFD   R13!, {R4-R6, PC}



._remove_handler
 LDR     R5, [R1, #_verify - _previous]
 LDR     R4, _verify
 TEQ     R5, R4				;is R1 pointing at _previous?
 BNE     _pass_on			;NO, pass on

 STMFD   R13!, {R2}
 LDR     R2, environment_handlers
 MOV     R14, #EnvHandler.size
 MLA     R14, R0, R14, R2

 LDR     R1, [R1, #_previous_handler - _previous]	;get previous handler address
 LDR     R2, [R14, #EnvHandler.entry]
 TEQ     R1, R2				;last handler in chain?
 MOVEQ   R2, #0				;YES, mark as "not active"
 STREQ   R2, [R14, #EnvHandler.active]
 STREQ   R2, [R14, #EnvHandler.user_entry]
 LDMFD   R13!, {R2}
 B       _pass_on			;pass on


._needs_pre_veneer
 STMFD   R13!, {R0-R3, R7}
 MOV     R7, #0				;immortal - it will remove itself
 MOV     R1, #0				;can't be reused
 MOV     R6, #_handler_end - _pre_veneer
 ADR     R0, _pre_veneer
 BL      allocate_codelet
 MOV     R1, R6
 MOV     R2, #_handler_end - _pre_veneer
 BL      MemCopy			;copy handler into codelet

 LDR     R0, [R13], #4
 MOV     R1, #0
 MOV     R2, #0
 MOV     R3, #0
 SWI     XOS_ChangeEnvironment		;get previous handler address
 STR     R1, [R6, #_previous_handler - _pre_veneer]
 LDMFD   R13!, {R1-R3, R7}

 ADD     R1, R1, #jit_code_start
 STR     R1, [R6, #_entry - _pre_veneer]

 ADR     R1, free_environment_handler_codelet
 STR     R1, [R6, #_free_codelet - _pre_veneer]
 STR     R0, [R6, #_handler_no - _pre_veneer]	;handler no for later
 TEQ     R0, #7					;callback?
 STREQ   R3, [R6, #_regdump_original - _pre_veneer]
 ADDEQ   R3, R6, #_regdump - _pre_veneer	;point regdump at veneer
 MOV     R1, R6
 SWI     XOS_ChangeEnvironment		;claim
 ADD     R1, R6, #_previous - _pre_veneer
 SUB     R1, R1, #jit_code_start
LDMFD   R13!, {R4-R6, PC}


._pre_veneer
 STR     R14, _return
 STR     R10, _tmp
 LDR     R14, _handler_no
 TEQ     R14, #7			;callback?
 BNE     _pre_veneer_ok			;NO

 STR     R0, _tmp2			;YES, need to correct R14
 STR     R1, _tmp3
 LDR     R1, _regdump_original
 ADR     R0, _regdump
 CMP     R1, #page_zero_Scratch_Space
 BLO     _invalid_regdump
 MOV     R10, #15
 ._pre_veneer_L1
   LDR     R14, [R0], #4
   STR     R14, [R1], #4
   SUBS    R10, R10, #1
 BNE     _pre_veneer_L1
 ._invalid_regdump

 #if compile_RO == 400			;get PSR/PC
   LDR     R10, _regdump + 15*4
   AND     R14, R10, #&FC000003
 #endif
 #if compile_RO > 400
   LDR     R14, _regdump + 16*4
   AND     R10, R14, #%11 << 6
   AND     R14, R14, #&F0000003
   ORR     R14, R14, R10, LSL #20	;convert PSR to 26bit PSR
   LDR     R10, _regdump + 15*4
 #endif
 STR     R10, _return
 ADR     R10, _our_return
 ORR     R14, R10, R14
 SUB     R14, R14, #jit_code_start	;point return via us

 ADR     R10, _regdump
 TEQ     R0, R10
 STRNE   R14, [R1]			;update PC
 STREQ   R14, _regdump + 15*4

 LDR     R0, _tmp2
 LDR     R1, _tmp3
 LDR     R10, _tmp
LDR     PC, _entry

 ._pre_veneer_ok
 MRS     R14, CPSR
 AND     R10, R14, #%11 << 6
 AND     R14, R14, #&F0000003
 ORR     R14, R14, R10, LSL #20
 ADR     R10, _our_return
 ORR     R14, R14, R10
 SUB     R14, R14, #jit_code_start
 LDR     R10, _tmp
LDR     PC, _entry

 ._entry	DCD 0
 ._tmp		DCD 0
 ._tmp2		DCD 0
 ._tmp3		DCD 0
 ._handler_no	DCD 0
 ._regdump	DBD 17			;R0-R15, PSR
 ._regdump_original	DCD 0

 ._our_return
LDR     PC, _return			;NEEDS TO BE ABOVE jit_code_start
 ._return	DCD 0

 ._previous
 STR     R14, _entry_r14		;preserve entry R14
 ADR     R14, _pre_veneer - 8		;pointer to env_handler_previous
LDR     PC, _free_codelet

 ._free_codelet		DCD 0
 ._verify		DCD &ADFF5888
 ._entry_r14		DCD 0		;mirror env_handler_previous
 ._previous_handler	DCD 0
 ._handler_end

#set env_handler_struct_offset = _verify - (_pre_veneer - 8)

STRUCT env_handler_struct
{
 DBB env_handler_struct_offset
 .verify		DCD 0
 .entry_r14		DCD 0
 .previous_handler	DCD 0
 .size
}



;Entry:
; could be entered in any CPU mode
; possibly no stack in USER

.free_environment_handler_codelet
 STR     R2, _entry_r2
 STR     R14, _codelet
 MRS     R2, CPSR
 AND     R14, R2, #%1111
 TEQ     R14, #%0011		;are we in SVC?
 SWINE   XOS_EnterOS		;NO
 STMFD   R13!, {R2, R14}	;STACK: <entry PSR>, <SVC R14>
 MRS     R14, CPSR
 ORR     R14, R14, #%10 << 6	;IRQ off
 MSR     CPSR_c, R14
 #if compile_IOMD == 1
   NOP
 #endif
 LDR     R2, _codelet
 LDR     R14, [R2, #env_handler_struct.entry_r14]
 STR     R14, _entry_r14
 LDR     R14, [R2, #env_handler_struct.previous_handler]
 STR     R14, _previous_handler
 BL      free_codelet		;free codelet

 LDMFD   R13!, {R2, R14}
 MSR     CPSR_all, R2		;back into entry mode
 #if compile_IOMD == 1
   NOP
 #endif

 ADR     R2, _entry_r2
LDMIA   R2, {R2, R14, PC}

 ._entry_r2		DCD 0
 ._entry_r14		DCD 0
 ._previous_handler	DCD 0
 ._codelet		DCD 0