;
; xfer.xload.s
;
; Simplified loading with coroutines (MDW)
;
;  1994 Straylight
;

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

		GET	libs:header
		GET	libs:swis

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

		GET	sapphire:alloc
		GET	sapphire:coRoutine
		GET	sapphire:fastMove
		GET	sapphire:sapphire

;----- How it all works -----------------------------------------------------
;
; xload attempts to unify the various loading routines you have to supply,
; by providing a single interface.
;
; There are several main routines, xload_file, xload_initBuf,
; xload_killBuf, xload_extend and xload_doneBuf, which are called from
; the main load branch table.
;
; Loading of data is done with the call xload_block.  This just reads
; a block of data somehow.  All the other routines are special interfaces to
; xload_block.

;----- How the error handling works -----------------------------------------
;
; The really tricky bit is passing of errors during a RAM transfer.
; Errors can be returned at two places -- in the user code (e.g. running out
; of memory) and by load's message handling if the receiver dies.  All
; errors must be handled by user code, to release claimed memory etc.
;
; The error gets passed along a path like this:
;
; Error created by user code
;
; user code --> xload__startCo --> xload_failed
;
; Error created by remote receiver
;
; xload_failed --> xload__read --> user code --> xload__startCo

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

		AREA	|Sapphire$$Code|,CODE,READONLY

; --- xload_file ---
;
; On entry:	R0 == pointer to loader routine
;		R1 == R10 value to pass to loader
;		R2 == R12 value to pass to loader
;		R3 == pointer to leafname of file (passed to loader in R0)
;		R4 == pointer to filename to load from
;		R5 == whether the file is safe (passed to loader in R1)
;
; On exit:	May return an error
;
; Use:		Calls a generalised loader routine to read data from a file.

		EXPORT	xload_file
