#set ARM_CACHE_LINE_SIZE	= 32	;cache alignment
#set ARM_SA_CACHE_SIZE		= 16 * 1024	;ARM SA cache size
#set ARM_SA_CACHE_CUTOVER	= ARM_SA_CACHE_SIZE / ARM_CACHE_LINE_SIZE	;flush entire SA cache if size above
#set max_cache_ops_index	= 128 * pending_cache_op.size


STRUCT pending_cache_op
{
 .address	DCD 0	;start address of block to flush
 .end		DCD 0	;end address of block
 .size
}

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


#if MMUControl2_support == 0
.MMUControl_clean_D_cache
 STMFD   R13!, {R0-R1, R14}
 ADR     R14, DCache_CleanBaseAddress
 LDR     R0, [R14, #4]
 SWP     R0, R0, [R14]
 STR     R0, [R14, #4]
 MOV     R1, #ARM_SA_CACHE_SIZE
 ._L1
   SUBS    R1, R1, #ARM_CACHE_LINE_SIZE * 8
   LDR     R14, [R0, #ARM_CACHE_LINE_SIZE * 1]
   LDR     R14, [R0, #ARM_CACHE_LINE_SIZE * 2]
   LDR     R14, [R0, #ARM_CACHE_LINE_SIZE * 3]
   LDR     R14, [R0, #ARM_CACHE_LINE_SIZE * 4]
   LDR     R14, [R0, #ARM_CACHE_LINE_SIZE * 5]
   LDR     R14, [R0, #ARM_CACHE_LINE_SIZE * 6]
   LDR     R14, [R0, #ARM_CACHE_LINE_SIZE * 7]
   LDR     R14, [R0], #ARM_CACHE_LINE_SIZE * 8
 BNE     _L1
 MCR     P15, 0, R0, C7, C10, 4		;drain write buffer
; #if JIT_debug == 1 AND MMUControl2_support == 0
;   ADR     R1, jit_cache_flushes
;   LDR     R0, [R1]
;   ADD     R0, R0, #1 << 8
;   STR     R0, [R1]
; #endif
 LDMFD   R13!, {R0-R1, PC}


;					;SA OS code - Analyse_WB_Crd (437)
.MMUControl_ARMop7_SA		;IMB_Full		IMB_Full_WB_Crd
.MMUControl_ARMop1_SA		;Cache_CleanAll		Cache_CleanAll_WB_Crd
 STMFD   R13!, {R14}
 BL      MMUControl_clean_D_cache
 MCR     P15, 0, R0, C7, C5, 0		;invalidate I cache
 LDMFD   R13!, {PC}


.MMUControl_ARMop5_SA		;TLB_InvalidateEntry	TLB_InvalidateEntry_WB_Crd (SA)
;							TLB_InvalidateEntry_ARMv3 (ARM600/710)
 STMFD   R13!, {R14}
 LDR     R14, cpu_id
 CMP     R14, #CPUID_StrongARM		;below StrongARM?
 MCRHS   P15, 0, R0, C8, C6, 1		;flush DTLB entry
 MCRHS   P15, 0, R0, C8, C5, 0		;flush ITLB
 MCRHS   P15, 0, R0, C7, C10, 4		;drain write buffer
 MCRLO   P15, 0, R0, C6, C0, 0		;ARM710VE-71 purge TLB entry
 LDMFD   R13!, {PC}


;used in abort handler to invalidate 1 or 2 cache lines
; R0 corrupt
.MMUControl_ARMop8_SA		;IMB_Range		IMB_Range_WB_Crd
 STMFD   R13!, {R14}
 ._L1
   MCR     P15, 0, R0, C7, C10, 1	;clean D Cache entry
   ADD     R0, R0, #ARM_CACHE_LINE_SIZE
   CMP     R0, R1
 BLO     _L1
 MCR     P15, 0, R0, C7, C10, 4		;drain write buffer
 MCR     P15, 0, R0, C7, C5, 0		;invalidate I cache
 LDMFD   R13!, {PC}


; R4-R5 corrupt
.MMUControl_ARMop9_SA		;IMB_List		IMB_List_WB_Crd
 CMP     R2, #ARM_SA_CACHE_CUTOVER
 BHS     MMUControl_ARMop7_SA
 STMFD   R13!, {R14}
 ._L1
   LDMIA   R0!, {R4-R5}
   ._L2
     MCR     P15, 0, R4, C7, C10, 1		;clean virtual address
     ADD     R4, R4, #ARM_CACHE_LINE_SIZE
     CMP     R4, R5
   BLO     _L2

   CMP     R0, R1
 BNE     _L1
 MCR     P15, 0, R0, C7, C10, 4			;drain write buffer
 MCR     P15, 0, R0, C7, C5, 0			;invalidate I cache
 LDMFD   R13!, {PC}


; R0, R1 corrupt
; only used when mapping/unmapping VIDC1 video memory and IOC chipset range
.MMUControl_ARMop14_SA		;MMU_ChangingEntries	MMU_ChangingEntries_WB_Crd (SA)
;							MMU_ChangingEntries_ARMv3 (ARM600/710)
 STMFD   R13!, {R14}
 LDR     R14, cpu_id
 CMP     R14, #CPUID_StrongARM		;below StrongARM?
 MCRLO   P15, 0, R0, C5, C0		;ARM710VE-71 invalidate TLB
 MCRLO   P15, 0, R0, C7, C0		;ARM710VE-71 invalidate cache
 LDMLOFD R13!, {PC}
 BL      MMUControl_clean_D_cache
 MCR     P15, 0, R0, C7, C10, 4		;drain write buffer
 MCR     P15, 0, R0, C8, C6, 0		;flush DTLB
 MCR     P15, 0, R0, C7, C6, 0		;flush D cache
 LDMFD   R13!, {PC}
#endif




ALIGN   32, 4

;CACHEOP_CLEAN_D_INVALIDATE_I
;----------------------------
;Cleans the data cache and invalidates the instruction 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_INVALIDATE_I
 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
 LDR     R4, [R2, #cache_ops_size]		;get the flush limit size
 ADD     R1, R1, R3				;must be at least one cache line
 BIC     R0, R0, R3				;cache align start
 BIC     R1, R1, R3				;..and end
 SUB     R3, R1, R0				;R3=size of area
 ADD     R4, R4, R3				;increase size counter

 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

 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




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

.CACHEOP_ACTION
 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?
 #if JIT_debug == 1
   ADR     R2, jit_cache_flushes
   LDR     R3, [R2]
     ADDMI   R3, R3, #1 << 8
     STRMI   R3, [R2]
 #endif
 MOVMI   R1, #0
 STRMI   R1, [R0, #pending_cache_op.address]	;clear
 STRMI   R1, [R0, #cache_ops_size]		;reset counter
 STRMI   R1, [R0, #cache_ops_index]		;reset index
 LDRMI   PC, MMUControl_ARMop7			;complete flush pending?
 LDRNE   R1, [R0, #cache_ops_index]		;R1=index pointer
 TEQNE   R1, #0					;are there pending actions?
 MOVEQ   PC, R14				;NO

 #if JIT_debug == 1
   ADD     R3, R3, R1
   BIC     R3, R3, #%1100000
   STR     R3, [R2]
 #endif

 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
 STR     R3, [R0, #cache_ops_index]		;reset index
 LDR     PC, MMUControl_ARMop9




;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
   LDR     R3, JIT_ARM4
   TEQ     R3, #1			;ARMv4 with Cache ON?
   BEQ     hv_reset_memory_block_exit	;YES, skip page reset

   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 JIT_self_modifying_support == 1
;toggle read only pages back to read/write
.reset_appspace_L2PT
 STMFD   R13!, {R0-R8, R14}
 LDR     R3, JIT_ARM4
 TEQ     R3, #1				;ARMv4 with Cache ON?
 LDMEQFD R13!, {R0-R8, PC}		;YES, skip page reset

 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	
LDMFD   R13!, {R0-R8, PC}
#endif




;SETUP_MMUControl2
;-----------------
;Requests the address of RISCOS's ARMOp's

.SETUP_MMUControl2
 STMFD   R13!, {R0, R14}

 #if MMUControl2_support == 1
   MOV     R0, #2 + (1 << 8)
   SWI     XOS_MMUControl
   STR     R0, MMUControl_ARMop1	;Cache_CleanAll
   
   MOV     R0, #2 + (5 << 8)
   SWI     XOS_MMUControl
   STR     R0, MMUControl_ARMop5	;TLB_InvalidateEntry
   
   MOV     R0, #2 + (7 << 8)
   SWI     XOS_MMUControl
   STR     R0, MMUControl_ARMop7	;IMB_Full
   
   MOV     R0, #2 + (8 << 8)
   SWI     XOS_MMUControl
   STR     R0, MMUControl_ARMop8	;IMB_Range
   
   MOV     R0, #2 + (9 << 8)
   SWI     XOS_MMUControl
   STR     R0, MMUControl_ARMop9	;IMB_List
   
   MOV     R0, #2 + (14 << 8)
   SWI     XOS_MMUControl
   STR     R0, MMUControl_ARMop14	;MMU_ChangingEntries
 #endif
 #if compile_GPU == 1
   MOV     R0, #2 + (21 << 8)
   SWI     XOS_MMUControl
   STR     R0, MMUControl_ARMop21	;Cache_CleanInvalidateRange
 #endif

 #if MMUControl2_support == 0
   ADR     R0, MMUControl_ARMop1_SA
   STR     R0, MMUControl_ARMop1	;Cache_CleanAll
   STR     R0, MMUControl_ARMop7	;IMB_Full

   ADR     R0, MMUControl_ARMop5_SA
   STR     R0, MMUControl_ARMop5	;TLB_InvalidateEntry

   ADR     R0, MMUControl_ARMop8_SA
   STR     R0, MMUControl_ARMop8	;IMB_Range
  
   ADR     R0, MMUControl_ARMop9_SA
   STR     R0, MMUControl_ARMop9	;IMB_List
  
   ADR     R0, MMUControl_ARMop14_SA
   STR     R0, MMUControl_ARMop14	;MMU_ChangingEntries
 #endif
LDMFD   R13!, {R0, PC}




#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
 ADRLO   R0, _mov_pc_r14
 STRLO   R0, MMUControl_ARMop1
 STRLO   R0, MMUControl_ARMop7
 STRLO   R0, MMUControl_ARMop8
 STRLO   R0, MMUControl_ARMop9
 LDMFD   R13!, {R0, PC}

 ._mov_pc_r14 MOV PC, R14
#endif




 #if MMUControl2_support == 1
  .L1_CACHE_LINE_SIZE	DCD ARM_CACHE_LINE_SIZE
 #endif
 .L1_CACHE_LINE_MASK	DCD ARM_CACHE_LINE_SIZE - 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
 #if compile_GPU == 1
   .MMUControl_ARMop21	DCD 0
 #endif

.CACHEOP_ACTION_END
