;
; dynArea.s
;
; The handling of the dynamic area itself
;
;  1994-1998 Straylight
;
;----- Licensing note -------------------------------------------------------
;
; This file is part of Straylight's Dynamite
;
; Dynamite is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2, or (at your option)
; any later version.
;
; Dynamite is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with Dynamite.  If not, write to the Free Software Foundation,
; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


;----- Standard header ------------------------------------------------------

		GET	libs:header
		GET	libs:swis

		GET	libs:stream

;----- External dependencies ------------------------------------------------

		GET	sh.wSpace

;----- Main code ------------------------------------------------------------

		AREA	|Dynamite$$Code|,CODE,READONLY

; --- da_findPage ---
;
; On entry:	R2 == address of the page
;
; On exit:	R0 == page number
;
; Use:		Finds the page number of the page with the address given.

		EXPORT	da_findPage
da_findPage	ROUT

		STMFD	R13!,{R1,R7-R9,R14}	;Stack the link
		LDR	R14,dyn_machine		;Load the machine type
		CMP	R14,#&A3		;RISC OS 3?
		BCS	%10da_findPage		;Yes -- jump ahead then

		LDR	R9,dyn_pageCount	;Load number of pages-1
		MOV	R14,#&164		;Find CAM map (RO 2 and 3!)
		ADD	R9,R14,R9,LSL #2	;Last valid entry
		MOV	R8,R2,LSL #4		;The address of interest << 4
		SUB	R7,R14,#4		;We're pre-indexing