xload_file	ROUT

		STMFD	R13!,{R0-R5,R10,R12,R14} ;Save lots of registers
		WSPACE	xload__wSpace		;Find my workspace address

		; --- First, try to allocate a buffer ---

		BL	xload__setBuff		;Allocate the buffer nicely
		BVS	%99xload_file		;If we couldn't, tidy up

		; --- Set up the file for writing ---

		MOV	R1,R4			;Point at the file name
		MOV	R0,#&4F			;Lots of errors, no path
		SWI	XOS_Find		;Try to open the file
		BVS	%98xload_file		;Tidy up if it wouldn't open
		STR	R0,xload__file		;Save the file handle away

		; --- Set up flags and let rip ---

		LDR	R14,xload__flags	;Load my current flags word
		AND	R14,R14,#xlFlag__inited	;Only leave inited flag
		STR	R14,xload__flags	;Save the flags back again

		MOV	R14,#0			;Nothing read yet
		STR	R14,xload__buffUsed	;So clear buffer size

		LDMIA	R13,{R2,R10,R12,R14}	;Load the arguments out
		MOV	R0,R14			;Get the leafname in R0
		ADDS	R1,R1,#0		;Clear overflow and carry
		LDR	R1,[R13,#16]		;Load the safeness flag
		MOV	R14,PC			;Set up the return address
		MOV	PC,R2			;And call the saver routine
		WSPACE	xload__wSpace		;Restore my workspace ptr
		BVS	%97xload_file		;Tidy up if it failed

		; --- Close the file ---

		MOV	R0,#0			;Close an open file
		LDR	R1,xload__file		;Get the file handle ready
		SWI	OS_Find			;Close it

		; --- Get rid of the buffer and return ---

		BL	xload__loseBuff		;Free up the buffer space
		LDMFD	R13!,{R0-R5,R10,R12,R14} ;Unstack all the registers
		BICS	PC,R14,#V_flag		;And return to caller

		; --- Tidy up after various catastrophes ---

97xload_file	MOV	R10,R0			;Keep the error pointer
		MOV	R0,#0			;Close an open file
		LDR	R1,xload__file		;Get the file handle ready
		SWI	OS_Find			;Close it
		MOV	R0,R10			;Restore the error pointer

98xload_file	BL	xload__loseBuff		;Free up the buffer space

99xload_file	ADD	R13,R13,#4		;Don't restore R0 on exit
		LDMFD	R13!,{R1-R5,R10,R12,R14} ;Unstack all the registers
		ORRS	PC,R14,#V_flag		;And return to caller

; --- xload_initBuf ---
;
; On entry:	R0 == pointer to loader routine
;		R1 == R10 value to pass to loader
;		R2 == R12 value to pass to loader
;		R3 == pointer to leafname of file (passed to loader in R0)
;
; On exit:	R0 == pointer to load buffer
;		R1 == size of load buffer
;		May return an error
;
; Use:		Starts a RAM transfer and starts up a generalised load
;		routine.

		EXPORT	xload_initBuf
xload_initBuf	ROUT

		STMFD	R13!,{R0-R3,R12,R14}	;Save some registers
		WSPACE	xload__wSpace		;Load my workspace address

		; --- Firstly, allocate the buffer ---

		BL	xload__setBuff		;Create the load buffer
		ADDVS	R13,R13,#8		;If failed, unstack R0,R1
		BVS	%99xload_initBuf	;And then return error

		; --- Now create the coroutine ---

		ADR	R0,xload__startCo	;Point to the coroutine
		MOV	R1,R13			;Point to saved corout info
		MOV	R2,R12			;Pass my workspace in R12
		MOV	R3,#0			;Default stack size please
		BL	coRout_create		;Try to create the coroutine
		ADDVS	R13,R13,#8		;Don't return R0,R1
		BVS	%98xload_initBuf	;If it failed, tidy up
		STR	R0,xload__coRout	;Save the couroutine handle

		; --- Let xload__startCo save coroutine info ---

		BL	coRout_switch		;Switch to it quickly
		ADD	R13,R13,#8		;Don't return R0,R1

		; --- Set up flags and bits of workspace ---

		LDR	R14,xload__flags	;Load my flags word
		AND	R14,R14,#xlFlag__inited	;Clear all but initialised
		ORR	R14,R14,#xlFlag__ramTran ;We're doing RAM transfer
		STR	R14,xload__flags	;Save the flags back
		MOV	R14,#0			;No data RAMmed in yet
		STR	R14,xload__rammed	;So save this information

		; --- Return the buffer information ---

		ADR	R14,xload__buffer	;Find my buffer info
		LDMIA	R14,{R0,R1}		;Load address and size
		LDMFD	R13!,{R2,R3,R12,R14}	;Unstack my registers
		BICS	PC,R14,#V_flag		;And return no error

		; --- Things went wrong ---

98xload_initBuf	BL	xload__loseBuff		;Get rid of transfer buffer
99xload_initBuf	LDMFD	R13!,{R2,R3,R12,R14}	;Unstack my registers
		ORRS	PC,R14,#V_flag		;And return the error

		LTORG

; --- xload__startCo ---
;
; On entry:	R0 == my coroutine handle
;		R10 == pointer to block containing (routine,R10,R12,leafname)
;		R12 == my workspace
;
; On exit:	Returns through coRout_end
;
; Use:		Starts the coroutine for sending data by RAM transfer.  It
;		handles errors reported by it, and terminates the transfer
;		properly.
;
;		The information about the real client is stored on the
;		main routine's stack.  Before it returns, therefore, we
;		need to load it from there and save it somewhere so that
;		it will be safe when we start the coroutine properly from
;		xload_extend.  Therefore we actually start from xload_initBuf
;		and save the information on the coroutine's stack, and
;		switch back quickly.

xload__startCo	ROUT

		LDMIA	R10,{R0-R3}		;Load the caller's registers
		STMFD	R13!,{R0-R3}		;Save them on our stack
		MOV	R0,#0			;Return to main routine
		BL	coRout_switch		;Switch back please

		LDR	R14,xload__flags	;Load my flags word
		ORR	R14,R14,#xlFlag__started ;Coroutine has started now
		STR	R14,xload__flags	;Save flags back again

		LDMFD	R13!,{R2,R10,R12,R14}	;Load client's workspace
		MOV	R0,R14			;Pass leafname in R0
		MOV	R1,#0			;File is not safe
		MOV	R14,PC			;Set up a return address
		MOV	PC,R2			;And call the saver routine
		BVS	%90xload__startCo	;If it failed, report error

		MOV	R14,#0			;A nice zero value
		WSPACE	xload__wSpace		;Load my workspace pointer
		STR	R14,xload__coRout	;Write over the coroutine
		LDR	R0,xload__buffer	;Load the buffer address
		MOV	R1,#xload__buffSize	;Get the current buffer size
		ADR	R14,xload__start	;Point to load area
		STMIA	R14,{R0,R1}		;Save the data in there
		B	coRout_end		;Terminate coroutine

		; --- Handle its return values ---

90
		WSPACE	xload__wSpace		;Locate my workspace address
		LDR	R14,xload__flags	;Load the flags word

		ORR	R14,R14,#xlFlag__error	;Set the main error flag
		STR	R14,xload__flags	;Save the flags back again
		STR	R0,xload__error		;And save the error pointer
		B	coRout_end		;And finish the coroutine

		LTORG

; --- xload_killBuf ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Does a buffer destroy for a failed load operation.

		EXPORT	xload_killBuf
xload_killBuf	ROUT

		STMFD	R13!,{R0,R12,R14}	;Save some registers
		WSPACE	xload__wSpace		;Find my workspace address
		LDR	R14,xload__flags	;Load my flags word
		TST	R14,#xlFlag__started	;Has the coroutine started?
		LDMNEFD	R13!,{R0,R12,PC}^	;Yes -- this is an error then

		; --- Destroy the buffer and the coroutine ---

		BL	xload__loseBuff		;Get rid of the load buffer
		LDR	R0,xload__coRout	;Load the coroutine handle
		BL	coRout_destroy		;Destroy the coroutine
		LDMFD	R13!,{R0,R12,PC}^	;Return to caller

		LTORG

; --- xload_extend ---
;
; On entry:	R1 == size of last buffer used for receiving
;
; On exit:	R0 == pointer to new buffer
;		R1 == size of new buffer
;		May return an error
;
; Use:		Performs a buffer extent operation during an xload RAM
;		transfer.

		EXPORT	xload_extend
xload_extend	ROUT

		STMFD	R13!,{R12,R14}		;Save some registers
		WSPACE	xload__wSpace		;Find my workspace address

		; --- Bump the amount of data received ---

		LDR	R14,xload__rammed	;Find how much was sent
		ADD	R14,R14,R1		;Add size of last buffer
		STR	R14,xload__rammed	;And save the size back

		; --- Let the coroutine do some loading ---

		BL	xload__resume		;Let the coroutine run a bit
		LDR	R14,xload__flags	;Load the coroutine's flags
		TST	R14,#xlFlag__error	;Has it returned an error?
		BNE	%90xload_extend		;Yes -- then return it

		; --- Get the next buffer to send from xload__read ---

		ADR	R14,xload__start	;Point to buffer data
		LDMIA	R14,{R0,R1}		;Load the buffer data
		LDMFD	R13!,{R12,R14}		;Unstack the registers
		BICS	PC,R14,#V_flag		;And don't return an error

		; --- Client had a wee problem ---

90xload_extend	LDR	R0,xload__error		;Load the error pointer
		LDMFD	R13!,{R12,R14}		;Unstack the registers
		ORRS	PC,R14,#V_flag		;And return the error

		LTORG

; --- xload_doneBuf ---
;
; On entry:	R1 == total size of data received
;
; On exit:	R0 corrupted
;		May return an error
;
; Use:		Handles the last bufferful of a RAM load.

		EXPORT	xload_doneBuf
xload_doneBuf	ROUT

		STMFD	R13!,{R12,R14}		;Save some registers
		WSPACE	xload__wSpace		;Find my workspace address

		; --- Work out how much of the last buffer was sent ---

		LDR	R14,xload__rammed	;Find how much was sent
		SUB	R0,R1,R14		;How much of last buffer?
		STR	R0,xload__length	;And save the size back
		LDR	R14,xload__flags	;Load the flags word
		ORR	R14,R14,#xlFlag__finish	;Set the finished flag
		STR	R14,xload__flags	;Save the flags back again
		TST	R14,#xlFlag__started	;Has the coroutine started?
		STREQ	R0,xload__buffUsed	;No -- set buffer size

		; --- Now let the coroutine finish off ---

		BL	xload__resume		;Let the coroutine run a bit
		LDR	R14,xload__flags	;Load the coroutine's flags
		TST	R14,#xlFlag__error	;Has it returned an error?
		BNE	%90xload_doneBuf	;Yes -- then return it

		; --- Return to caller ---

		BL	xload__loseBuff		;Don't want this any more
		LDMFD	R13!,{R12,R14}		;Unstack the registers
		BICS	PC,R14,#V_flag		;Don't return an error

		; --- Coroutine hit a wee problem ---

90xload_doneBuf	LDR	R0,xload__error		;Load the error pointer
		LDMFD	R13!,{R12,R14}		;Unstack the registers
		ORRS	PC,R14,#V_flag		;And return the error

		LTORG

; --- xload_done ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Tidies up after a successful load job.

		EXPORT	xload_done
xload_done	ROUT

		MOVS	PC,R14			;Nothing to do here

; --- xload_failed ---
;
; On entry:	R0 == pointer to error block
;
; On exit:	--
;
; Use:		Tidies up a RAM transfer after an error.

		EXPORT	xload_failed
xload_failed	ROUT

		STMFD	R13!,{R12,R14}		;Save some registers
		WSPACE	xload__wSpace		;Find my workspace
		LDR	R14,xload__flags	;Load my flags word
		TST	R14,#xlFlag__ramTran	;Are we using RAM transfer?
		LDMEQFD	R13!,{R12,PC}^		;No -- return right now then

		; --- Find out if the user has seen the error yet ---

		STMFD	R13!,{R0}		;Save some more registers
		TST	R14,#xlFlag__error	;Has he seen the error?
		BNE	%30xload_failed		;Yes -- then don't switch

		; --- Get xload__write to return an error ---

		ORR	R14,R14,#xlFlag__error	;Set the error flag
		STR	R0,xload__error		;And save the error pointer
		BL	xload__resume		;Let the coroutine run a bit

		; --- Now tidy everything up ---
		;
		; The coroutine should have killed itself by now

30xload_failed	BL	xload__loseBuff		;Kill off the data buffer
		LDMFD	R13!,{R0,R12,PC}^	;Return to caller

		LTORG

; --- xload__resume ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Lets the coroutine run for a bit.

xload__resume	ROUT

		STMFD	R13!,{R0,R1,R14}	;Save some registers
		LDR	R0,xload__coRout	;Load the coroutine handle
		CMP	R0,#0			;Has it finished yet?
		BLNE	coRout_switch		;No -- run it a bit more then
		LDMNEFD	R13!,{R0,R1,PC}^	;And return to caller

		LDR	R0,xload__buffer	;Load the buffer address
		MOV	R1,#xload__buffSize	;Get the current buffer size
		ADR	R14,xload__start	;Point to load area
		STMIA	R14,{R0,R1}		;Save the data in there
		LDMFD	R13!,{R0,R1,PC}^	;Return to caller

		LTORG

; --- xload__setBuff ---
;
; On entry:	--
;
; On exit:	May return an error
;
; Use:		Sets up the buffer for a data transfer.

xload__setBuff	ROUT

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

		; --- Allocate the buffer memory ---

		MOV	R0,#xload__buffSize	;Get the buffer size
		BL	alloc			;Try to allocate the buffer
		BLCS	alloc_error		;Get an error if it failed
		BCS	%90xload__setBuff	;And tidy things up

		; --- Set up los of variables ---

		ADR	R14,xload__buffer	;Point at the buffer info
		MOV	R1,#xload__buffSize	;No buffer used yet
		MOV	R2,#0			;Start buffer from beginning
		MOV	R3,#0			;No data sent yet either
		STMIA	R14,{R0-R3}		;Save these values away
		LDMFD	R13!,{R0-R3,R14}	;Restore all the registers
		BICS	PC,R14,#V_flag		;And return with V clear

		; --- Report an error ---

90		ADD	R13,R13,#4		;Don't restore R0 on exit
		LDMFD	R13!,{R1-R3,R14}	;Restore all the registers
		ORRS	PC,R14,#V_flag		;And return with V set

		LTORG

; --- xload__loseBuff ---
;
; On entry:	--
;
; On exit:	--
;
; Use:		Kills off the buffer.

xload__loseBuff	ROUT

		STMFD	R13!,{R0,R14}		;Save some registers
		LDR	R0,xload__buffer	;Load the buffer address
		BL	free			;Free the buffer
		LDR	R14,xload__flags	;Load my current flags word
		AND	R14,R14,#xlFlag__inited	;Only leave inited flag
		STR	R14,xload__flags	;Save the flags back again
		LDMFD	R13!,{R0,PC}^		;And return to caller

		LTORG

; --- xload_byte ---
;
; On entry:	--
;
; On exit:	CC if data read OK, and
;		  R0 == byte read
;		else CC if end-of-file, and
;		  R0 corrupted
;		May return an error
;
; Use:		Reads a byte from the current input.

		EXPORT	xload_byte
xload_byte	ROUT

		STMFD	R13!,{R0-R2,R14}	;Save some registers
		MOV	R0,R13			;Overwrite saved R0
		MOV	R1,#1			;Just read one byte
		BL	xload_block		;Read it onto the stack
		STRVS	R0,[R13,#0]		;If error, store pointer
		LDR	R0,[R13],#4		;Load the byte/error pointer
		ANDVC	R0,R0,#&FF		;If byte/EOF, kill other bits
		LDMFD	R13!,{R1,R2,PC}		;And return to caller

		LTORG

; --- xload_word ---
;
; On entry:	--
;
; On exit:	CC if data read OK, and
;		  R0 == word read
;		else CS if end-of-file and
;		  R0 corrupted
;		May return an error
;
; Use:		Reads a word from the current input.

		EXPORT	xload_word
xload_word	ROUT

		STMFD	R13!,{R0-R2,R14}	;Save some registers
		MOV	R0,R13			;Overwrite saved R0
		MOV	R1,#4			;Just read one word
		BL	xload_block		;Read it onto the stack
		STRVS	R0,[R13,#0]		;If error, store pointer
		LDR	R0,[R13],#4		;Load the byte/error pointer
		LDMFD	R13!,{R1,R2,PC}		;And return to caller

		LTORG

; --- xload_block ---
;
; On entry:	R0 == pointer to buffer to read
;		R1 == size of buffer to read
;
; On exit:	R0, R1 preserved
;		R2 == number of bytes read
;		CC if more data available, CS for end-of-file
;		May return an error
;
; Use:		Reads in a block of data.  Data is buffered, so this is
;		fairly quick for reading small objects.  Large data blocks
;		are read directly to avoid buffering overhead.

		EXPORT	xload_block
xload_block	ROUT

		CMP	R1,#0			;Is there anything there?
		MOVEQ	R2,#0			;Nothing read yet
		BICEQS	PC,R14,#V_flag		;No -- return with no error

		STMFD	R13!,{R0,R1,R3-R8,R12,R14} ;Save a load of registers
		WSPACE	xload__wSpace		;Load my workspace address
		MOV	R6,R0			;Keep his buffer address
		MOV	R7,R1			;Look after his buffer size
		MOV	R8,#0			;No data copied yet

		; --- First, try to read from the buffer ---

		ADR	R14,xload__buffer	;Point to the buff info
		LDMIA	R14,{R3-R5}		;Load the buffer stuff
00xload_block	SUBS	R14,R4,R5		;Find out how much there is
		BEQ	%20xload_block		;No -- skip ahead then

		; --- Read as much as we need from buffer ---

		CMP	R7,R14			;Is there enough there?
		MOVLT	R2,R7			;Yes -- get as much as we can
		MOVGE	R2,R14			;Otherwise empty the buffer
		ADD	R1,R3,R5		;Get the correct address
		MOV	R0,R6			;Point at his buffer
		BL	fastMove		;Copy the data across nicely

		; --- Reset the buffer arguments ---

		ADD	R5,R5,R2		;Add on the offset now
		ADD	R6,R6,R2		;Bump up his buffer address
		SUBS	R7,R7,R2		;And decrease the size
		ADD	R8,R8,R2		;Bump amount of data read
		BEQ	%90xload_block		;If no more to do, return

		; --- Buffer was empty ---
		;
		; If he wants more than a buffer full, we should just read
		; directly into his buffer.  Otherwise we fill the buffer
		; and loop back again to copy data.

20xload_block	CMP	R7,#xload__buffSize	;Does he want more?
		BGE	%50xload_block		;Yes -- skip ahead then

		MOV	R0,R3			;Point to the buffer
		MOV	R1,#xload__buffSize	;Get the buffer size
		BL	xload__read		;Read data into the buffer
		BVS	%95xload_block		;If it failed, return error
		MOVS	R4,R2			;Get the new buffer size
		MOV	R5,#0			;Haven't started on it yet
		BNE	%00xload_block		;If anything read, go round
		B	%90xload_block		;Otherwise we'd better stop

		; --- Read data into his big buffer ---

50xload_block	MOV	R0,R6			;Point to his buffer
		MOV	R1,R7			;And get his buffer size
		BL	xload__read		;Read data into the buffer
		BVS	%95xload_block		;If it failed, return error
		ADD	R8,R8,R2		;Bump by amount read

		; --- Everything went OK ---

90xload_block	ADR	R14,xload__buffUsed	;Point to the buff info
		STMIA	R14,{R4,R5}		;Store new offsets
		MOV	R2,R8			;Get amount of data read

		LDR	R14,xload__total	;Load the total data read
		ADD	R14,R14,R8		;Add new amount read
		STR	R14,xload__total	;And save back the new total

		LDR	R14,xload__flags	;Load the flags word
		LDR	R1,[R13,#4]		;Load amount he wanted
		CMP	R1,R2			;Did we read it all?
		ORRGT	R14,R14,#xlFlag__finish	;No -- set eof flag then
		STRGT	R14,xload__flags	;Save the flags back

		LDMFD	R13!,{R0,R1,R3-R8,R12,R14} ;Unstack loads of regs
		BIC	R14,R14,#V_flag		;Clear error indicator
		ORRGTS	PC,R14,#C_flag		;Set Carry on exit if EOF
		BICLES	PC,R14,#C_flag		;Otherwise clear it

		; --- We had an error ---

95xload_block	ADD	R13,R13,#4		;Don't restore R0 on exit
		LDMFD	R13!,{R1,R3-R8,R12,R14}	;Unstack loads of regs
		ORRS	PC,R14,#V_flag		;Return the error condition

		LTORG

; --- xload__read ---
;
; On entry:	R0 == pointer to buffer to read
;		R1 == size of buffer to read
;
; On exit:	May return an error
;		R2 == actual amount of data read
;
; Use:		Reads a block of data from the input stream.

xload__read	ROUT

		BIC	R14,R14,#V_flag		;Clear error indicator
		MOV	R2,#0			;Nothing read yet
		CMP	R1,#0			;Is there anything there?
		MOVEQS	PC,R14			;No -- return with no error

		STMFD	R13!,{R14}		;Save some registers
		LDR	R14,xload__flags	;Find my flags word
		TST	R14,#xlFlag__finish	;Is the transfer over?
		LDMNEFD	R13!,{PC}^		;Yes -- return right now
		TST	R14,#xlFlag__ramTran	;Am I using RAM transfer?
		BNE	%50xload__read		;Yes -- do that then

		; --- Use OS_HeebieJeebie to read the data ---

		STMFD	R13!,{R0,R1,R3,R4}	;Save some more registers
		MOV	R2,R0			;Point to the user's buffer
		MOV	R3,R1			;And get the buffer size
		LDR	R1,xload__file		;Get the file's handle
		MOV	R0,#4			;Read at current position
		SWI	XOS_GBPB		;Read the data in nicely
		STRVS	R0,[R13,#0]		;If error, save error ptr
		LDRVC	R1,[R13,#4]		;Load number of bytes wanted
		SUBVC	R2,R1,R3		;Get number actually read
		LDMFD	R13!,{R0,R1,R3,R4,R14}	;Unstack the registers
		ORRVSS	PC,R14,#V_flag		;Return V set as required
		BICVCS	PC,R14,#V_flag

		; --- Get the main coroutine to read the buffer ---

50xload__read	STMFD	R13!,{R0}		;Save another register
		ADR	R14,xload__start	;Point to the buffer info
		STMIA	R14,{R0,R1}		;Save for main coroutine
		MOV	R0,#0			;Get magic coroutine handle
		BL	coRout_switch		;And switch to main code
		LDR	R2,xload__length	;Read data size read
		LDR	R14,xload__flags	;Reload the flags
		TST	R14,#xlFlag__error	;Was there a problem?
		LDMFD	R13!,{R0,R14}		;Restore the registers
		BICEQS	PC,R14,#V_flag		;No -- just clear V then
		LDRNE	R0,xload__error		;Otherwise load error pointer
		ORRNES	PC,R14,#V_flag		;And return with V set

		LTORG

xload__wSpace	DCD	0

;----- Constants ------------------------------------------------------------

xload__buffSize	EQU	1024			;1K buffer should be enough

;----- Workspace ------------------------------------------------------------

		^	0,R12
xload__wStart	#	0

xload__flags	#	4			;Various run-time flags
xload__coRout	#	0			;The load coroutine handle
xload__file	#	4			;The load file handle
xload__buffer	#	4			;Pointer to my 1K buffer
xload__buffUsed	#	4			;Amount of data in buffer
xload__buffOff	#	4			;Offset reading from buffer
xload__total	#	4			;How much data read so far
xload__error	#	0			;Error pointer from corout
xload__start	#	4			;Start of buffer to send
xload__length	#	4			;Size of next buffer to send
xload__rammed	#	4			;Data RAMFetched so far

xload__wSize	EQU	{VAR}-xload__wStart

xlFlag__inited	EQU	(1<<0)			;Are we initialised already?
xlFlag__ramTran	EQU	(1<<1)			;Are we doing RAM transfer?
xlFlag__finish	EQU	(1<<2)			;This is the very last block
xlFlag__error	EQU	(1<<3)			;Should return an error
xlFlag__started	EQU	(1<<4)			;Coroutine has started

		AREA	|Sapphire$$LibData|,CODE,READONLY

		DCD	xload__wSize
		DCD	xload__wSpace
		DCD	0
		DCD	0

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

		END
