;RO31_OS_ChangeDynamicArea
;-------------------------
;implements OS_ChangeDynamicArea on our area for RO3.1
;
;Entry:
;  R0 = area to alter
;  R1 = amount to move (signed)
; R11 - pointer to VARS
;
;Exit:
;  R1 = unsigned amount moved

.RO31_OS_ChangeDynamicArea
 LDR     R12, [R11, #RO31_Next_DynamicAreaNo - VARS]
 CMP     R0, #RO31_First_DynamicAreaNo-1	;below our first DA?
 CMPHI   R12, R0 			;or above our last DA?
 BLS     vector_handler_forward		;YES, pass to OS

 LDR     R12, [R11, #DA_mask - VARS]	;need to round the size to
 ADD     R1, R1, R12			;...the next page boundry
 BICS    R1, R1, R12			;R1 = amount of memory we want
 EXITSWIEQ ""				;exit if 0

 STMFD   R13!, {R0, R2-R10}
 ADD     R12, R11, #RO31_DA_table - VARS	;pointer to DA table
 SUB     R0, R0, #RO31_First_DynamicAreaNo
 ADD     R12, R12, R0, LSL #4		;R12 now points at DA table entry

 LDR     R0, [R12, #RO31_DA.end_address]	;check DA size limits
 LDR     R2, [R12, #RO31_DA.address]
 LDR     R3, [R12, #RO31_DA.max_size]
 SUB     R0, R0, R2			;R0 = DA size
 ADDS    R2, R0, R1			;below 0?
 RSBMI   R1, R0, #0			;YES, reduce to 0 instead
 CMP     R2, R3				;above the limit?
 RSBGT   R1, R0, R3			;YES, cap to limit

 MOVS    R8, R1				;R8=delta change to DA
 EXITSWIEQ "R0, R2-R10"			;exit if no change

					;*****************************
					;check if the WIMP is running

 MOV     R0, #WimpSysInfo_ActiveTasks	;get number of WIMP tasks
 SWI     XWimp_ReadSysInfo		;PRM3-216
 TEQ     R0, #0				;R0>0 if WIMP is active
 STMNEFD R13!, {R11, R12}		;YES, preserve R11,R12 on stack
 BNE     _WIMP_active			; |+  and go to WIMP handler
					;*****************************
					;WIMP isn't running
 MOV     R0, #CAOPointer		;get the Current Active Object
 MOV     R1, #0
 SWI     XOS_ChangeEnvironment		;PRM1-320
 MOVVC   R2, R1				;R1=CAO
 MOVVC   R0, R8				;amount app space will change by
 MOVVC   R1, #Service_Memory		;PRM3-66
 SWIVC   XOS_ServiceCall		;PRM1-254
 BVS     _failed_memory_request		;exit on error
 TEQ     R1, #0				;blocked?
 BNE     _not_under_WIMP		;NO, continue
 ADR     R0, error_appmem_in_use	;YES, report error
 MOV     R8, #0				; |  no bytes moved
 B       _error_exit			; |+ exit

 ._WIMP_Entry				;the WIMP will enter here after
					;its setup memory ready for a move
 ADD     R13, R13, #&10			;clear stack of registers added
					;by OS SWI re-entry, they're not needed
 LDMFD   R13!, {R11, R12}		;restore R11,R12 from stack
 MOV     R8, R1				;R1 on entry contains amount to move

 ._not_under_WIMP
 MOV     R0, #MemLimit
 MOV     R1, #0				;read MemLimit
 SWI     XOS_ChangeEnvironment		;PRM1-320
 BVS     _failed_memory_request		;exit on error
 MOV     R9, R1				;R9 = End of Application space
 SUBS    R10, R1, R8			;R10 = new memlimit
 BMI     _failed_memory_request		;oops, not enough memory
 LDR     R0, [R11, #RO31_DynamicAreaAddress - VARS]
 CMP     R10, R0			;does Appspace overlap our DA?
 ADDHI   R9, R0, R8			;YES, cap Appspace

 MOV     R0, #ApplicationSpaceSize
 MOV     R1, #0				;read application space
 SWI     XOS_ChangeEnvironment		;PRM1-320
 BVS     _failed_memory_request		;exit on error
 SUBS    R6, R1, R8			;R6 = new appspacelimit
 BMI     _failed_memory_request		;oops, not enough memory

 STR     R8, [R13, #-4]!		;preserve R8
 BL      _change_our_DA			;increase DA size
 LDR     R7, [R13], #4			;preserve R8
 BVS     _failed_memory_request
 SUB     R7, R7, R8			;R7 = corrected R8

 ADD     R1, R10, R8			;correct memlimit
 LDR     R10, [R11, #RO31_DynamicAreaAddress - VARS]
 CMP     R1, R10			;does memlimit overlap our DA?
 MOVHI   R1, R10			;YES, cap memlimit
 MOV     R0, #MemLimit
 SWI     XOS_ChangeEnvironment		;PRM1-320
 BVS     _exit_error
 MOV     R0, #ApplicationSpaceSize
 ADD     R1, R6, R8			;correct application space
 CMP     R1, R10			;does Appspace overlap our DA?
 MOVHI   R1, R10			;Yes, cap Appspace
 SWI     XOS_ChangeEnvironment		;PRM1-320
 MOVVC   R0, #0				;no error
 ._exit_error
 ADRVS   R0, error_DA_failed		;error

 MOV     R8, R7				;R8=bytes moved
 ._exit
 MOV     R1, #Service_MemoryMoved	;PRM1-367
 SWI     XOS_ServiceCall		;PRM1-254

 LDR     R5, [R11, #WIMP_reentry - VARS]
 TEQ     R5, #0				;were we called by the WIMP?
 LDRNE   R4, [R11, #WIMP_OScopy_ChangeDynamic - VARS]	;YES, put it's original copy of
 STRNE   R5, [R4]			;..OS_ChangeDynamic back
 MOVNE   R5, #0				;and clear our semaphore
 STRNE   R5, [R11, #WIMP_reentry - VARS]

 MOVS    R1, R8				;exit with number of bytes moved
 RSBMI   R1, R1, #0			;ensuring it's positive

 TEQ     R0, #0				;did an error occur?
 LDMEQFD R13!, {R0, R2-R10}		;NO, exit cleanly
 BEQ     vector_handler_trap

 ._error_exit
 ADD     R13, R13, #4			;drop R0 from stack
 LDMFD   R13!, {R2-R10}			;clear down stack
B       vector_handler_trap_VS


 ._failed_memory_request
 ADR     R0, error_DA_failed		;error block
 MOV     R8, #0				;no bytes moved
B       _exit





;Entry:
; R8 - amount of memory to allocate (signed)

 ._WIMPModule	DCB "WindowManager", 0
		ALIGN
 ._WIMP_var1	DCD &E8BD403F		;LDMFD   R13!,{R0-R5,R14}


._WIMP_active
 MOV     R0, #Module_Lookup		;find WIMP module
 ADR     R1, _WIMPModule
 SWI     XOS_Module			;PRM1-249
 MOVVS   R1, #0
 BVS     _error_exit			;exit if we can't find module
					;R3 = WIMP CAO
					;R4 = WIMP private area

 ;****************************************************************
 ;search WindowManager to find the address offsets in its private
 ;space by looking for unique instructions
 ;****************************************************************

 MOV     R2, R3				;R2=module code start
 LDR     R0, _WIMP_var1			;find freepoolbase
 ._WIMP_L1
   LDR     R1, [R2], #4
   TEQ     R1, R0
 BNE     _WIMP_L1

 MOV     R6, #&FFF			;mask for offset in instruction
 LDR     R1, [R2]			;load the instruction
 AND     R6, R1, R6			;R6 = offset for OScopy_ChangeDynamic

 ADD     R4, R4, R6			;now swap the WIMP's redirect for
 STR     R4, [R11, #WIMP_OScopy_ChangeDynamic - VARS]	;OS_ChangeDynamic to our code
 LDR     R5, [R4]
 STR     R5, [R11, #WIMP_reentry - VARS]
 ADR     R0, _WIMP_Entry
 STR     R0, [R4]

 ADR     R14, _P2			;setup return address
 MOV     R0, PC
 AND     R0, R0, #&FC000003		;26bit, use current CPU mode
 ORR     R14, R14, R0

 MOV     R1, R8
 LDR     R0, OS_SWI_Vector		;let SWI run its course
 MOV     PC, R0
 SWI     XOS_ChangeDynamicArea		;not called, used to fool RISC OS

 ._P2
B       call_OS_Exit			;will never get here




;Entry:
;
; R8 - size to alter DA (signed)
; R9 - end of application space
; R11 - pointer to VARS
; R12 - pointer to DA table entry
;
;Exit:
; R0-R2, R5, R7, R9 - corrupt
; R3, R4, R6 - preserved
; R8 - >0 if unable to complete request
; R10-R12 - preserved

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

 TEQP    PC, #3 + (1<<27)		;SVC IRQ disabled
 NOP

 LDR     R0, [R12, #RO31_DA.end_address]	;end of our DA
 LDR     R7, [R11, #current_PageSize - VARS]	;page size
 MOV     R2, R8
 CMP     R8, #0				;increase or decrease DA?
 SUBMI   R5, R0, R7			;decrease, page to find in our DA
 SUBPL   R5, R9, R7			;increase, page to find in free slot
 MOVPL   R9, R0				;|+ end of our DA

 ADD     R0, R11, #RO31_memmap_entry - VARS	;mem block entry
 ._L1
   STR     R5, [R0, #4]			;block to find
   SWI     XOS_FindMemMapEntries	;PRM1-398
   BVS     _map_our_DA_error		;exit on error
   LDR     R1, [R0]
   CMN     R1, #1			;was the page found?
   BEQ     _map_our_DA_error		;NO, report error

   STR     R9, [R0, #4]			;address to map block to
   MOV     R1 , #0
   STR     R1, [R0, #8]			;make page unavailable in user mode
   SWI     XOS_SetMemMapEntries		;PRM1-394
   BVS     _map_our_DA_error		;exit on error

   ADD     R9, R9, R7			;increase address to move to
   CMP     R2, #0			;increase or decrease?
   STRPL   R9, [R12, #RO31_DA.end_address]	;increase, store new DA size
   SUBPLS  R8, R8, R7			;|+ reduce memory to grab
   STRMI   R5, [R12, #RO31_DA.end_address]	;decrease, store new DA size
   ADDMIS  R8, R8, R7			;|+ reduce memory to grab
   SUB     R5, R5, R7			;decrease page to find
 BNE      _L1
LDMFD   R13!, {PC}^			;exit re-enabling IRQ's


._map_our_DA_error
 ADR     R0, error_DA_failed		;error block
 LDR     R14, [R13], #4
ORRS    PC, R14, #1 << 28		;set V and exit
