#set transient_stack_size = 4 * 1024		;4K stack size
#set transient_stack_count = 32			;number of stacks to allocate


;VIDC_List
;---------
;Converts standard RISC OS MODE definitions to VIDC parameter lists
;

MACRO VIDC_List lbpp:I, hsync:I, hbpch:I, hlbdr:I, hdisp:I, hrbdr:I, hfpch:I, vsync:I, vbpch:I, vtbdr:I, vdisp:I, vbbdr:I, vfpch:I, pixrate:I, sync:I
{
 #set HSWR = hsync
 #set HBSR = HSWR + hbpch
 #set HDSR = HBSR + hlbdr
 #set HDER = HDSR + hdisp
 #set HBER = HDER + hrbdr
 #set HCR  = HBER + hfpch

 #set VSWR = vsync
 #set VBSR = VSWR + vbpch
 #set VDSR = VBSR + vtbdr
 #set VDER = VDSR + vdisp
 #set VBER = VDER + vbbdr
 #set VCR  = VBER + vfpch

 #if compile_IOMD == 1
   DCW ((HCR  - 2) >> 1) AND &FFFE	; VIDC1 format
   DCW VCR  - 1

   DCW ((HSWR - 2) >> 1) AND &FFFE
   DCW VSWR - 1

   DCW (((HBSR - 1) >> 1) AND &FFFF) OR 1
   DCW VBSR - 1

   #if compile_IOMD == 1 AND lbpp == 0
     DCW (((HDSR - 19) >> 1) AND &FFFF) OR 1
     DCW VDSR - 1

     DCW (((HDER - 19) >> 1) AND &FFFF) OR 1
     DCW VDER - 1
   #endif
   #if compile_IOMD == 1 AND lbpp == 1
     DCW (((HDSR - 11) >> 1) AND &FFFF) OR 1
     DCW VDSR - 1

     DCW (((HDER - 11) >> 1) AND &FFFF) OR 1
     DCW VDER - 1
   #endif
   #if compile_IOMD == 1 AND lbpp == 2
     DCW (((HDSR - 7) >> 1) AND &FFFF) OR 1
     DCW VDSR - 1

     DCW (((HDER - 7) >> 1) AND &FFFF) OR 1
     DCW VDER - 1
   #endif
   #if compile_IOMD == 1 AND lbpp == 3
     DCW (((HDSR - 5) >> 1) AND &FFFF) OR 1
     DCW VDSR - 1

     DCW (((HDER - 5) >> 1) AND &FFFF) OR 1
     DCW VDER - 1
   #endif

   DCW (((HBER - 1) >> 1) AND &FFFF) OR 1
   DCW VBER - 1
 #endif
 #if compile_GPU == 1
   DCW (HCR  - 8) AND &FFFC		; VIDC20-18
   DCW VCR  - 2

   DCW (HSWR - 8) AND &FFFE
   DCW VSWR - 1

   DCW (HBSR - 12) AND &FFFE
   DCW VBSR - 1

   DCW (HDSR - 18) AND &FFFE
   DCW VDSR - 1

   DCW (HDER - 18) AND &FFFE
   DCW VDER - 1

   DCW (HBER - 12) AND &FFFE
   DCW VBER - 1
 #endif

 DCB lbpp
 DCB sync
 DCW pixrate
}




;M_DIV
;-----
;
;Entry
; n - numerator
; d - divisor
; m - working register
;
;Exit
; n - result
; m - modulus
; d - corrupt

MACRO M_DIV n:R, d:R, m:R
{
 TEMP    R14
 DIV     m, n, d
 EXG     n, m
 LOCK    R14
}





;RORWL
;-----
;Bit rotates bytes across R3 thru R14
;
;Entry
; number of bytes to rotate left by

MACRO M_RORWL rotate:I
{
  #set left  = rotate * 8
  #set right = 32 - left

  ORR     R3,  R3,  R4,  LSL #left
  MOV     R4,  R4,       LSR #right
  ORR     R4,  R4,  R5,  LSL #left
  MOV     R5,  R5,       LSR #right
  ORR     R5,  R5,  R6,  LSL #left
  MOV     R6,  R6,       LSR #right
  ORR     R6,  R6,  R7,  LSL #left
  MOV     R7,  R7,       LSR #right
  ORR     R7,  R7,  R8,  LSL #left
  MOV     R8,  R8,       LSR #right
  ORR     R8,  R8,  R9,  LSL #left
  MOV     R9,  R9,       LSR #right
  ORR     R9,  R9,  R10, LSL #left
  MOV     R10, R10,      LSR #right
  ORR     R10, R10, R11, LSL #left
  MOV     R11, R11,      LSR #right
  ORR     R11, R11, R12, LSL #left
  MOV     R12, R12,      LSR #right
  ORR     R12, R12, R14, LSL #left
}




;On_Off_Option
;-------------
;evaluates a commandline for *xxx On / Off
;
;Entry:
; R0 - command line
;
;Exit:
; R0 - 0 for Off, -1 for On

