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




MACRO oDIV res:R, top:R, bot:R, t:TR	;res=top DIV bot  top=top MOD bot
{
 MOVS    t, bot			; Put divisor in t
 MOV     res, #0		; Initialize quotient
 BEQ     _divide_by_zero
 CMP     t, top, LSR #1		; double it until
 ._div_loop1
   MOVLS   t, t, LSL #1		; 2 * t>top
   CMP     t, top, LSR #1
 BLS     _div_loop1

 ._div_loop2
   CMP     top, t		; Can we subtract t?
   SUBCS   top, top, t		; YES
   ADC     res, res, res	; Double res
   MOV     t, t, LSR #1		; Halve t
   CMP     t, bot		; and loop until
 BHS     _div_loop2		; less than divisor
 ._divide_by_zero
}



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

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

MACRO M_DIV top:R, bot:R, res:R	; top=top DIV bot  res=top MOD bot
{
 MOVS    R14, bot		; Put divisor in t
 MOV     res, #0		; Initialize quotient
 BEQ     _divide_by_zero
 CMP     R14, top, LSR #1	; double it until
 ._div_loop1
   MOVLS   R14, R14, LSL #1	; 2 * t>top
   CMP     R14, top, LSR #1
 BLS     _div_loop1

 ._div_loop2
   CMP     top, R14		; Can we subtract t?
   SUBCS   top, top, R14	; YES
   ADC     res, res, res	; Double res
   MOV     R14, R14, LSR #1	; Halve t
   CMP     R14, bot		; and loop until
 BHS     _div_loop2		; less than divisor
 ._divide_by_zero
 EXG     top, res
}


