#set ARM_CACHE_LINE_SIZE	= 32	;cache alignment
#set ARM_CACHE_CUTOVER		= 1024	;flush the entire cache if size above
					;maximum pending cache transactions
#set max_cache_ops_index	= 128 * pending_cache_op.size


STRUCT pending_cache_op
{
 .address	DCD 0	;start address of block to flush
			;bit 0 = Invalidate instruction cache
			;bit 1 = Clean data cache
 .end		DCD 0	;end address of block
 .size
}
#set CLEAN_D		= 2	;bit in pending cache op to indicate D op
#set INVALIDATE_I	= 1	;bit in pending cache op to indicate I op

#set cache_ops_index	= -4
#set cache_ops_size	= -8


#if MMUControl2_support == 1
;SETUP_MMUControl2
;-----------------
;Requests the address of RISCOS's ARMOp's

.SETUP_MMUControl2
 STMFD   R13!, {R0, R14}

 MOV     R0, #2 + (1 << 8)
 SWI     XOS_MMUControl
 STR     R0, MMUControl_ARMop1

 MOV     R0, #2 + (5 << 8)
 SWI     XOS_MMUControl
 STR     R0, MMUControl_ARMop5

 MOV     R0, #2 + (7 << 8)
 SWI     XOS_MMUControl
 STR     R0, MMUControl_ARMop7

 MOV     R0, #2 + (8 << 8)
 SWI     XOS_MMUControl
 STR     R0, MMUControl_ARMop8

 MOV     R0, #2 + (9 << 8)
 SWI     XOS_MMUControl
 STR     R0, MMUControl_ARMop9

 MOV     R0, #2 + (14 << 8)
 SWI     XOS_MMUControl
 STR     R0, MMUControl_ARMop14

 MOV     R0, #2 + (21 << 8)
 SWI     XOS_MMUControl
 STR     R0, MMUControl_ARMop21
LDMFD   R13!, {R0, PC}
#endif



ALIGN   32, 4

;CACHEOP_CLEAN_D_RANGE
;---------------------
;Cleans the data cache for a region of memory
;
;ref. ARM ARM rev. I B6-21 (PDF p805)
;ref. ARM ARM rev. I B6-26 (PDF p811)
;ref. Intel X-Scale Core Developers Manual p87
;ref. StrongARM Data Sheet v2.0-18 (PDF p21)
;ref. ARM1176JZF-S TRM rev. r0p7 3-76 (PDF p208)
;
;Entry:
; R0 - start address
; R1 - end address

;Exit:
; R0-R4 - corrupt

.CACHEOP_CLEAN_D_RANGE
 #if MMUControl2_support == 0
   MOV     R3, #CLEAN_D
   B       store_cache_op
 #endif
; fall-thru to CACHEOP_CLEAN_D_INVALIDATE_I if MMUControl2_support == 1



;CACHEOP_CLEAN_D_INVALIDATE_I
;----------------------------
;Cleans the data cache and invalidates the instruction cache
;for a region of memory

;Entry:
; R0 - start address
; R1 - end address

;Exit:
; R0-R4 - corrupted