.On_Off_Option
 STR     R14, [R13, #-4]!
 ._L1
   LDRB    R14, [R0], #1
   BIC     R14, R14, #&20		;clear case
   TEQ     R14, #0			;EOL?
   TEQNE   R14, #ASC("O")		;"O" or "o"
 BNE     _L1

 TEQ     R14, #0			;EOL?
 BEQ     _off				;YES, presume off

 LDRB    R14, [R0]
 BIC     R14, R14, #&20			;clear case
 TEQ     R14, #ASC("N")			;On?
 MVNEQ   R0, #0				;YES, return R0=-1
 LDREQ   PC, [R13], #4			;YES, exit

 ._off
 MOV     R0, #0				;presume Off, return R0=0
LDR     PC, [R13], #4




;Set_On_Off_Option
;-----------------
;Sets an option on/off depending on commandline
;
;Entry:
; R0  - command line
; R14 - address of parameter to set (0 = OFF, -1 = ON)
; return address stacked

.Set_On_Off_Option
 TEQ     R1, #0				;is there a parameter?
 MOV     R1, R14
 BLNE    On_Off_Option			;YES, read On/Off option
					;NO, presume on
 STR     R0, [R1]

 SUBS    R0, R0, R0			;clear V
LDR     PC, [R13], #4




;Create_DynamicAreas
;-------------------
;Creates our Dynamic Areas
;
;Entry:
; R12 - pointer to VARS

.Create_DynamicAreas
 STMFD   R13!, {R1-R8, R14}

 MOV     R2, #0						;initial size
;              +----------- requires specific physical pages
;              |+---------- area can't be dragged in task bar
;              ||+--------- area is doubly mapped
;              |||+-------- area is not cacheable
;              ||||+------- area is not bufferable
;              |||||++++--- access privileges (2=supervisor only)
 MOV     R4, #%000110010
 #if compile_RO == 311
   MOV     R5, #RO31_DynamicAreaLimit		;max area size RO3.11 will handle
 #else
   LDR     R5, [R12, #File_Size_Limit - VARS]
 #endif
 ADR     R6, Dynamic_area_handler			;DA handler pointer
 ADR     R8, _Floppy_DA_name				;pointer to our DA name
 BL      _create_DA
 STR     R1, [R12, #our_Floppy_DA_number - VARS]	;our DA
 STR     R3, [R12, #DA_address - VARS]			;our DA address

 #if Managed_ChannelHandler == 1
   MOV     R2, #1 << audio_buffer_size			;initial size
;                +----------- requires specific physical pages
;                |+---------- area can't be dragged in task bar
;                ||+--------- area is doubly mapped
;                |||+-------- area is not cacheable
;                ||||+------- area is not bufferable
;                |||||++++--- access privileges (2=supervisor only)
 #if compile_RO <= 400 AND Managed_ChannelHandler == 1
   MOV     R4, #%011000000
 #endif
 #if compile_RO > 400 AND Managed_ChannelHandler == 1
   MOV     R4, #%011100000
 #endif
   MOV     R5, R2					;max area size
   MOV     R6, #0					;DA handler pointer
   ADR     R8, _sound_DA_name				;pointer to our DA name
   BL      _create_DA
   STR     R1, [R12, #our_sound_DA_number - VARS]	;our DA
   SUB     R3, R3, #1 << audio_buffer_size		;start of DA
   TEMP    R7
   STR     R3, sound_DA_address				;our DA address
   LOCK    R7
 #endif
LDMFD   R13!, {R1-R8, PC}



 ._Floppy_DA_name		DCB "ADFFS Floppy Buffer", 0
 #if Managed_ChannelHandler == 1
   ._sound_DA_name		DCB "ADFFS Sound Buffer", 0
 #endif
 #if compile_RO > 311
   ._transient_stack_DA_name	DCB "ADFFS Transient Stacks", 0
 #endif
 #if JIT_instruction_trace == 1
   ._instruction_trace_name	DCB "ADFFS Instruction Trace", 0
 #endif
 #if compile_RO == 400
   ._abort_stack_DA_name	DCB "ADFFS ABT32 Stack", 0
   ._Undefined_stack_DA_name	DCB "ADFFS UND32 Stack", 0
 #endif
 ALIGN


 ._create_DA
 STMFD   R13!, {R14}

 MOV     R0, #0				;Create DA PRM5a-55
 MVN     R1, #0				;area number
 MVN     R3, #0				;logical base of area
 MOV     R7, #0				;workspace pointer
 SWI     XOS_DynamicArea		;PRM5a-55
 LDMVCFD R13!, {PC}
 ADD     R13, R13, #4			;drop R14 from stack
LDMFD   R13!, {R1-R8, PC}		;exit on error




#if compile_RO > 311
;Create_Transient_DA
;--------------------
;Creates DA's for temporary stacks and instruction trace used by the JIT
;
;Entry:
; R12 - pointer to VARS

._Create_Transient_DA
 LDR     R0, [R12, #transient_stack_DA_number - VARS]
 TEQ     R0, #0				;have they already been created?
 MOVNE   PC, R14			;YES
 STMFD   R13!, {R1-R8, R14}

 MOV     R2, #transient_stack_size * (transient_stack_count+2)	;initial size
;              +----------- requires specific physical pages
;              |+---------- area can't be dragged in task bar
;              ||+--------- area is doubly mapped
;              |||+-------- area is not cacheable
;              ||||+------- area is not bufferable
;              |||||++++--- access privileges (2=supervisor only)
 MOV     R4, #%010000000
 MOV     R5, R2					;max area size
 MOV     R6, #0					;DA handler pointer
 STR     R6, [R12, #transient_stacks_used - VARS]	;reset stacks being used
 ADR     R8, _transient_stack_DA_name			;pointer to our DA name
 BL      _create_DA
 STR     R1, [R12, #transient_stack_DA_number - VARS]	;our DA
 ADD     R3, R3, R2					;start of DA

 STR     R3, [R12, #transient_stacks - VARS]		;Transient stacks
 SUB     R3, R3, #transient_stack_size * transient_stack_count
 TEMP    R2
 STR     R3, IRQ_vector_stack				;IRQ stack
 LOCK    R2
 SUB     R3, R3, #transient_stack_size
 STR     R3, [R12, #IOC_IRQ_stack - VARS]		;IOC IRQ stack

 #if JIT_instruction_trace == 1
   LDR     R2, [R12, #IT_DA_address - VARS]	;our DA
   TEQ     R2, #0
   BNE     _debug_setup
   MOV     R2, #jit_trace_buffer_size*3		;initial size
;                +----------- requires specific physical pages
;                |+---------- area can't be dragged in task bar
;                ||+--------- area is doubly mapped
;                |||+-------- area is not cacheable
;                ||||+------- area is not bufferable
;                |||||++++--- access privileges (2=supervisor only)
   MOV     R4, #%010000000
   MOV     R5, R2				;max area size
   MOV     R6, #0				;DA handler pointer
   ADR     R8, _instruction_trace_name		;pointer to our DA name
   BL      _create_DA
   STR     R1, [R12, #our_IT_DA_number - VARS]	;our DA
   STR     R3, [R12, #IT_DA_address - VARS]	;our DA address
   ADD     R3, R3, #jit_trace_buffer_size	;align in buffer
   BIC     R3, R3, #jit_trace_buffer_size-1
   TST     R3, #jit_trace_buffer_size
   ADDNE   R3, R3, #jit_trace_buffer_size
   STR     R3, [R12, #instruction_trace_ptr - VARS]
   STR     R3, [R3, #-4]
   ._debug_setup
 #endif
LDMFD   R13!, {R1-R8, PC}



#if compile_RO == 400
;create_Abort_Stack
;--------------------
;Creates a 32bit Abort stack
;
;Entry:
; R12 - pointer to VARS

._create_Abort_Stack
 STMFD   R13!, {R1-R8, R14}
 MOV     R2, #&1000					;initial size
;              +----------- requires specific physical pages
;              |+---------- area can't be dragged in task bar
;              ||+--------- area is doubly mapped
;              |||+-------- area is not cacheable
;              ||||+------- area is not bufferable
;              |||||++++--- access privileges (2=supervisor only)
 MOV     R4, #%010000000
 MOV     R5, R2						;max area size
 MOV     R6, #0						;DA handler pointer
 ADR     R8, _abort_stack_DA_name			;pointer to our DA name
 BL      _create_DA
 STR     R1, [R12, #abort_stack_DA_number - VARS]	;our DA
 ADD     R3, R3, R2					;start of DA
 STR     R3, [R12, #abt_stack - VARS]			;stack
LDMFD   R13!, {R1-R8, PC}




;create_Undefined_Stack
;--------------------
;Creates a 32bit Undefined stack
;
;Entry:
; R12 - pointer to VARS

._create_Undefined_Stack
 STMFD   R13!, {R1-R8, R14}
 MOV     R2, #&1000					;initial size
;              +----------- requires specific physical pages
;              |+---------- area can't be dragged in task bar
;              ||+--------- area is doubly mapped
;              |||+-------- area is not cacheable
;              ||||+------- area is not bufferable
;              |||||++++--- access privileges (2=supervisor only)
 MOV     R4, #%010000000
 MOV     R5, R2						;max area size
 MOV     R6, #0						;DA handler pointer
 ADR     R8, _Undefined_stack_DA_name			;pointer to our DA name
 BL      _create_DA
 STR     R1, [R12, #undefined_stack_DA_number - VARS]	;our DA
 ADD     R3, R3, R2					;start of DA
 STR     R3, [R12, #und_stack - VARS]			;stack
LDMFD   R13!, {R1-R8, PC}


 B      _create_Undefined_Stack
 B      _create_Abort_Stack
#endif

 B      _Create_Transient_DA
.DA_jump_table
 #if compile_RO == 400
   #set create_Undefined_Stack	= DA_jump_table-12
   #set create_Abort_Stack	= DA_jump_table-8
 #endif
 #set Create_Transient_DA	= DA_jump_table-4
#endif








;Remove_DynamicAreas
;-------------------
;Removes our DA's
;
;Entry:
; R12 - pointer to VARS

.Remove_DynamicAreas
 STMFD   R13!, {R0-R1, R14}

 MOV     R0, #0
 TEMP    R1
 STR     R0, reserve_DA					;allow DA to be removed
 LOCK    R1
 LDR     R1, [R12, #our_Floppy_DA_number - VARS]
 BL      _release_DA

 #if Managed_ChannelHandler == 1
   LDR     R1, [R12, #our_sound_DA_number - VARS]
   BL      _release_DA
 #endif

 #if compile_RO == 400
   LDR     R1, [R12, #abort_stack_DA_number - VARS]
   TEQ     R1, #0
   BLNE    _release_DA
   LDR     R1, [R12, #undefined_stack_DA_number - VARS]
   TEQ     R1, #0
   BLNE    _release_DA
 #endif
LDMFD   R13!, {R0-R1, PC}

 ._release_DA
 STMFD   R13!, {R14}
 MOV     R0, #1						;remove the DA
 SWI     XOS_DynamicArea				;PRM5a-53
 LDMFD   R13!, {PC}




#if compile_RO > 311
._Remove_Transient_DA
 STMFD   R13!, {R0-R2, R14}

 LDR     R1, [R12, #transient_stack_DA_number - VARS]
 TEQ     R1, #0					;is it allocated?
 LDMEQFD R13!, {R0-R2, PC}			;NO
 MOV     R2, #0
 BL      _release_DA
 STR     R2, [R12, #transient_stack_DA_number - VARS]
LDMFD   R13!, {R0-R2, PC}




;Remove_Transient_DA
;-------------------
;Removes our transient DA's
;
;Entry:
; R12 - pointer to VARS

 B       _Remove_Transient_DA
.Remove_Transient_DA




;Allocate_Transient_Stack
;------------------------
;Returns an unallocated transient stack address
;
;Exit:
; R0 - stack address
; R1 - stack number allocated

.Allocate_Transient_Stack
 STMFD   R13!, {R11-R12, R14}

 ADR     R12, VARS
 MRS     R11, CPSR
 ORR     R14, R11, #%11 << 6
 MSR     CPSR_c, R14			;disable IRQ/FIQ

 LDR     R14, [R12, #transient_stacks_used - VARS]
 LDR     R0, [R12, #transient_stacks - VARS]
 MOV     R1, #1
 ._L1
   TST     R14, R1			;is the stack being used
   BEQ     _found_stack			;NO

   SUB     R0, R0, #transient_stack_size
   MOVS    R1, R1, LSL #1
 BNE     _L1

 MOV     R12, #&1C			;ADFF501C
 B       report_unimplemented		;failed to find a stack

 ._found_stack
 ORR     R14, R14, R1			;set semaphore
 STR     R14, [R12, #transient_stacks_used - VARS]
 MSR     CPSR_c, R11			;reset IRQ/FIQ
LDMFD   R13!, {R11-R12, PC}




;Free_Transient_Stack
;--------------------
;Deallocates a transient stack
;
;Entry:
; R0 - stack number being used

.Free_Transient_Stack
 STMFD   R13!, {R11-R12, R14}

 ADR     R12, VARS
 MRS     R11, CPSR
 ORR     R14, R11, #%11 << 6
 MSR     CPSR_c, R14			;disable IRQ/FIQ

 LDR     R14, [R12, #transient_stacks_used - VARS]
 BIC     R14, R14, R0			;unset semaphore
 STR     R14, [R12, #transient_stacks_used - VARS]
 MSR     CPSR_c, R11			;reset IRQ/FIQ
LDMFD   R13!, {R11-R12, PC}
#endif




;Dynamic_area_handler (PRM5a-45)
;-------------------------------
;Allows us to prevent the user from making the DA too small for the current
;mounted floppy.

.Dynamic_area_handler
 STMFD   R13!, {R0, R14}
 TEMP    R14

 TEQ     R0, #2				;PreShrink PRM5a-46
 BEQ     _preshink
 TEQ     R0, #3				;PostShrink PRM5a-47
 TEQNE   R0, #1				;PostGrow PRM5a-46
 STREQ   R4, DA_size
LDMFD   R13!, {R0, PC}

 ._preshink
 LDR     R14, ADF_Mounted
 TEQ     R14, #0			;is an image mounted?
 LDRNE   R14, ADF_Buffer_Length		;YES, get our minimum required size
 LDR     R0, reserve_DA			;our reserve
 CMP     R0, R14			;is the reserve above the minimum
 MOVHI   R14, R0			;YES, use reserve
 SUB     R0, R4, R3			;size area will become
 CMP     R0, R14			;is it too small?
 SUBLO   R3, R4, R14			;YES, set to min size we'll allow

 LOCK    R14
LDMFD   R13!, {R0, PC}




;Reduce_DynamicArea
;------------------
;reduces our DA to a specific size, used after mounting APD's
;
;Entry:
; R0 - new buffer limit

.Reduce_DynamicArea
 STMFD   R13!, {R0-R2, R14}
 TEMP    R14

 STR     R0, ADF_Buffer_Length		;allow DA to reduce
 LDR     R2, reserve_DA			;reserve size for DA
 CMP     R0, R2				;is the requested size below reserve?
 LDMLOFD R13!, {R0-R2, PC}		;YES, exit

 LDR     R2, DA_size			;current DA size
 RSBS    R1, R2, R0			;R1 = amount to reduce DA by
 LDMPLFD R13!, {R0-R2, PC}		;exit if greater than zero
 LDR     R0, our_Floppy_DA_number
 SWI     XOS_ChangeDynamicArea		;hand back memory
 SUB     R2, R2, R1
 STR     R2, DA_size			;update our DA size

 LOCK    R14
LDMFD   R13!, {R0-R2, PC}




;MemAlloc
;---------
;Allocate memory for file load, releases current
;Entry:
; R4 = amount requested in bytes
; R4 = -1 to release previously allocated memory

.MemAlloc
 STMFD   R13!, {R1-R4, R7, R12, R14}
 ADR     R12, VARS

 LDR     R0, [R12, #our_Floppy_DA_number - VARS]	;our DA number
 LDR     R2, [R12, #DA_size - VARS]		;current DA size
 LDR     R1, [R12, #reserve_DA - VARS]		;reserve size for DA
 MOV     R7, R4

 CMN     R4, #1				;are we releasing memory
 BEQ     _release_memory

 CMP     R4, R1				;is the new size above reserve?
 MOVLO   R4, R1				;NO, set to reserve size

 SUBS    R3, R4, R2			;R3 = delta change
 MOV     R1, R3
 SWINE   XOS_ChangeDynamicArea		;PRM1-384
 LDMVSFD R13!, {R1-R4, R7, R12, PC}	;exit on error

 CMP     R3, #0				;are we increasing or decreasing?
 ADDPL   R3, R2, R1			;size = original + amount moved
 SUBMI   R3, R2, R1			;size = original - amount moved
 STR     R3, [R12, #DA_size - VARS]	;R3 = new length
 LDR     R2, [R12, #DA_address - VARS]	;our DA address

 ._exit
 STR     R2, [R12, #ADF_Buffer_Address - VARS]
 STR     R7, [R12, #ADF_Buffer_Length - VARS]

 SUBS    R0, R0, R0			;clear V
LDMFD   R13!, {R1-R4, R7, R12, PC}


 ._release_memory
 TEQ     R2, #0				;is buffer allocated?
 LDMEQFD R13!, {R1-R4, R7, R12, PC}	;NO, exit

 STR     R1, [R12, #ADF_Buffer_Length - VARS]	;allow buffer to reduce to reserve
 SUBS    R1, R1, R2			;delta from current size to reserve
 BEQ     _dont_reduce
 SWI     XOS_ChangeDynamicArea		;PRM1-384
 SUB     R2, R2, R1
 STR     R2, [R12, #DA_size - VARS]	;update DA size

 ._dont_reduce
 MOV     R7, #0
 STR     R7, [R12, #ADF_Mounted - VARS]	;mark drive as empty
 STR     R7, [R12, #APD_Tracks - VARS]
 STR     R7, [R12, #APD_Sectors - VARS]
B       _exit




;MemCopy
;-------
;Optimized Memory copy, with tailored mis-alignment code
;
;Entry:
; R0 - source
; R1 - destination
; R2 - size
;
;Exit:
; R0 - source + size
; R1 - dest + size
; other registers - preserved

.MemCopy
 TEQ     R2, #0
 MOVEQ   PC, R14			;exit if bytes to copy is 0

 #if compile_RO > 311
   STR     R14, [R13, #-4]!

   BL      check_JIT_and_taskID		;is the task supported?
   BNE     _no_JIT			;NO

   CMP     R1, #jit_appspace_end	;are we overwriting application space?
   BHS     _no_JIT			;NO

   STMFD   R13!, {R2, R4}		;YES, reset the memory block
   MOV     R4, R2			;size
   MOV     R2, R1			;start address
   BL      hv_reset_memory_block	;flush the area
   LDMFD   R13!, {R2, R4}
   ._no_JIT
   LDR     R14, [R13], #4
 #endif

 CMP     R2, #47			;if there's less than 47 bytes, skip block copy
 BLO     MemCopy_SmallCopy		;47 is the max we copy during a block move

 STMFD   R13!, {R2-R12, R14}		;store registers we want to preserve

 AND     R4, R0, #%11			;source alignment
 AND     R5, R1, #%11			;destination alignment
 ORRS    R4, R5, R4, LSL #2
 ADRNE   R5, CopyMatrix			;get the address from the matrix
 LDRNE   R6, [R5, R4, LSL #2]		;offset from here to jump too

.RelativeJumpSource
 ADDNE   PC, PC, R6			;jump to the relevant code

;.S0D0					;source and destination aligned
 SUB     R2, R2, #44			;ensure we don't overrun
 .S0D0_Loop
#if compile_RO == 500
   PLD     [R0, #128]			;pre-load 128 bytes ahead
#endif
   LDMIA   R0!, {R3-R12, R14}		;copy 44 bytes
   STMIA   R1!, {R3-R12, R14}

   SUBS    R2, R2, #44
 BPL     S0D0_Loop

 ADDS    R2, R2, #44-12			;see if there's 11+ bytes left
 LDMPLIA R0!, {R3-R5}			; 1024/44 leaves 12 bytes remainder
 STMPLIA R1!, {R3-R5}
 ADDMIS  R2, R2, #12			;correct the remaining bytes
 BNE     MemCopy_TrailingBytes		;if R2 isn't zero, deal with the odd bytes left

LDMFD   R13!, {R2-R12, PC}


 .S0D1
 LDR     R3, [R0], #4			;load first word

 STRB    R3, [R1], #1			;write bytes until destination is word aligned
 MOV     R3, R3, LSR #8			;S1D2
#if compile_RO < 500
 STRB    R3, [R1], #1			;S2D3
 MOV     R3, R3, LSR #8
 STRB    R3, [R1], #1			;S3D0
 MOV     R3, R3, LSR #8			;R3 contains carry
#else
 STRH    R3, [R1], #2
 MOV     R3, R3, LSR #16		;R3 contains carry
#endif

 SUB     R2, R2, #44+3			;ensure we don't overrun, remove bytes copied
B      S3D0_Loop


 .S0D2
 LDR     R3, [R0], #4			;load first word

#if compile_RO < 500
 STRB    R3, [R1], #1			;write bytes until destination is word aligned
 MOV     R3, R3, LSR #8			;S1D3
 STRB    R3, [R1], #1			;S2D0
 MOV     R3, R3, LSR #8			;R3 contains carry
#else
 STRH    R3, [R1], #2
 MOV     R3, R3, LSR #16		;R3 contains carry
#endif

 SUB     R2, R2, #44 + 2		;ensure we don't overrun, remove bytes copied
B       S2D0_Loop


 .S0D3
 LDR     R3, [R0], #4			;load first word

 STRB    R3, [R1], #1			;write bytes until destination is word aligned
 MOV     R3, R3, LSR #8			;R3 contains carry

 SUB     R2, R2, #44 + 1		;ensure we don't overrun, remove bytes copied
B       S1D0_Loop


 .S1D0					;source 1 byte out, destination aligned
#if compile_RO < 500
 LDR     R3, [R0], #3			;CPU will ROR #8 automatically
 BIC     R3, R3, #&FF000000		;remove bits we dont want
#else
 LDR     R3, [R0, #-1]
 MOV     R3, R3, LSR #8
 ADD     R0, R0, #3
#endif

 SUB     R2, R2, #44			;ensure we don't overrun
 .S1D0_Loop
#if compile_RO == 500
   PLD     [R0, #128]			;pre-load 128 bytes ahead
#endif
   LDMIA   R0!, {R4-R12, R14}		;read 40 bytes

   M_RORWL 3				;rotate the bytes around

   STMIA   R1!, {R3-R12}		;store 40 bytes
   SUBS    R2, R2, #40
   MOVPL   R3, R14, LSR #8		;rotate the carry
 BPL S1D0_Loop

 ADD     R2, R2, #44			;correct the remaining bytes
 SUB     R0, R0, #3			;jump back, to catch the carry

B MemCopy_TrailingBytes


 .S1D1
					;write bytes until source/dest are word aligned
#if compile_RO < 500
 LDR     R3, [R0], #3			;CPU will ROR #8 automatically
 STRB    R3, [R1], #1			;S2D2
 MOV     R3, R3, LSR #8
 STRB    R3, [R1], #1			;S3D3
 MOV     R3, R3, LSR #8
 STRB    R3, [R1], #1			;S0D0
#else
 LDR     R3, [R0, #-1]
 MOV     R3, R3, LSR #8
 STRB    R3, [R1], #1			;S2D2
 MOV     R3, R3, LSR #8
 STRH    R3, [R1], #2			;S0D0
 ADD     R0, R0, #3
#endif

 SUB     R2, R2, #44 + 3		;ensure we don't overrun, remove bytes copied
B       S0D0_Loop


 .S1D2
					;write bytes until destination is word aligned
#if compile_RO < 500
 LDR     R3, [R0], #3			;CPU will ROR #8 automatically
#else
 LDR     R3, [R0, #-1]
 MOV     R3, R3, LSR #8
 ADD     R0, R0, #3
#endif
 STRB    R3, [R1], #1			;S2D3
 MOV     R3, R3, LSR #8
 STRB    R3, [R1], #1			;S3D0
 MOV     R3, R3, LSR #8			;R3 contains carry

 SUB     R2, R2, #44 + 2		;ensure we don't overrun, remove bytes copied
B       S3D0_Loop


 .S1D3
#if compile_RO < 500
 LDR     R3, [R0], #3			;CPU will ROR #8 automatically
#endif
#if compile_RO == 500
 LDR     R3, [R0, #-1]
 MOV     R3, R3, LSR #8
 ADD     R0, R0, #3
#endif
 STRB    R3, [R1], #1			;S2D0
 MOV     R3, R3, LSR #8			;R3 contains carry

 SUB     R2, R2, #44 + 1		;ensure we don't overrun, remove bytes copied
B       S2D0_Loop


 .S2D0					;source 2 bytes out, destination aligned
#if compile_RO < 500
 LDR     R3, [R0, #-2]                 ;load first word, word aligned
 MOV     R3, R3, LSR #16               ;rotate bytes 1,2 out
#else
 LDRH    R3, [R0, #-2]
#endif

 ADD     R0, R0, #2                    ;re-align the source to a word boundry
 SUB     R2, R2, #44			;ensure we don't overrun
 .S2D0_Loop
#if compile_RO == 500
   PLD     [R0, #128]			;pre-load 128 bytes ahead
#endif
   LDMIA   R0!, {R4-R12, R14}		;read 40 bytes

   M_RORWL 2				;rotate the bytes around

   STMIA   R1!, {R3-R12}		;store 40 bytes
   SUBS    R2, R2, #40
   MOVPL   R3, R14, LSR #16		;rotate the carry
 BPL     S2D0_Loop

 ADD     R2, R2, #44			;correct the remaining bytes
 SUB     R0, R0, #2			;jump back, to catch the carry

B       MemCopy_TrailingBytes


 .S2D1
#if compile_RO < 500
 LDR     R3, [R0], #2			;CPU will ROR #16 automatically
#else
 LDRH    R3, [R0], #2
#endif
 STRB    R3, [R1], #1			;S3D2
 MOV     R3, R3, LSR #8
 STRB    R3, [R1], #1			;S0D3
 LDR     R3, [R0], #4			;load next word
 STRB    R3, [R1], #1			;S1D0
 MOV     R3, R3, LSR #8			;R3 contains carry

 SUB     R2, R2, #44 + 3		;ensure we don't overrun, remove bytes copied
B       S1D0_Loop


 .S2D2
					;write bytes until source/dest are word aligned
#if compile_RO < 500
 LDR     R3, [R0], #2			;CPU will ROR #16 automatically
 STRB    R3, [R1], #1			;S3D3
 MOV     R3, R3, LSR #8
 STRB    R3, [R1], #1			;S0D0
#else
 LDRH    R3, [R0], #2
 STRH    R3, [R1], #2
#endif

 SUB     R2, R2, #44 + 2		;ensure we don't overrun, remove bytes copied
B       S0D0_Loop


 .S2D3
					;write bytes until destination is word aligned
#if compile_RO < 500
 LDR     R3, [R0], #2			;CPU will ROR #16 automatically
#else
 LDRH    R3, [R0], #2
#endif
 STRB    R3, [R1], #1			;S3D0
 MOV     R3, R3, LSR #8			;R3 contains carry

 SUB     R2, R2, #44 + 1		;ensure we don't overrun, remove bytes copied
B       S3D0_Loop


 .S3D0					;source 3 bytes out, destination aligned
 LDRB    R3, [R0], #1			;load odd byte and re-align source

 SUB     R2, R2, #44			;ensure we don't overrun
 .S3D0_Loop
#if compile_RO == 500
   PLD     [R0, #128]			;pre-load 128 bytes ahead
#endif
   LDMIA   R0!, {R4-R12, R14}		;read 40 bytes

   M_RORWL 1				;rotate the bytes around

   STMIA   R1!, {R3-R12}		;store 40 bytes
   SUBS    R2, R2, #40
   MOVPL   R3, R14, LSR #24		;rotate the carry
 BPL     S3D0_Loop

 ADD     R2, R2, #44			;correct the remaining bytes
 SUB     R0, R0, #1			;jump back, to catch the carry
B       MemCopy_TrailingBytes


 .S3D1
 LDRB    R3, [R0], #1			;write bytes until destination is word aligned
 STRB    R3, [R1], #1			;S0D2
 LDR     R3, [R0], #4			;fetch next word
 STRB    R3, [R1], #1			;S1D3
 MOV     R3, R3, LSR #8
 STRB    R3, [R1], #1			;S2D0
 MOV     R3, R3, LSR #8			;R3 contains carry

 SUB     R2, R2, #44 + 3		;ensure we don't overrun, remove bytes copied
B      S2D0_Loop


 .S3D2
 LDRB    R3, [R0], #1			;write bytes until destination is word aligned
 STRB    R3, [R1], #1			;S0D3
 LDR     R3, [R0], #4			;fetch next word
 STRB    R3, [R1], #1			;S1D0
 MOV     R3, R3, LSR #8			;R3 contains carry

 SUB     R2, R2, #44 + 2		;ensure we don't overrun, remove bytes copied
B       S1D0_Loop


 .S3D3
 LDRB    R3, [R0], #1			;write bytes until destination is word aligned
 STRB    R3, [R1], #1			;S0D0

 SUB     R2, R2, #44 + 1		;ensure we don't overrun, remove bytes copied
B       S0D0_Loop


 .MemCopy_TrailingBytes			;deal with odd bytes at end
   SUBS    R2, R2, #1
   LDRPLB  R14, [R0], #1		;if byte left, copy byte at a time
   STRPLB  R14, [R1], #1
 BPL MemCopy_TrailingBytes

 .MemCopy_Exit
LDMFD   R13!, {R2-R12, PC}


 .MemCopy_SmallCopy			;deal with small copies less than 47 bytes
 STMFD   R13!, {R2, R14}

 .MemCopy_SmallCopy_L1
   LDRB  R14, [R0], #1			;if byte left, copy byte at a time
   STRB  R14, [R1], #1
   SUBS  R2, R2, #1
 BNE MemCopy_SmallCopy_L1
LDMFD   R13!, {R2, PC}


.CopyMatrix
 DCD MemCopy_Exit - RelativeJumpSource - 8	; we never hit this
 DCD S0D1 - RelativeJumpSource - 8		; source aligned, dest 1 byte out
 DCD S0D2 - RelativeJumpSource - 8		; source aligned, dest 2 bytes out
 DCD S0D3 - RelativeJumpSource - 8		; source aligned, dest 3 bytes out
 DCD S1D0 - RelativeJumpSource - 8		; source 1 byte  out, dest aligned
 DCD S1D1 - RelativeJumpSource - 8		; source 1 byte  out, dest 1 byte out
 DCD S1D2 - RelativeJumpSource - 8		; source 1 byte  out, dest 2 bytes out
 DCD S1D3 - RelativeJumpSource - 8		; source 1 byte  out, dest 3 bytes out
 DCD S2D0 - RelativeJumpSource - 8		; source 2 bytes out, dest aligned
 DCD S2D1 - RelativeJumpSource - 8		; source 2 bytes out, dest 1 byte out
 DCD S2D2 - RelativeJumpSource - 8		; source 2 bytes out, dest 2 bytes out
 DCD S2D3 - RelativeJumpSource - 8		; source 2 bytes out, dest 3 bytes out
 DCD S3D0 - RelativeJumpSource - 8		; source 3 bytes out, dest aligned
 DCD S3D1 - RelativeJumpSource - 8		; source 3 bytes out, dest 1 byte out
 DCD S3D2 - RelativeJumpSource - 8		; source 3 bytes out, dest 2 bytes out
 DCD S3D3 - RelativeJumpSource - 8		; source 3 bytes out, dest 3 bytes out




#if compile_RO > 311
;get_domainID_address
;--------------------
;gets the address of domainID in Page Zero from the OS if possible (RO5.17+)

.get_domainID_address
 STMFD   R13!, {R14}

 TEMP    R14
 #if compile_RO > 400
   LDR     R0, os_version
   MOV     R1, #&517 << 8		;RO 5.17+
   CMP     R0, R1
   BLO     _P1				;NO
   MOV     R0, #6			;request system internal info
   MOV     R1, #0			;request one item
   MOV     R2, #70			;item# for domainID
   SWI     XOS_ReadSysInfo		;see ROOL online documentation
   STR     R2, domainID			;store domainID pointer
   ._P1
 #endif

 BL      reset_taskID
LDMFD   R13!, {PC}




;reset_taskID
;------------
;prevent tasks being trapped

.reset_taskID
 STMFD   R13!, {R14}
 MVN     R0, #0
 STR     R0, JIT_task_ID
 LOCK    R14
LDMFD   R13!, {PC}




;get_taskID
;----------
;gets the current taskID

.get_taskID
 STMFD   R13!, {R0, R12, R14}

 ADR     R12, VARS
 LDR     R14, [R12, #JIT_task_ID - VARS]	;need to see if its a new task
 LDR     R0, [R12, #domainID - VARS]
 LDR     R0, [R0, #0]
 STR     R0, [R12, #JIT_task_ID - VARS]		;store so we can check later

 TEQ     R0, R14
 LDRNE   R0, [R12, #jit_task_count - VARS]
 ADDNE   R0, R0, #1				;increase JIT task count
 STRNE   R0, [R12, #jit_task_count - VARS]
LDMFD   R13!, {R0, R12, PC}




;return_taskID
;-------------
;returns the current taskID in R10

.return_taskID
 LDR     R10, domainID
 LDR     R10, [R10, #0]
MOV     PC, R14




;check_WimpID
;------------
;checks if the task passed in R0 is one we're managing
;
;Entry:
; R0 - Task ID to check
;
;Exit:
; EQ if task is supported

.check_WimpID
 STR     R14, [R13, #-4]!
 LDR     R14, JIT_task_ID + 4		;JIT task ID
 TEQ     R14, R0
LDR     PC, [R13], #4




;set_domain_WimpID
;-----------------
;tracks Wimp Task ID's for this domain
;
;Entry:
; R1 - Wimp Task ID

.set_domain_WimpID
 STR     R14, [R13, #-4]!
 TEMP    R14
 STR     R1, JIT_task_ID + 4
 LOCK    R14
LDR     PC, [R13], #4




;clear_domain_WimpID
;-------------------
;tracks Wimp Task ID's for this domain
;
;Entry:
; R0 - Wimp Task ID

.clear_domain_WimpID
 STMFD   R13!, {R1, R14}
 TEMP    R14
 MOV     R1, #0
 STR     R1, JIT_task_ID + 4
 LOCK    R14
LDMFD   R13!, {R1, PC}




;check_JIT_and_taskID
;checks the task ID if the JIT is running
;
;Exit:
; EQ if task is supported and JIT is running

.check_JIT_and_taskID
 STMFD   R13!, {R12, R14}

 LDR     R14, jit_code		;is the JIT running?
 TEQ     R14, #0
 BNE     check_taskID_P1

 TEQ     R14, #1		;return with NE
LDMFD   R13!, {R12, PC}




;check_taskID
;------------
;checks if the current task is one we're handling
;
;Exit:
; EQ if task is supported

.check_taskID
 STMFD   R13!, {R12, R14}

.check_taskID_P1
 LDR     R12, domainID			;address of current task ID
 LDR     R14, JIT_task_ID		;JIT task ID
 LDR     R12, [R12, #0]			;get current task ID
 TEQ     R14, R12			;is this the task we support?
LDMFD   R13!, {R12, PC}




;check_and_return_taskID
;------------------------
;checks if the current task is one we're handling
;
;Exit:
; EQ if task is supported
; R0 - current task ID

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

 BL      check_JIT_and_taskID
 LDREQ   R0, domainID			;address of current task ID
 LDREQ   R0, [R0, #0]			;get current task ID
LDR     PC, [R13], #4




;remove_taskID
;-------------
;removes a task from the monitored list
;
;Entry:
; R0  - task ID
; R12 - pointer to VARS

.remove_taskID
 STMFD   R13!, {R12, R14}
 BL      check_taskID			;is the task one we're handling
 LDMNEFD R13!, {R12, PC}		;NO
 MVN     R14, #0
 STR     R14, [R12, #JIT_task_ID - VARS]

 LDR     R0, [R12, #jit_task_count - VARS]
 SUB     R0, R0, #1			;decrease JIT task count
 STR     R0, [R12, #jit_task_count - VARS]
LDMFD   R13!, {R12, PC}




;remove_this_taskID
;------------------
;removes the current task from the monitored task list
;
;Entry:
; R12 - pointer to VARS

.remove_this_taskID
 STMFD   R13!, {R0, R14}
 LDR     R0, [R12, #domainID - VARS]	;address of current task ID
 LDR     R0, [R0, #0]			;get current task ID
 BL      remove_taskID
LDMFD   R13!, {R0, PC}




;remove_this_taskID_if_no_JIT
;----------------------------
;removes the current task from the monitor task list, only if its not
;running under the JIT
;
;Entry:
; R12 - pointer to VARS

.remove_this_taskID_if_no_JIT
 STMFD   R13!, {R0, R14}
 BL      check_and_return_taskID	;is the JIT managing this task?
 BLNE    remove_taskID			;NO
LDMFD   R13!, {R0, PC}




;check_SWI_in_appspace_or_JIT
;----------------------------
;EQ if SWI is within appspace or generated by the JIT
;
;Exit:
; EQ if SWI is within appspace or ADFFS

.check_SWI_in_appspace_or_JIT
 STMFD   R13!, {R12, R14}
 LDR     R12, [R13, #2*4 + SWI_stack.R14]
 CMP     R12, #jit_slotsize		;is it within appspace?
 TEQLO   R12, R12			;YES
 BLNE    ADFFS_internal_check		;NO, check if its the JIT
LDMFD   R13!, {R12, PC}




;ADFFS_internal_check
;--------------------
;checks to see if R12 is an address within ADFFS
;
;Entry:
; R12 - address
;
;Exit:
; EQ if address is within ADFFS

.ADFFS_internal_check
 STR     R14, [R13, #-4]!
 ADR     R14, ADFFS_end
 CMP     R14, R12
 ADRHI   R14, 0
 CMPHI   R12, R14			;is it within ADFFS?
 TEQHS   R12, R12			;its within ADFFS, return EQ
LDR     PC, [R13], #4




;check_SWI_within_ADFFS
;----------------------
;checks to see if SWI is within ADFFS
;
;Exit:
; R11 - address
; EQ if address is within ADFFS

.check_SWI_within_ADFFS			;R11 corrupted
 LDR     R11, [R13, #SWI_stack.R14]
 STR     R14, [R13, #-4]!
 ADR     R14, ADFFS_end
 CMP     R14, R11
 ADRHI   R14, 0
 CMPHI   R11, R14			;is it within ADFFS?
 TEQHS   R11, R11			;its within ADFFS, return EQ
LDR     PC, [R13], #4




;read_page_zero_value
;--------------------
;returns a value from the current tasks Page Zero

;Entry:
; Rx - offset in Page Zero

;Exit:
; Rx - Page Zero value

;.read_page_zero_value_R0
; STR     R14, [R13, #-4]!
;
; MOV     R14, #jit_page_zero
; LDR     R0, [R14, R0]
;LDR     PC, [R13], #4

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

 MOV     R14, #jit_page_zero
 LDR     R2, [R14, R2]
LDR     PC, [R13], #4




;task_page_zero
;--------------
;returns a pointer to the current tasks Page Zero

;Exit:
; Rx - Address of Page Zero

.task_page_zero_R0
 MOV     R0, #jit_page_zero
MOV     PC, R14

.task_page_zero_R1
 MOV     R1, #jit_page_zero
MOV     PC, R14

.task_page_zero_R2
 MOV     R2, #jit_page_zero
MOV     PC, R14

.task_page_zero_R3
 MOV     R3, #jit_page_zero
MOV     PC, R14

.task_page_zero_R4
 MOV     R4, #jit_page_zero
MOV     PC, R14

.task_page_zero_R5
 MOV     R5, #jit_page_zero
MOV     PC, R14

.task_page_zero_R11
 MOV     R11, #jit_page_zero
MOV     PC, R14
#endif
