#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
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




;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




;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
   #if MMUControl2_support == 0 AND compile_RO == 400
     MOV     R7, #%10000011		;SVC26 IRQ disabled
   #endif
   #if MMUControl2_support == 0 AND compile_RO > 400
     MOV     R7, #%10010011		;SVC32 IRQ disabled
   #endif
   MSR     CPSR_all, R7
   NOP
   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
   NOP
  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-R7, R12, R14}
 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}
   SUBGTS  R4, R4, #4 * 4
   STMPLIA R2!, {R0, R1, R3, R12}
   SUBGTS  R4, R4, #4 * 4
   STMPLIA R2!, {R0, R1, R3, R12}
   SUBGTS  R4, R4, #4 * 4
   STMPLIA R2!, {R0, R1, R3, R12}
   SUBGTS  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, DA_mask			;page size mask
   #if JIT_use_subpages == 1
     MOV     R3, R3, LSR #2		;convert to 4 sub-pages
   #endif

   LDR     R2, [R13, #2 * 4]		;get start address from stack: {R0-R5, 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
   ADD     R2, R2, R3
   BIC     R2, R2, R3			;page align start
   CMP     R2, R4			;is it less than a page?
   BHS     hv_reset_memory_block_exit	;YES, exit
   SUB     R4, R4, R2			;R4=page count

   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
     #if JIT_use_subpages == 1
       AND     R5, R0, #%11			;R14=1KB sub-page
       MOV     R5, R5, LSL #1
       MOV     R14, #11 << 4
     #endif
     BIC     R0, R0, #%11			;align to word

     LDR     R1, [R0]
     #if JIT_use_subpages == 1 AND compile_RO >= 400
       ORR     R14, R1, R14, LSL R5		;user is read/write
     #endif
     #if JIT_use_subpages == 0 AND compile_RO >= 500
       ORR     R14, R1, #%00000011 << 4		;user is read/write
     #endif
     #if JIT_use_subpages == 0 AND compile_RO > 311 AND compile_RO < 500
       ORR     R14, R1, #%11111111 << 4		;user is read/write
     #endif
     TEQ     R1, R14				;is it currently read/write?
     STRNE   R14, [R0]				;NO, change
     #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
LDMFD   R13!, {R0-R7, 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
 .L1_CACHE_LINE_SIZE	DCD ARM_CACHE_LINE_SIZE
 .L1_CACHE_LINE_MASK	DCD ARM_CACHE_LINE_SIZE - 1
#endif

.CACHEOP_ACTION_END
