;
; chunk.s
;
; Loading and management of options chunks (MDW)
;
;  1995-1998 Straylight
;

;----- Licensing note -------------------------------------------------------
;
; This file is part of Straylight's Sapphire library.
;
; Sapphire 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.
;
; Sapphire 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 Sapphire.  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	sapphire:alloc
		GET	sapphire:fastMove
		GET	sapphire:flex
		GET	sapphire:string

		GET	sapphire:xfer.xsave

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

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- chunk_create ---
;
; On entry:	--
;
; On exit:	R0 == chunk file handle
;		May return an error
;
; Use:		Creates a new chunk file structure and returns a handle to
;		it.

		EXPORT	chunk_create
chunk_create	ROUT

		STMFD	R13!,{R14}		;Save a register
		MOV	R0,#cBase__size		;Get the size of the block
		BL	alloc			;Try to allocate the block
		BLCS	alloc_error		;If it failed, get error
		MOVCC	R14,#0			;Otherwise make list empty
		STRCC	R14,[R0,#cBase__list]	;By clearing the link
		LDMFD	R13!,{R14}		;Restore link register
		BICCCS	PC,R14,#V_flag		;If OK, return without error
		ORRCSS	PC,R14,#V_flag		;Else return the error

		LTORG

; --- chunk_destroy ---
;
; On entry:	R0 == chunk file handle
;
; On exit:	--
;
; Use:		Removes a chunk file structure from memory.  Chunk data in
;		flex blocks is freed; chunk claimers who free flex blocks
;		should clear the anchors to 0.

		EXPORT	chunk_destroy
chunk_destroy	ROUT

		STMFD	R13!,{R0-R2,R14}	;Save some registers
		MOV	R2,R0			;Look after the chunk base
		LDR	R1,[R2,#0]		;Load the first chunk block

00chunk_destroy	MOVS	R0,R1			;Is this the end of the list?
		BEQ	%f00chunk_destroy	;Yes -- stop the loop
		LDR	R14,[R0,#0]		;Load the flex anchor
		CMP	R14,#0			;Is there a block here?
		BLNE	flex_free		;Yes -- free it then
		LDR	R1,[R0,#cLink__next]	;Load the next block
		BL	free			;Free this block
		B	%b00chunk_destroy	;And loop back

00chunk_destroy	MOV	R0,R2			;Point to the chunk base
		BL	free			;Don't need that any more
		LDMFD	R13!,{R0-R2,PC}^	;Return to caller

		LTORG

; --- chunk_claim ---
;
; On entry:	R0 == chunk file handle
;		R1 == pointer to chunk name
;		R2 == pointer to saver routine, or 0 for none, or -1 for no
;			change
;		R3 == R10 value to pass to saver
;		R4 == R12 value to pass to saver
;
; On exit:	R1 == chunk handle/anchor
;		CS if the chunk already existed
;		May return an error
;
; Use:		Claims a chunk, installing a save handler for it.  The chunk
;		handle returned is actually the address of a flex anchor for
;		the chunk's data (use flex_size to find the block's size).
;		If the save handle is 0, the data in the flex block (which
;		may be modified) is saved directly from the block.  Otherwise
;		the save routine is expected to save its data, using xsave.
;
;		The anchor is followed by 3 unused words -- you can use them
;		for whatever you want.
;
;		Warning: this routine may move flex blocks.

		EXPORT	chunk_claim
chunk_claim	ROUT

		BIC	R14,R14,#V_flag		;Clear V and hope...
		STMFD	R13!,{R0,R5,R6,R14}	;Save some registers
		SUB	R5,R0,#cLink__next	;Look after the handle

00chunk_claim	LDR	R6,[R5,#cLink__next]	;Load the next pointer
		CMP	R6,#0			;Is this the very end?
		BEQ	%f00chunk_claim		;Yes -- create a new chunk
		ADD	R0,R6,#cLink__name	;Point to the chunk's name
		BL	str_icmp		;Compare the names
		MOVNE	R5,R6			;No match -- move on
		BNE	%b00chunk_claim		;Loop back to start

		; --- Found a matching chunk ---

		CMP	R2,#-1			;Do we change the saver?
		ADDNE	R14,R6,#cLink__saver	;No -- point to saver info
		STMNEIA	R14,{R2-R4}		;And stuff the values in
		MOV	R1,R6			;Point to the anchor
		LDMFD	R13!,{R0,R5,R6,R14}	;Restore registers
		ORRS	PC,R14,#C_flag		;And return to caller

		; --- No match -- create a new chunk ---

00chunk_claim	MOV	R0,#cLink__size		;Get the block size I need
		BL	alloc			;Try to allocate the memory
		BLCS	alloc_error		;If it failed, get error
		BCS	%99chunk_claim		;And return the failure
		MOV	R6,R0			;Get the chunk handle
		ADD	R0,R6,#cLink__name	;Point to the name field
		BL	str_cpy			;Fill in the new name

		; --- Allocate an empty flex block for it ---

		MOV	R1,#0			;Allocate no memory
		MOV	R0,R6			;Point to the anchor
		BL	flex_alloc		;Allocate the block
		BLCS	alloc_error		;If it failed, get error
		BCS	%98chunk_claim		;And tidy up on the way out

		; --- Fill in the rest of the chunk ---

		MOV	R14,#0			;Fill in the next field
		STR	R14,[R6,#cLink__next]	;Clear the next pointer
		STR	R6,[R5,#cLink__next]	;And link in the new chunk
		STR	R14,[R6,#cLink__flags]	;Clear flags initially
		CMP	R2,#-1			;Do we change the saver?
		MOVEQ	R2,#0			;No -- then leave none there
		ADD	R14,R6,#cLink__saver	;Point to saver info
		STMIA	R14,{R2-R4}		;And stuff the values in

		MOV	R1,R6			;Point to the anchor
		LDMFD	R13!,{R0,R5,R6,R14}	;Restore registers
		BICS	PC,R14,#C_flag		;And return to caller

		; --- Tidy up on the way out ---

98chunk_claim	MOV	R5,R0			;Look after the error
		MOV	R0,R6			;Point to new chunk block
		BL	free			;Free the block
		MOV	R0,R5			;Get the error pointer back

99chunk_claim	ADD	R13,R13,#4		;Don't restore R0 on exit
		LDMFD	R13!,{R5,R6,R14}	;Restore registers
		ORRS	PC,R14,#V_flag		;And return the error

		LTORG

; --- chunk_makeBinary ---
;
; On entry:	R0 == chunk file handle
;		R1 == chunk handle
;
; On exit:	--
;
; Use:		Marks a given chunk as containing binary data.

		EXPORT	chunk_makeBinary
chunk_makeBinary ROUT

		STMFD	R13!,{R14}		;Save a register
		LDR	R14,[R1,#cLink__flags]	;Load the flags word
		ORR	R14,R14,#clFlag__binary	;Set the binary flag
		STR	R14,[R1,#cLink__flags]	;Save the flags back again
		LDMFD	R13!,{PC}^		;And return to caller

		LTORG

; --- chunk_read ---
;
; On entry:	R0 == chunk file handle
;		R1 == address of a flex anchor
;
; On exit:	May return an error
;
; Use:		Merges the data contained in the flex block with that already
;		in the chunk file.  If the chunk file is empty, this is
;		equivalent to a load.  Data from the flex block is appended
;		to chunks already loaded where appropriate.
;
;		Warning: this routine may move flex blocks.

		EXPORT	chunk_read
chunk_read	ROUT

		STMFD	R13!,{R0-R9,R14}	;Save some registers
		LDR	R8,[R1,#0]		;Load the block address
		MOV	R7,R0			;Look after the chunk handle
		MOV	R0,R1			;Point to the anchor
		BL	flex_size		;Read the block's size
		ADD	R9,R8,R0		;And find the limit pointer

		; --- Find the first chunk ---
		;
		; There'll probably be some comments at the top -- we don't
		; care about them.

10chunk_read	MOV	R2,#&0A			;Say this is a new line
00chunk_read	CMP	R8,R9			;Have we reached the end?
		BCS	%90chunk_read		;Yes -- nothing to read
		LDRB	R1,[R8],#1		;Load the next byte
		CMP	R2,#&0A			;Is this a new line?
		CMPEQ	R1,#'['			;And is this a chunk start?
		MOVNE	R2,R1			;No -- stash previous char
		BNE	%b00chunk_read		;And skip back for more

		; --- Read the next chunk ---

		MOV	R2,R11			;Copy name to scratchpad
00chunk_read	CMP	R8,R9			;Reached the end yet?
		BCS	%90chunk_read		;Yes -- ignore bogus header
		LDRB	R1,[R8],#1		;Get a byte of name
		CMP	R1,#']'			;Is it the end of it?
		MOVEQ	R1,#0			;Yes -- terminate the name
		STRB	R1,[R2],#1		;Store this byte away
		BNE	%b00chunk_read		;Keep on going to the end
		STRB	R1,[R11,#19]		;Make sure name's <12 chars

		; --- Ignore the rest of this line ---

		MOV	R3,#0			;Clear size just in case
00chunk_read	CMP	R8,R9			;Reached the end yet?
		BCS	%f05chunk_read		;Yes -- stop here then
		LDRB	R1,[R8],#1		;Load the next byte out
		CMP	R1,#&0A			;Is this the end of the line?
		BNE	%b00chunk_read		;No -- skip back then

		; --- Work out how big the chunk is ---

		AND	R0,R8,#3		;Get the non-word-alignedness
		BIC	R1,R8,#3		;And round down nicely
		LDMIA	R1,{R2-R4}		;Load next two fullwords
		MOVS	R0,R0,LSL #3		;Turn bytes into bits
		RSB	R14,R0,#32		;And work out other shift
		MOVNE	R2,R2,LSR R0		;Shift down lowest word
		ORRNE	R2,R2,R3,LSL R14	;Copy in bottom of next word
		MOVNE	R3,R3,LSR R0		;Shift rest of this one down
		ORRNE	R3,R3,R4,LSL R14	;Copy in bottom of top word

		LDR	R14,=chunk__binMark	;Is this the binary marker?
		CMP	R2,R14			;Is this a binary chunk?
		MOVNE	R5,#0			;No -- don't set any flags
		MOVEQ	R5,#clFlag__binary	;Yes -- set the binary flag
		BNE	%f00chunk_read		;No -- search for next chunk

		ADD	R8,R8,#8		;Skip past those two words
		ADD	R8,R8,R3		;And move on to end of chunk
		B	%f05chunk_read		;And skip on (size in R3)

		; --- Now search for a new chunk ---

00chunk_read	MOV	R3,#0			;No bytes counted so far
00chunk_read	CMP	R8,R9			;Reached the end yet?
		BCS	%f05chunk_read		;Yes -- stop here then
		LDRB	R0,[R8],#1		;Load the next byte
		CMP	R1,#&0A			;Is this a new line?
		CMPEQ	R0,#'['			;And is this a new chunk?
		ADDNE	R3,R3,#1		;No -- bump the counter
		MOVNE	R1,R0			;Remember the last character
		BNE	%b00chunk_read		;And go back found

		SUB	R8,R8,#1		;Go back to the newline char

		; --- Now claim a chunk for us ---

05chunk_read	MOV	R0,R7			;Get the chunk handle
		MOV	R1,R11			;Point to the chunk's name
		MOV	R2,#-1			;Don't care about savers
		FSAVE	"R8,R9"			;Stash flex pointers
		BL	chunk_claim		;Claim this block please
		BVS	%99chunk_read		;It failed -- tidy up
		MOV	R6,R1			;Look after the link block

		LDR	R14,[R6,#cLink__flags]	;Load the flags word
		ORR	R14,R14,R5		;Set any flags we need
		STR	R14,[R6,#cLink__flags]	;Save the flags back again

		; --- Grow the flex block and copy ---

		MOV	R0,R6			;Point to the anchor
		BL	flex_size		;Read the block's size
		MOV	R5,R0			;Look after this
		ADD	R1,R0,R3		;Add on a little size
		MOV	R0,R6			;Point to the anchor again
		BL	flex_extend		;Make the block bigger
		BLCS	alloc_error		;If it failed, moan a lot
		BCS	%99chunk_read		;And go off in a huff

		LDR	R0,[R6,#0]		;Load the block base
		ADD	R0,R0,R5		;Find the destination addr
		FLOAD	"R8,R9"			;Reload my saved pointers
		SUB	R1,R8,R3		;Reclaim my data
		MOV	R2,R3			;Set the amount to copy
		BL	fastMove		;And copy it all across

		; --- That's done -- now do the rest ---

		B	%10chunk_read		;Go back to the top again

		; --- We made it -- huzzah! ---

90chunk_read	LDMFD	R13!,{R0-R9,R14}	;Restore registers
		BICS	PC,R14,#V_flag		;And return with no error

		; --- Something went awry ---

99chunk_read
		FLOAD	"R8,R9"			;Restore regs from flex stack
		ADD	R13,R13,#4		;Don't restore R0 on exit
		LDMFD	R13!,{R1-R9,R14}	;Restore registers
		ORRS	PC,R14,#V_flag		;And return the error

		LTORG

; --- chunk_enum ---
;
; On entry:	R0 == chunk file handle
;		R1 == 0 for first call or continuation value
;
; On exit:	CC if this isn't over yet, and
;		  R1 == continuation value for next call
;		  R2 == pointer to chunk name
;		else CS and
;		  R1 == 0
;		  R2 preserved
;
; Use:		Allows you to enumerate the chunks in a chunk file structure.

		EXPORT	chunk_enum
chunk_enum	ROUT

		CMP	R1,#0			;Is this a new call?
		SUBEQ	R1,R0,#cLink__next	;Yes -- set up continuation
		LDR	R1,[R1,#cLink__next]	;Move onto the next block
		CMP	R1,#0			;Is this at the end?
		ADDNE	R2,R1,#cLink__name	;No -- point to the name
		BICNES	PC,R14,#C_flag		;And return OK
		ORREQS	PC,R14,#C_flag		;Otherwise say it's over

		LTORG

; --- chunk_save ---
;
; On entry:	R0 == chunk file handle
;
; On exit:	May return an error
;
; Use:		Saves a chunk file to xsave's current output.

		EXPORT	chunk_save
chunk_save	ROUT

		STMFD	R13!,{R0-R4,R10,R12,R14} ;Save a few registers
		LDR	R4,[R0,#0]		;Load the list base
05chunk_save	CMP	R4,#0			;Is there nothing to do?
		BEQ	%90chunk_save		;That's OK -- do nothing then

		; --- Write out this chunk's name ---

		MOV	R0,#'['			;Output a `[' character
		BL	xsave_byte		;Write that OK
		BVS	%99chunk_save		;Die if that went wrong

		ADD	R1,R4,#cLink__name	;Point to the name
00chunk_save	LDRB	R0,[R1],#1		;Load the next byte
		CMP	R0,#0			;Is this the end yet?
		MOVEQ	R0,#']'			;Yes -- write terminator
		BL	xsave_byte		;Write that out
		BVS	%99chunk_save		;Die if that went wrong
		BNE	%b00chunk_save		;And if there's more, do it

		; --- Write any necessary preamble ---

		MOV	R0,#&0A			;Start a new line
		BL	xsave_byte		;Write the newline byte
		BVS	%99chunk_save		;Die if that went wrong

		LDR	R14,[R4,#cLink__flags]	;Load the flags word
		TST	R14,#clFlag__binary	;Is this block binary?
		LDRNE	R0,=chunk__binMark	;Yes -- write a bin marker
		BLNE	xsave_word		;Yes -- write a whole word
		BVS	%99chunk_save		;Die if that went wrong

		; --- Now work out how to write this block ---

		ADD	R14,R4,#cLink__saver	;Point to saver info
		LDMIA	R14,{R2,R10,R12}	;Load that lot out
		CMP	R2,#0			;Is this a real saver?
		BEQ	%10chunk_save		;No -- skip on then
		MOV	R0,R4			;Point to the anchor
		MOV	R14,PC			;Set up return address
		MOV	PC,R2			;And call the saver
		LDRVC	R4,[R4,#cLink__next]	;Find the next block
		BVC	%05chunk_save		;And loop back to the start
		BVS	%99chunk_save		;Die if that went wrong

		; --- We must save the chunk ourselves ---

10chunk_save	LDR	R14,[R4,#cLink__flags]	;Load the flags word
		TST	R14,#clFlag__binary	;Is this block binary?

		MOV	R0,R4			;If OK, get flex anchor
		BL	flex_size		;Find the block's size
		BLNE	xsave_word		;If binary, write that out
		MOVVC	R1,R0			;Look after that
		LDRVC	R0,[R4,#0]		;Load the flex base address
		BLVC	xsave_block		;And write out the whole lot
		BVS	%99chunk_save		;Die if anything went wrong
		MOVNE	R0,#&0A			;Write a final line end
		BLNE	xsave_byte		;Just to make sure
		BLNE	xsave_byte		;Leave blank line under bin
		LDRVC	R4,[R4,#cLink__next]	;Find the next block
		BVC	%05chunk_save		;And loop back to the start
		BVS	%99chunk_save		;Die if anything went wrong

		; --- Finished writing the file ---

90chunk_save	LDMFD	R13!,{R0-R4,R10,R12,R14} ;Restore registers
		BICS	PC,R14,#V_flag		;Return error-free

		; --- Something got messed up ---

99chunk_save	ADD	R13,R13,#4		;Don't restore R0
		LDMFD	R13!,{R1-R4,R10,R12,R14} ;Restore other registers
		ORRS	PC,R14,#V_flag		;And return the error

		LTORG

;----- Data structures ------------------------------------------------------

; --- Chunk base ---

		^	0
cBase__list	#	4			;List of chunks
cBase__size	#	0

; --- Chunk link blocks ---

		^	0
cLink__anchor	#	4			;Anchor for flex block
cLink__userData	#	12			;12 bytes of user data
cLink__next	#	4			;Link to next block
cLink__flags	#	4			;Interesting things
cLink__name	#	20			;Name of this chunk
cLink__saver	#	12			;Saver routine for chunk
cLink__size	#	0

; --- Flags ---

clFlag__binary	EQU	(1<<0)			;Chunk contains binary data

; --- Other magic values ---

chunk__binMark	EQU	&6E694200		;Magic word for binary data

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

		END