00da_findPage	CMP	R7,R9			;Have we finished?
		LDRNE	R1,[R7,#4]!		;Load out the entry
		TEQNE	R8,R1,LSL #4		;Is this a match?
		BNE	%00da_findPage		;No -- keep on looking
		TEQ	R8,R1,LSL #4		;Was that a match?
		SUBEQ	R7,R7,R14		;Yes -- get offset from base
		MOVEQ	R0,R7,LSR #2		;And turn into page number
		MOVNE	R0,#-1			;Otherwise return -1
		LDMFD	R13!,{R1,R7-R9,PC}^	;Return to caller

		; --- We are on RISC OS 3 ---

10da_findPage	MOV	R1,#0			;Start at this page
		MOV	R14,#-1			;Get a terminator
		STMDB	R13!,{R1,R2,R3,R14}	;Store that in block
		MOV	R0,R13			;Point to the block
		SWI	OS_FindMemMapEntries	;Find the page number
		LDR	R0,[R13],#16		;Load the page number
		LDMFD	R13!,{R1,R7-R9,PC}^	;Return to caller

		LTORG

; --- da_addPages ---
;
; On entry:	R0 == number of pages to add
;		R12 == workspace address
;
; On exit:	Possible error returned
;
; Use:		Increases the size of the dynamic area by the number of
;		pages given in R0

		EXPORT	da_addPages
da_addPages	ROUT

		STMFD	R13!,{R0-R9,R14}	;Save some registers

		LDR	R9,dyn_machine		;What machine are we on?
		CMP	R9,#&A5			;Is it a RISC PC?
		BGE	%50da_addPages		;Yes -- do things differently

		MOVS	R9,R0			;Remember this value
		BEQ	%10da_addPages		;Nothing to do -- return
		LDR	R14,dyn_sprSize		;Get address of spr area size
		LDR	R14,[R14]		;Get the size out
		LDR	R1,dyn_areaSize		;Find our area size
		SUB	R4,R14,R1		;Find top of system sprites
		ADD	R2,R4,#&01400000	;Top of sprite area

		LDR	R3,dyn_log2PageSize	;Find page size of machine
		MOV	R5,R9,LSL R3		;Find size in bytes
		LDR	R3,dyn_pageSize		;Find real page size
		MOV	R1,R5			;We want this in R1 too
		MOV	R0,#3			;Change sprite area size
		SWI	XOS_ChangeDynamicArea	;Yes... do it now!
		BVS	%99da_addPages		;Error -- return

		; --- Make sure system sprite size remains the same ---

		MOV	R14,#&01400000		;Get address of sprite area
		STR	R4,[R14,#0]		;Get system sprite size

		; --- Work out how big dynamite area is now ---

		LDR	R14,dyn_areaSize	;Get the previous size
		RSB	R8,R14,#&01800000	;Put new blocks here
		ADD	R14,R14,R5		;The new size
		STR	R14,dyn_areaSize	;Save this size back

		; --- Now we need to map up the area ---

		SUB	R13,R13,#16		;Get me a mem map block
		MOV	R14,#0			;A nice 0 thing
		STR	R14,[R13,#8]		;Protection level
		MOV	R14,#-1			;The terminator
		STR	R14,[R13,#12]		;Put that in the block
00da_addPages	SUB	R8,R8,R3		;Next page goes here
		BL	da_findPage		;Find page number of R2
		STR	R0,[R13,#0]		;Store the page number
		STR	R8,[R13,#4]		;Put that page here please
		MOV	R0,R13			;Point to the block
		SWI	XOS_SetMemMapEntries	;Set mem map entries
		SUBS	R9,R9,#1		;Reduce page count
		ADD	R2,R2,R3		;Now move next page
		BGT	%00da_addPages		;Keep doing that then
		ADD	R13,R13,#16		;Get the block back

		; --- Phew! -- almost there ---

		CMP	R4,#0			;Is there a sprite area?
		MOVNE	R4,#&01400000		;Yes -- get its address
		MOV	R14,#&1000		;VDU driver workspace
		STR	R4,[R14,#1364]		;And store as sprite area ptr

10da_addPages	LDMFD	R13!,{R0-R9,PC}^	;Return to caller

		; --- We are on a RISC PC ---

50da_addPages	LDR	R1,dyn_log2PageSize	;Get the log 2 page size
		MOV	R1,R0,LSL R1		;Work out increment
		LDR	R0,dyn_areaHandle	;Load the area handle
		SWI	XOS_ChangeDynamicArea	;And increase appropriately
		LDRVC	R14,dyn_areaSize	;Load the old area size
		ADDVC	R14,R14,R1		;Apply our increment to it
		STRVC	R14,dyn_areaSize	;And save it back again
		LDMVCFD	R13!,{R0-R9,PC}^	;Return to caller

99da_addPages	LDMFD	R13!,{R0-R9,R14}	;Load back registers
		ADR	R0,da__noMem		;Point to error
		ORRS	PC,R14,#V_flag		;Return with error

da__noMem	DCD	1
		DCB	"No pages left",0

; --- da_removePages ---
;
; On entry:	R0 == number of pages to remove
;
; On exit:	--
;
; Use:		Removes the given number of pages from the dynamite
;		area. If the number of pages to remove is greater than
;		the actual number of pages allocated, as many as possible
;		are removed

		EXPORT	da_removePages
da_removePages	ROUT

		STMFD	R13!,{R0-R10,R14}	;Stack some registers

		LDR	R9,dyn_machine		;What machine are we on?
		CMP	R9,#&A5			;Is it a RISC PC?
		BGE	%50da_removePages	;Yes -- do things differently

		MOV	R9,R0			;Remember this value
		LDR	R8,dyn_areaSize		;Get my area size
		RSB	R2,R8,#&01800000	;The lowest page

		LDR	R5,dyn_log2PageSize	;Get the page size
		MOV	R7,R8,LSR R5		;Number of dynamite pages
		CMP	R9,R7			;Are we in range?
		MOVGT	R9,R7			;No -- we are now
		CMP	R9,#0			;Are we removing 0 pages?
		BEQ	%99da_removePages	;Yes -- return now

		LDR	R14,dyn_sprSize		;Get address of spr area size
		LDR	R14,[R14]		;Get the size out
		LDR	R1,dyn_areaSize		;Find our area size
		SUB	R10,R14,R1		;Find top of system sprites
		ADD	R6,R10,#&01400000	;Top of sprite area

		SUB	R14,R8,R9,LSL R5	;The new dynamite size
		STR	R14,dyn_areaSize	;Store this away nicely

		; --- Now we need to map down the area ---

		LDR	R3,dyn_pageSize		;Get the page size
		MOV	R4,R9			;Look after the page count
		SUB	R13,R13,#16		;Get me a mem map block
		MOV	R14,#0			;A nice 0 thing
		STR	R14,[R13,#8]		;Protection level
		MOV	R14,#-1			;The terminator
		STR	R14,[R13,#12]		;Put that in the block
00		BL	da_findPage		;Find page number of R2
		STR	R0,[R13,#0]		;Store the page number
		STR	R6,[R13,#4]		;Put that page here please
		MOV	R0,R13			;Point to the block
		SWI	XOS_SetMemMapEntries	;Set mem map entries
		ADD	R2,R2,R3		;Now move next page
		ADD	R6,R6,R3		;Put the next one here
		SUBS	R9,R9,#1		;Reduce page count
		BGT	%00da_removePages	;Keep doing that then
		ADD	R13,R13,#16		;Get the block back

		CMP	R10,#0			;Was there an area before
		BNE	%10da_removePages	;Yes -- all ok then
		LDR	R6,dyn_sprSize		;Get address of spr area size
		LDR	R6,[R6]			;Get the size out
		MOV	R7,#0			;Sprite area header
		MOV	R8,#16
		MOV	R9,#16
		MOV	R2,#&1400000		;Point to sprite area
		STMIA	R2,{R6-R9}		;Store the header

10		MOV	R1,R4,LSL R5		;We reduce by this amount
		RSB	R1,R1,#0		;Make it negative
		MOV	R0,#3			;Change sprite area size
		SWI	XOS_ChangeDynamicArea	;Remove those damn pages
		B	%99da_removePages	;Return to caller

		; --- We're on a RISC PC ---

50		LDR	R1,dyn_log2PageSize	;Get the log 2 page size
		MOV	R1,R0,LSL R1		;Work out increment
		RSB	R1,R1,#0		;Negate it nicely
		LDR	R0,dyn_areaHandle	;Load the area handle
		SWI	XOS_ChangeDynamicArea	;And increase appropriately
		LDR	R14,dyn_areaSize	;Load the current size
		SUB	R14,R14,R1		;How much was it altered by?
		STR	R14,dyn_areaSize	;And save that back

99		LDMFD	R13!,{R0-R10,PC}^	;Return to caller

		LTORG


; --- da_readSize ---
;
; On entry:	--
;
; On exit:	R0 == size of area
;
; Use:		Returns the size of the sprite area (including the
;		dynamite area).

		EXPORT	da_readSize
da_readSize	ROUT

		LDR	R0,dyn_machine		;What machine are we on?
		CMP	R0,#&A5			;Is it a RISC PC?
		BGE	%50da_readSize		;Yes -- do other things

		LDR	R0,dyn_sprSize		;Get spr area size pointer
		LDR	R0,[R0,#0]		;Load out the size
		MOVS	PC,R14			;And return to caller

		; --- We're on a RISC PC ---

50da_readSize	STMFD	R13!,{R1,R14}		;Stack some registers
		MOV	R0,#3			;Read size of sprite area
		SWI	XOS_ReadDynamicArea	;Read things out then
		MOV	R0,R1			;Put size in R0
		LDMFD	R13!,{R1,PC}^		;Return to caller

		LTORG

; --- da_describe ---
;
; On entry:	--
;
; On exit:	R0 == dynamic area number (3 == sprite area)
;		R1 == size of area
;		R2 == total free in area
;
; Use:		Gives back som information on the dynamite area.

		EXPORT	da_describe
da_describe	ROUT

		LDR	R0,dyn_machine		;Load out the machine type
		CMP	R0,#&A5			;Is it a RISC PC?
		LDRGE	R0,dyn_areaHandle	;Yes -- load dynamic area hnd
		MOVLT	R0,#-1			;No -- return a silly value
		LDR	R1,dyn_areaSize		;Put area size in R1
		LDR	R2,dyn_heapSize		;And heap size in R2
		SUB	R2,R1,R2		;Get size of unused area
		MOVS	PC,R14			;Return to caller

		LTORG

;----- That's all, folks ----------------------------------------------------

		END