.CACHEOP_CLEAN_D_INVALIDATE_I
 #if MMUControl2_support == 0
   MOV     R3, #CLEAN_D + INVALIDATE_I
   .store_cache_op

   ADR     R2, pending_cache_ops		;cache ops buffer
   LDR     R4, [R2, #pending_cache_op.address]	;get 1st entry
   CMN     R4, #1				;is a complete flush pending?
   MOVEQ   PC, R14				;YES, exit

   BIC     R0, R0, #ARM_CACHE_LINE_SIZE - 1	;cache align start
   ORR     R0, R0, R3				;add in our flush flags
 #endif
 #if MMUControl2_support == 1
   ADR     R2, pending_cache_ops		;cache ops buffer
   LDR     R4, [R2, #pending_cache_op.address]	;get 1st entry
   CMN     R4, #1				;is a complete flush pending?
   MOVEQ   PC, R14				;YES, exit

   LDR     R3, L1_CACHE_LINE_MASK		;get cache alignment
   ADD     R1, R1, #1
   ADD     R1, R1, R3				;must be at least one cache line
   BIC     R0, R0, R3				;cache align start
   BIC     R1, R1, R3
   LDR     R4, [R2, #cache_ops_size]		;get the flush limit size
   SUB     R3, R1, R0				;decrease by start address
   ADD     R4, R4, R3
 #endif                                 
                                        
 LDR     R3, [R2, #cache_ops_index]		;get current index
 TEQ     R3, #max_cache_ops_index		;are we at the limit?
 BEQ     _flush_everything			;YES, flush everything

 #if MMUControl2_support == 0
   LDR     R4, [R2, #cache_ops_size]		;get the flush limit size
   ADD     R4, R4, R0				;add start address
   SUBS    R4, R4, R1				;decrease by end address
   BMI     _flush_everything			;MI: A complete cache is required
 #endif
 STR     R4, [R2, #cache_ops_size]		;update the flush counter

 ADD     R4, R2, R3				;R4=table entry
 STR     R1, [R4, #pending_cache_op.end]	;store end address
 ._exit
 STR     R0, [R2, R3]				;store address
 ADD     R3, R3, #pending_cache_op.size
 STR     R3, [R2, #cache_ops_index]		;update index
MOV     PC, R14

 ._flush_everything
 MOV     R3, #0					;we now have only 1 cache-op entry
 MVN     R0, #0					;-1 in first entry means complete
B       _exit




#if ARMv3_support == 1
;detect_ARMv3
;---------------
;Prevents unnessary cache flushing on ARMv3

.detect_ARMv3
 STMFD   R13!, {R0, R14}
 LDR     R0, cpu_id
 CMP     R0, #CPUID_StrongARM
 LDRLO   R0, _mov_pc_r14
 STRLO   R0, CACHEOP_ACTION
 STRLO   R0, CACHEOP_CLEAN_D_INVALIDATE_I
 STRLO   R0, CACHEOP_CLEAN_D_RANGE
 LDMFD   R13!, {R0, PC}

 ._mov_pc_r14 MOV PC, R14
#endif




;CACHEOP_ACTION
;--------------
;Actions all pending cache ops
;
;Exit:
; R0-R7 - Corrupted

.CACHEOP_ACTION
 #if MMUControl2_support == 1
   ADR     R0, pending_cache_ops		;R0=cache ops buffer
   LDR     R1, [R0, #pending_cache_op.address]	;get 1st entry
   CMP     R1, #0				;are there pending actions?
   MOVEQ   PC, R14				;NO
   BMI     _complete_flush			;complete flush pending?

   #if MMUControl2_support == 1 AND JIT_debug == 1
     ADR     R2, jit_cache_flushes
     LDR     R1, [R2]
     ADD     R1, R1, #1
     BIC     R1, R1, #64
     STR     R1, [R2]
   #endif

   LDR     R1, [R0, #cache_ops_index]		;R6=index pointer
   TEQ     R1, #0				;are there pending actions?
   MOVEQ   PC, R14				;NO
   MOV     R3, #0
   LDR     R2, [R0, #cache_ops_size]		;amount of memory in total to clean
   ADD     R1, R1, R0				;R1=end pointer
   STR     R3, [R0, #cache_ops_size]		;reset counter
   LDR     PC, MMUControl_ARMop9

   ._complete_flush
   #if MMUControl2_support == 1 AND JIT_debug == 1
     ADR     R0, jit_cache_flushes
     LDR     R1, [R0]
     ADD     R1, R1, #1 << 8
     STR     R1, [R0]
   #endif

   LDR     PC, MMUControl_ARMop7
 #endif
 #if MMUControl2_support == 0
   ADR     R3, pending_cache_ops		;R3=cache ops buffer
   LDR     R6, [R3, #cache_ops_index]		;R6=index pointer
   TEQ     R6, #0				;are there pending actions?
   MOVEQ   PC, R14				;NO

   STR     R14, [R13, #-4]!

   MOV     R5, #0
   STR     R5, [R3, #cache_ops_index]		;reset the index
   MOV     R5, #ARM_CACHE_CUTOVER		;reset flush limit
   STR     R5, [R3, #cache_ops_size]

   MRS     R5, CPSR				;R5=entry CPU mode
   BIC     R7, R5, #%1111
   #if MMUControl2_support == 0 AND compile_RO == 400
     ORR     R7, R7, #%10000011			;SVC26 IRQ disabled
   #endif
   #if MMUControl2_support == 0 AND compile_RO > 400
     ORR     R7, R7, #%10010011			;SVC32 IRQ disabled
   #endif
   MSR     CPSR_all, R7
   #if MMUControl2_support == 0 AND compile_IOMD == 1
     NOP
   #endif
   MRS     R7, SPSR				;preserve SVC SPSR and R14
   MOV     R4, R14

   LDR     R1, [R3, #pending_cache_op.address] ;get 1st entry
   CMN     R1, #1				;is a complete flush pending?
   BEQ     _complete_flush			;YES

   MOV     R0, #1				;flush range
   ._L1
     BIC     R1, R1, #CLEAN_D + INVALIDATE_I	;clear flags
     LDR     R2, [R3, #pending_cache_op.end]	;get 1st entry
     SWI     XOS_SynchroniseCodeAreas		;App-Note-295-8

     #if MMUControl2_support == 0 AND JIT_debug == 1
       ADR     R2, jit_cache_flushes
       LDR     R1, [R2]
       ADD     R1, R1, #1
       BIC     R1, R1, #64
       STR     R1, [R2]
     #endif

     ADD     R3, R3, #pending_cache_op.size	;go to next op
     SUBS    R6, R6, #pending_cache_op.size	;last entry?
     LDRNE   R1, [R3, #pending_cache_op.address]	;get next entry
   BNE     _L1
   B       _exit

   ._complete_flush
   MOV     R0, #0				;indicate complete flush
   SWI     XOS_SynchroniseCodeAreas
   STR     R0, [R3, #pending_cache_op.address]	;clear complete cache flush flag

   #if MMUControl2_support == 0 AND JIT_debug == 1
     ADR     R0, jit_cache_flushes
     LDR     R1, [R0]
     ADD     R1, R1, #1 << 8
     STR     R1, [R0]
   #endif

   ._exit
  ; MOV     R0, #1 + DRAIN_WRITE_BUFFER
  ; SWI     XOS_MMUControl
  ;^^^ ref: https://www.riscosopen.org/wiki/documentation/show/OS_MMUControl

   MOV     R14, R4				;preserve SVC SPSR and R14
   MSR     SPSR_all, R7
   MSR     CPSR_all, R5				;back into entry CPU mode
   #if MMUControl2_support == 0 AND compile_IOMD == 1
     NOP
   #endif
   LDR    PC, [R13], #4
 #endif




;hv_reset_memory_block
;---------------------
;overwrite JIT area to trigger JIT and change memory to R/W
;
;Entry:
; R2 - start address in normal appspace
; R4 - size
;
;Exit:
; Registers preserved

.hv_reset_memory_block
 STMFD   R13!, {R0-R8, R11-R12, R14}

 CMP     R2, #page_zero_Scratch_Space	;make sure we don't overwrite
 BHS     _keep
 RSBLO   R8, R2, #page_zero_Scratch_Space	;page zero
 MOVLO   R2, #page_zero_Scratch_Space	;fixes BBC6502Emulator in
 SUBLOS  R4, R4, R8			;Enthar Seven
 LDMLSFD R13!, {R0-R8, R11-R12, PC}
 ._keep
 ADD     R8, R2, R4
 SUBS    R8, R8, #jit_26bit_exits-jit_26bit_exit_size-jit_code_start
 SUBPL   R4, R4, R8			;cap at our 26bit exits

 MRS     R11, CPSR
 ORR     R3, R11, #%11 << 6		;need to disable all IRQ/FIQ
 MSR     CPSR_c, R3

 BIC     R2, R2, #%11			;word align start
 #if JIT_codelet_recycling == 1
   LDR     R5, first_codelet		;get first codelet address
   #if compile_RO == 500 AND JIT_codelet_recycling == 1
     PLD     [R5, #0]			;preload codelet header
   #endif
   ADD     R3, R2, #8			;account for pipeline
   ._L0
     MOV     R12, R5
;@@@ next line needs to check all sources for the codelet
     LDR     R0, [R5, #codelet_struct.source]
     SUB     R0, R0, R3
     CMP     R0, R4			;codelet within range?
     ADDLO   R2, R5, #codelet_struct.codelet - 8
     LDR     R5, [R5, #codelet_struct.next]
     #if compile_RO == 500 AND JIT_codelet_recycling == 1
       PLD     [R5, #0]			;preload codelet header
     #endif
     BLLO    free_codelet		;YES, free the codelet

     CMP     R5, R12			;last codelet?
   BLO     _L0				;NO
   SUB     R2, R3, #8			;preserve R2
 #endif

 LDR     R12, branch_to_jit		;reset JIT memory to Hypercalls
 ADD     R2, R2, #jit_code_start
 MOV     R0, R12
 MOV     R1, R12
 MOV     R3, R12

 SUBS    R4, R4, #4 * 4
 ._L1
   STMPLIA R2!, {R0, R1, R3, R12}
   SUBPLS  R4, R4, #4 * 4
   STMPLIA R2!, {R0, R1, R3, R12}
   SUBPLS  R4, R4, #4 * 4
   STMPLIA R2!, {R0, R1, R3, R12}
   SUBPLS  R4, R4, #4 * 4
   STMPLIA R2!, {R0, R1, R3, R12}
   SUBPLS  R4, R4, #4 * 4
 BGT      _L1

 ADDMIS   R4, R4, #4 * 4
 STRNE    R12, [R2], #4			;catch odd words
 SUBNES   R4, R4, #4
 STRNE    R12, [R2], #4
 SUBNES   R4, R4, #4
 STRNE    R12, [R2], #4

 #if JIT_self_modifying_support == 1	;change pages to read/write
   #if JIT_self_modifying_support == 1 AND ARMv4_JIT == 1
     LDR     R3, JIT_ARM4
     TEQ     R3, #1			;ARMv4?
     BEQ     hv_reset_memory_block_exit	;YES, skip page reset
   #endif
   LDR     R8, subpages_supported	;R8 [0]=1 if subpages supported
   LDR     R3, DA_mask			;page size mask
   TST     R8, #1			;subpages supported?
   MOVNE   R3, R3, LSR #2		;YES, convert to 4 sub-pages

   LDR     R2, [R13, #2 * 4]		;get start address from stack: {R0-R8, R11-R12, R14}
   LDR     R4, [R13, #4 * 4]		;get size from stack
   ADD     R4, R4, R2			;R4=end address
   BIC     R4, R4, R3			;page align end, rounded down
   ADD     R2, R2, R3
   BIC     R2, R2, R3			;page align start, rounded up
   SUBS    R4, R4, R2			;R4=page count, more than one complete page?
   BLE     hv_reset_memory_block_exit	;NO, exit

   LDR     R12, L2PT_addr
   ADD     R3, R3, #1			;R3=page size
   SUB     R2, R2, R3			;fix for 80321 errata 7
   .hv_reset_memory_block_L2
     ADD     R2, R2, R3				;go to next page +fix for 80321 errata 7
     ADD     R0, R12, R2, LSR #10		;L2PT entry address for page ARM710VE-83
     TST     R8, #1				;subpages supported?
     ANDNE   R5, R0, #%11			;YES, R5=1KB sub-page
     MOVNE   R5, R5, LSL #1			; |
     MOVNE   R14, #%11 << 4			; |+  R14=AP mask
     BIC     R0, R0, #%11			;align to word
     LDR     R1, [R0, #0]
     ORRNE   R14, R1, R14, LSL R5		;YES, user is read/write
     ORREQ   R14, R1, R8, LSR #16		;NO, user is read/write
     TEQ     R1, R14				;is it currently read/write?
     #if JIT_self_modifying_support == 1 AND compile_IOMD == 1
       SWPNE   R1, R14, [R0]			;NO, make read only and flush WB
     #endif
     #if JIT_self_modifying_support == 1 AND compile_IOMD == 0
       STRNE   R14, [R0, #0]			;NO, make read only
     #endif
     #if JIT_self_modifying_support == 1 AND JIT_flush_TLB == 1
       CACHEOP_INVALIDATE_TLB_ENTRYNE R2	;invalidate D TLB entry by MVA
     #endif

     SUBS    R4, R4, R3				;fix for 80321 errata 7
   BNE     hv_reset_memory_block_L2		;+fix for 80321 errata 7
 #endif

 .hv_reset_memory_block_exit
 LDR     R0, [R13, #2 * 4]			;get start address from stack {R0-R5, R12, R14}
 ADD     R0, R0, #jit_code_start		;adjust to JIT space
 LDR     R1, [R13, #4 * 4]			;get size from stack
 ADD     R1, R1, R0				;R1=end address
 BL      CACHEOP_CLEAN_D_INVALIDATE_I		;flush the cache
 BL      CACHEOP_ACTION				;action the flush immediately
 MSR     CPSR_c, R11				;reset IRQ/FIQ
LDMFD   R13!, {R0-R8, R11-R12, PC}




#if MMUControl2_support == 1
 .MMUControl_ARMop1	DCD 0
 .MMUControl_ARMop5	DCD 0
 .MMUControl_ARMop7	DCD 0
 .MMUControl_ARMop8	DCD 0
 .MMUControl_ARMop9	DCD 0
 .MMUControl_ARMop14	DCD 0
 .MMUControl_ARMop21	DCD 0
 .L1_CACHE_LINE_SIZE	DCD ARM_CACHE_LINE_SIZE
 .L1_CACHE_LINE_MASK	DCD ARM_CACHE_LINE_SIZE - 1
#endif




#if compile_IOMD == 1
;CACHEOP_INVALIDATE_TLB_R0
;-------------------------
;invalidate TLB entry for address in R0
;
;Entry:
; R0 - TLB MVA
; R1 - TLB value

.CACHEOP_INVALIDATE_TLB_R0
 STMFD   R13!, {R0-R3, R14}
 LDR     R2, L2PT_addr
 MOV     R3, R0, LSR #10			;convert to offset in L2PT
 BIC     R3, R3, #3				;word align
 ADD     R2, R2, R3
 SWP     R1, R1, [R2]				;write to L2PT and flush write buffer
 CACHEOP_INVALIDATE_TLB_ENTRY R0		;purge TLB entry
LDMFD   R13!, {R0-R3, PC}
#endif




#if JIT_self_modifying_support == 1
;toggle read only pages back to read/write
.reset_appspace_L2PT
 STMFD   R13!, {R0-R8, R14}
 #if JIT_self_modifying_support == 1 AND ARMv4_JIT == 1
   LDR     R3, JIT_ARM4
   TEQ     R3, #1			;ARMv4?
   LDMEQFD R13!, {R0-R8, PC}		;YES, skip page reset
 #endif
 LDR     R8, subpages_supported		;R8 [31..15]=TLB mask
 LDR     R7, DA_mask			;page size mask
 LDR     R2, L2PT_addr
 MOV     R3, #jit_allocate_memory	;size of appspace
 MOV     R4, #&8000			;start address
 SUB     R3, R3, R4
 ADD     R7, R7, #1			;R7=page size
 .reset_appspace_L2PT_L1
   MOV     R5, R4, LSR #10		;convert to offset in L2PT
   BIC     R5, R5, #3			;word align
   LDR     R1, [R2, R5]
   #if JIT_self_modifying_support == 1 AND compile_IOMD == 0
     ORR     R14, R1, R8, LSR #16	;user is read/write
     TEQ     R1, R14			;is it currently read/write?
     STRNE   R14, [R2, R5]		;NO, make read only
   #endif
   #if JIT_self_modifying_support == 1 AND compile_IOMD == 1
     ADD     R0, R2, R5
     ORR     R14, R1, R8, LSR #16	;user is read/write
     TEQ     R1, R14			;is it currently read/write?
     SWPNE   R1, R14, [R0]		;NO, make read only and flush WB
   #endif
   #if JIT_self_modifying_support == 1 AND JIT_flush_TLB == 1
     CACHEOP_INVALIDATE_TLB_ENTRYNE R4	;invalidate D TLB entry by MVA
   #endif

   ADD     R4, R4, R7			;go to next page / fix for 80321 errata 7
   SUBS    R3, R3, R7			;+fix for 80321 errata 7
 BNE     reset_appspace_L2PT_L1	

 #if JIT_self_modifying_support == 1 AND JIT_protect_page_zero == 1
   LDR     R0, os_version		;ARM710VE-83 / 85
   CMP     R0, #&380 << 8		;are we below RO3.8?
   LDRLO   R1, page_zero_L2PT		;YES, restore page zero protection
   MOVLO   R0, #0			; |
   BLLO    CACHEOP_INVALIDATE_TLB_R0	; |+ flush TLB
 #endif
LDMFD   R13!, {R0-R8, PC}
#endif

.CACHEOP_ACTION_END