;MACRO U_DIV n:R, d:R, m:R	;n=n DIV d  m=n MOD d
;{
; RSB     d, d, #0		;negate the divisor
;
; MOV     m, #0			;setup the modulus
; MOVS    n, n, LSL #1
; #set    div_loop = div_jump
; #rept 31
;   BCS     div_loop		;jump out when carry set
;   MOVS    n, n, LSL #1		;n = n << 1 setting carry
;   #set    div_loop = div_loop + (3*4)
; #endr
;
;.div_jump
; #rept 32
;   ADCS    m, d, m, LSL #1	;m = m << 1 + d + carry
;   SUBCC   m, m, d		;if no carry, m -= d
;   ADCS    n, n, n		;n = n << 1 + carry
; #endr
;}
MACRO U_DIV top:R, bot:R, res:R	; top=top DIV bot  res=top MOD bot
{
 STMFD   R13!, {R14}
 MOVS    R14, bot		; Put divisor in t
 MOV     res, #0		; Initialize quotient
 BEQ     _divide_by_zero
 CMP     R14, top, LSR #1	; double it until
 ._div_loop1
   MOVLS   R14, R14, LSL #1	; 2 * t>top
   CMP     R14, top, LSR #1
 BLS     _div_loop1

 ._div_loop2
   CMP     top, R14		; Can we subtract t?
   SUBCS   top, top, R14	; YES
   ADC     res, res, res	; Double res
   MOV     R14, R14, LSR #1	; Halve t
   CMP     R14, bot		; and loop until
 BHS     _div_loop2		; less than divisor
 ._divide_by_zero
 EXG     top, res
 LDMFD   R13!, {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
; R12 - pointer to module private word
; R14 - offset from VARS of parameter to set (0 = OFF, &FF = ON)
; return address stacked

.Set_On_Off_Option
 LDR     R12, [R12, #0]
 TEQ     R1, #0				;is there a parameter?
 MOV     R1, R14
 BLNE    On_Off_Option			;YES, read On/Off option
					;NO, presume on
 STRB    R0, [R12, R1]
 SUBS    R0, R0, R0			;clear V
LDR     PC, [R13], #4




;hourglass_off
;-------------
;turns the hourglass off
;
;Entry:
; R12 - pointer to VARS

.hourglass_off
 STMFD   R13!, {R14}
 LDRB    R14, [R12, #prevent_Hourglass - VARS]
 TEQ     R14, #0			;are we controlling the hourglass?
 SWIEQ   XHourglass_Off			;YES
LDMFD   R13!, {PC}




;hourglass_on
;------------
;turns the hourglass on
;
;Entry:
; R12 - pointer to VARS

.hourglass_on
 STMFD   R13!, {R0, R14}
 LDRB    R14, [R12, #prevent_Hourglass - VARS]
 TEQ     R14, #0			;should we display an Hourglass?
 MOVEQ   R0, #10			;YES
 SWIEQ   XHourglass_Start		; |+ PRM2-751
LDMFD   R13!, {R0, PC}




;hourglass_percentage
;--------------------
;sets the hourglass percentage
;
;Entry:
; R12 - pointer to VARS
;
;Exit:
; R0 - capped to 99%

.hourglass_percentage
 STMFD   R13!, {R14}
 LDRB    R14, [R12, #prevent_Hourglass - VARS]
 TEQ     R14, #0			;should we display an Hourglass?
 LDMNEFD R13!, {PC}

 CMP     R0, #99
 MOVHI   R0, #99			;cap the percentage
 SWI     XHourglass_Percentage
LDMFD   R13!, {PC}




;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=privileged access 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
   #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

 #if MMUControl2_support == 0
   LDR     R0, [R12, #cpu_id - VARS]
   TEQ     R0, #CPUID_StrongARM				;StrongARM?
   BNE     _cacheops_not_required
   MOV     R2, #32*1024		;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=privileged access only)
   MOV     R4, #%010000010
   MOV     R5, R2					;max area size
   MOV     R6, #0					;DA handler pointer
   ADR     R8, _cacheops_name				;pointer to our DA name
   BL      _create_DA
   STR     R1, [R12, #cacheops_DA_number - VARS]	;our DA
   STR     R3, [R12, #cacheops_DA_address - VARS]	;our DA address
   STR     R3, [R12, #DCache_CleanBaseAddress - VARS]
   ADD     R3, R3, #16*1024
   STR     R3, [R12, #DCache_CleanBaseAddress+4 - VARS]
   ._cacheops_not_required
 #endif

 #if compile_RO == 400
   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=privileged access only)
   MOV     R4, #%010000010
   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
   TEMP    R7
   STR     R3, abt_stack				;stack
   LOCK    R7


   MRS     R4, CPSR				;R4=CPSR
   ORR     R0, R4, #%11 << 6			;disable IRQ/FIQ
   MSR     CPSR_c, R0
   NOP

   MOV     R0, #Vector_UndefinedInstr + (1<<8)	;claim vector
   ADR     R1, _Abort_Check
   SWI     XOS_ClaimProcessorVector		;PRM5a-30
   ORRVS   R4, R4, #1 << 28			;set V on error
   MSRVS   CPSR_cf, R4
   NOP						;fix for StrongARM errata
   LDMVSFD R13!, {R1-R8, PC}			;exit on error

   MRC     P8, 0, R0, C0, C0			;trigger Undefined32 vector
   TEQ     R5, #0				;setup UND32 stack?
   BLEQ    _create_Undefined_Stack		;YES
   MRC     P8, 0, R0, C0, C0			;trigger Undefined32 vector

   MOV     R0, #Vector_UndefinedInstr		;release vector
   ADR     R2, _Abort_Check
   SWI     XOS_ClaimProcessorVector		;PRM5a-30

   MSR     CPSR_c, R4
   NOP
 #endif
LDMFD   R13!, {R1-R8, PC}


#if compile_RO == 400
 ._Abort_Check
 TEQ     R13, #0			;is there a 32bit UND32 stack?
 LDREQ   R13, [R12, #und_stack - VARS]	;NO, use our stack
 MOV     R5, R13
 MOVS    PC, R14			;exit, skipping aborting instruction


._create_Undefined_Stack
 STMFD   R13!, {R14}
;                +----------- 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=privileged access only)
 MOV     R4, #%010000010
 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!, {PC}
#endif



 ._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 Trace Buffer", 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
 #if MMUControl2_support == 0
   ._cacheops_name		DCB "ADFFS Cache Ops", 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, R12			;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+1)	;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
 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

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




 B      _Create_Transient_DA
.DA_jump_table

 #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
 STR     R0, [R12, #reserve_DA - VARS]			;allow DA to be removed
 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
; don't release the undefined stack as its now imortal
;   LDR     R1, [R12, #undefined_stack_DA_number - VARS]
;   TEQ     R1, #0
;   BLNE    _release_DA
 #endif

 #if MMUControl2_support == 0
   LDR     R1, [R12, #cacheops_DA_number - VARS]
   TEQ     R1, #0
   BLNE    _release_DA
 #endif

 #if compile_RO >= 400
   BL      Remove_Transient_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
;-------------------
;Removes our transient DA's
;
;Entry:
; R12 - pointer to VARS

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

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

;Entry:
; R12 - pointer to VARS

.Dynamic_area_handler
 STMFD   R13!, {R0, R14}
 TEQ     R0, #2				;PreShrink PRM5a-46
 BEQ     _preshink
 TEQ     R0, #3				;PostShrink PRM5a-47
 TEQNE   R0, #1				;PostGrow PRM5a-46
 STREQ   R4, [R12, #DA_size - VARS]
LDMFD   R13!, {R0, PC}

 ._preshink
 LDRB    R14, [R12, #Type_Mounted - VARS]
 TEQ     R14, #Type_None		;is an image mounted?
 LDRNE   R14, [R12, #ADF_Buffer_Length - VARS]		;YES, get our minimum required size
 LDR     R0, [R12, #reserve_DA - VARS]			;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
LDMFD   R13!, {R0, PC}




;Reduce_DynamicArea
;------------------
;reduces our DA to a specific size, used after mounting APD's
;
;Entry:
; R0 - new buffer limit
; R_buffer - end address of used buffer
; R12 - pointer to VARS

.Reduce_DynamicArea
 STMFD   R13!, {R0-R2, R14}
 LDR     R0, [R12, #DA_address - VARS]
 SUB     R0, R_buffer, R0		;work out new DA size

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

 LDR     R2, [R12, #DA_size - VARS]	;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, [R12, #our_Floppy_DA_number - VARS]
 SWI     XOS_ChangeDynamicArea		;hand back memory
 SUB     R2, R2, R1
 STR     R2, [R12, #DA_size - VARS]	;update our DA size
LDMFD   R13!, {R0-R2, PC}




;entry:
; R12 - pointer to VARS

#if compile_RO > 311
.release_fscontrol_filename
 ADD     R0, R12, #fscontrol_filename - VARS
 B       release_RMA
#endif

.release_filename_recording
 ADD     R0, R12, #record_filename - VARS
 B       release_RMA

.release_filename_current
 ADD     R0, R12, #Current_File_Mounted - VARS




;release_filename
;================
;releases the RMA allocation held in [R0]

;entry:
; R0  - address that holds the filename pointer

.release_RMA
 STMFD   R13!, {R0, R2, R14}
 LDR     R2, [R0]
 TEQ     R2, #0				;do we have a filename already?
 MOVNE   R14, #0
 STRNE   R14, [R0]			;mark as released
 MOVNE   R0, #Module_Free		;free the RMA allocation
 SWINE   XOS_Module
LDMFD   R13!, {R0, R2, PC}




;allocate_filename
;=================
;allocated a buffer and canonicalises a filename (ie full path)

;entry:
; R0 - pointer to word to store new filename pointer
; R1 - filename
;exit:
; R0 - pointer to filename or error if VS

.allocate_filename
 STMFD   R13!, {R1-R6, R14}
 MOV     R6, R0				;preserve R0
 MOV     R0, #37			;Canonicalise path
 MOV     R2, #0				;how long is the path?
 MOV     R3, #0
 MOV     R4, #0
 MOV     R5, #0
 SWI     XOS_FSControl			;R5=-length less terminator

 RSBVC   R5, R5, #1			;R5=length+terminator
 MOVVC   R0, #Module_Claim
 MOVVC   R3, R5				;filename length
 SWIVC   XOS_Module			;claim space for the filename

 MOVVC   R0, R6				;preserve R0
 STRVC   R2, [R0]			;store the pointer

 MOVVC   R0, #37			;Canonicalise path
 MOVVC   R3, #0
 MOVVC   R4, #0
 SWIVC   XOS_FSControl			;R5=-length less terminator

 MOVVC   R0, R2				;R0=filename
LDMFD   R13!, {R1-R6, PC}




;entry:
; R0 - pointer to word to store new filename pointer
; R1 - filename
;exit:
; R10 - pointer to filename or error if VS
; R11 - pointer to end of filename

.allocate_copy_filename
 STMFD   R13!, {R1-R4, R14}
 MOV     R10, R0
 BL      release_RMA			;free if we already have a buffer

 MOV     R4, #0
 ._L1
   LDRB    R2, [R1, R4]
   ADD     R4, R4, #1
   TEQ     R2, #0
   TEQNE   R2, #13
 BNE     _L1				;R4=length of filename

 MOV     R0, #Module_Claim
 ADD     R3, R4, #6			;filename length, plus space for ".!Run<0>"
 SWI     XOS_Module			;claim space for the filename
 LDMVSFD R13!, {R1-R4, PC}
 STR     R2, [R10]
 ADD     R11, R2, R4

 MOV     R0, #0
 ._L2
   STRB    R0, [R2, R4]
   SUBS    R4, R4, #1
   LDRPLB  R0, [R1, R4]
 BPL     _L2

 MOV     R10, R2
LDMFD   R13!, {R1-R4, PC}




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

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

 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, 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     R7, [R12, #ADF_Buffer_Length - VARS]

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


 ._release_memory
 TEQ     R2, #0				;is buffer allocated?
 LDMEQFD R13!, {R1-R4, R7, 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, #Type_None
 STRB    R7, [R12, #Type_Mounted - VARS]	;mark drive as empty
 STR     R7, [R12, #JFD_Tracks - VARS]
 STR     R7, [R12, #JFD_Sectors - VARS]
B       _exit




;ZeroBlock
;---------
;Zero a range of memory
;
;Entry:
; R0 - start (inclusive)
; R1 - end (exclusive)
;Exit:
; registers preserved

.ZeroBlock
 CMP     R0, R1				;sanity check
 MOVHS   PC, R14

 STMFD   R13!, {R0-R1, R3-R4, R14}
 MOV     R3, #0
 MOV     R4, #0

 ._L1
   TST     R0, #%11			;align start to a word
   STRNEB  R3, [R0], #1
 BNE     _L1

 ._L2
   TST     R1, #%11			;align end to a word
   STRNEB  R3, [R1, #-1]!
 BNE     _L2

 TST     R0, #%100			;align start to double-word
 STRNE   R3, [R0], #4
 TST     R1, #%100			;align end to double-word
 STRNE   R3, [R1, #-4]!
 ._L3
   CMP     R0, R1
   STMLOIA R0!, {R3-R4}
 BLO     _L3
LDMFD   R13!, {R0-R1, R3-R4, PC}




;MemMove
;-------
;Move memory up or down, start and end must be word aligned
;size must be >8 and start must be word aligned
;
;Entry:
; R0 - source
; R1 - destination
; R2 - size
;Exit:
; registers preserved

.MemMove
 TEQ     R2, #0
 MOVEQ   PC, R14			;exit if bytes to copy is 0
 STMFD   R13!, {R0-R4, R14}

 ADD     R3, R0, R2
 CMP     R1, R0				;is the source inside the destination area?
 CMPHI   R3, R1
 BHI     _backward			;YES

 ._sL1
   TST     R0, #%11			;word align start
   LDRNEB  R3, [R0], #1
   STRNEB  R3, [R1], #1
   SUBNES  R2, R2, #1
 BNE     _sL1

 TST     R2, #%100			;align to double-word
 LDRNE   R3, [R0], #4
 STRNE   R3, [R1], #4
 SUBNE   R2, R2, #4
 ._L1
   SUBS    R2, R2, #8
   LDMHSIA R0!, {R3-R4}
   STMHSIA R1!, {R3-R4}
 BHI     _L1

 ADDNES  R2, R2, #8			;copy trailing bytes
 ._eL1
   LDRNEB  R3, [R0], #1
   STRNEB  R3, [R1], #1
   SUBNES  R2, R2, #1
 BNE     _eL1
LDMFD   R13!, {R0-R4, PC}

 ._backward				;need to copy backwards
 ADD     R0, R0, R2
 ADD     R1, R1, R2

 ._sL2
   TST     R0, #%11			;word align start
   LDRNEB  R3, [R0, #-1]!
   STRNEB  R3, [R1, #-1]!
   SUBNES  R2, R2, #1
 BNE     _sL2

 TST     R2, #4				;align to double-word
 LDRNE   R3, [R0, #-4]!
 STRNE   R3, [R1, #-4]!
 SUBNE   R2, R2, #4
 ._L2
   SUBS    R2, R2, #8
   LDMHSDB R0!, {R3-R4}
   STMHSDB R1!, {R3-R4}
 BHI     _L2

 ADDNES  R2, R2, #8			;copy trailing bytes
 ._eL2
   LDRNEB  R3, [R0, #-1]!
   STRNEB  R3, [R1, #-1]!
   SUBNES  R2, R2, #1
 BNE     _eL2
LDMFD   R13!, {R0-R4, PC}




;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




;skip_spaces_R1
;--------------
;skip spaces in string pointed to by R1

;Entry:
; R1 - string pointer

;Exit:
; R2 - first char that wasn't a SPACE
; CC if char<32

.skip_spaces_R1
   LDRB    R2, [R1], #1
   CMP     R2, #32
 BEQ     skip_spaces_R1
MOV     PC, R14




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

.get_domainID_address
 MOV     R3, R14
 #if compile_RO > 400
   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, [R12, #domainID - VARS]	;store domainID pointer
 #endif
 MVN     R0, #0
 STR     R0, [R12, #JIT_task_ID - VARS]
MOV     PC, R3




;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
 MOVNE   R0, #0
 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
; JIT task is mapped in

;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


.random_number_R0
  STMFD   R13!, {R10, R11, R14}
  ADR     R11, _seed
  LDMIA   R11, {R10, R11}
  MLA     R0, R10, R11, R11
  STR     R0, _seed
LDMFD   R13!, {R10, R11, PC}

._seed  DCD 123, 1664525
