!
! Filename:     resource.asm
! Module name:  Resource
! Description:  resource manager (to be included in coke.asm)
!
! Commands:
!   CheckSpace (byte attributes, word length) - check memory before uploading
!   word QuerySpace () - return the amount of free space
!   byte QueryResources () - return the current number of resources
!   special byte LoadResource (byte attributes, word length, byte[] data, word checksum)
;           - download a new resource
!   FreeResources (byte rezID) - de-allocate one or more resources
!
! Functions:
!   InitResource - initialize the resource manager
!   GetFreeSpace - get the amount of free memory
!   CreateResource - create a new empty resource
!   GetResource - get a pointer to the start of a given resource
!   GetLength - get the length of a given resource
!   GetAttributes - get the attributes byte of a given resource
!   SetAttributes - set the attributes byte of a given resource
!   FreeResources - de-allocate one or more resources
!
! Comments:
!   Usually, high-level programs run on an external machine connected
!   through the serial port, and the hc11 is used as an i/o controller.
!   Each program often has a number of things such as fonts, graphics
!   scripts, music files, etc. that it needs to use.  However, it would
!   be too slow to send these files to the hc11 every time they are 
!   needed.  Resources provide a way for the external machine to upload
!   each file once, before the program starts, and access it from then
!   on with a simple reference.
! 
!   Resources can be loaded or freed at any time, but they must be freed
!   from the end.
!
! History:
!   bv 9/21/98	- initial revision
!   bv 5/25/99 	- adapted for CokeOS v3


; Resource_InitResource inits the resource loader.

InitResource subroutine
	clr	resource_numresources	; no resources loaded yet
	ldd	#FREEMEM_START		; get ptr to start of available memory
	std	resource_freepointer	; store it as start of unused memory
	ldx	#Resource_CommandTable	; get pointer to command table
	jsr	InstallCommandTable	; install the commands
	rts


; Resource_GetFreeSpace returns in d the amount of free memory left.

GetFreeSpace subroutine
	ldd	#RAMTOP			; get end of memory
	subd	resource_freepointer	; subtract used area 
	rts


; Resource_CreateResource allocates a chunk of memory for whatever purpose.
; carry is returned set if there isn't enough room to allocate the memory.
; otherwise, carry is returned clear, and the new resource id is returned in b.
; size of new resource is in d.  Attributes are initially  set to zero, but
; can be changed with Resource_SetAttributes.

CreateResource subroutine
	pshb				; stash d
	psha
	addd	resource_freepointer	; check for space requirements
	bcs	.bad
	cpd	#RAMTOP
	blo	.good
.bad
	pula				; if bad, restore d
	pulb
	sec				; set carry to show it's bad
	rts

.good
	pshx				; stash x
	
	ldab	resource_numresources	; get new resource number
	ldx	#resource_resourcetable	; get pointer to resource table
	abx				; add rez number 3 times for pointer
	abx				; to resource pointer
	abx		
	ldd	resource_freepointer	; put the ptr to the start of free mem
	std	1,x			; into the resource table
	clra
	staa	0,x			; clear attributes

	pulx				; restore x
	pula				; restore size in d
	pulb
	addd	resource_freepointer	; add pointer to start of free memory
	std	resource_freepointer	; allocate the memory

	ldab	resource_numresources	; get new resource num in b
	inc	resource_numresources	; inc number of resources
	clc				; clear carry to show it's good
	rts


; Resource_GetResource returns a pointer to the start of the resource data
; for the given resource number.  If the resource does not exist, carry is
; returned set, otherwise carry is clear.
; resource pointer is returned in x.
; resource number is in b.

GetResource subroutine
	psha
	pshb

	cmpb	resource_numresources	; does this resource exist?
	blo	.noerror		; if so, that's good
	sec				; if not, set the carry flag
	bra	.exit			; and leave.
.noerror
	ldx	#resource_resourcetable	; get start of resource table
	abx				; add 3 times the desired rez number
	abx
	abx
	ldaa	0,x			; get the type byte
	bita	#RFLAG_PAGEALIGN 	; is the page-align bit set?
	beq	.noalign		; if not, no worries
	ldd	1,x			; get ptr to start of alloc'd memory
	tstb				; is the low byte clear?
	beq	.noalign		; if so, no worries
	clrb				; if not, clear it
	inca				; and inc the high byte
	xgdx				; put into x
	clc				; no error
	pulb
	pula
	rts

.noalign
	ldx	1,x			; get the pointer to the header byte
	clc				; no error
.exit
	pulb
	pula
	rts


; GetResourceMem returns a pointer to the start of the allocated memory for
; a resource file, which may be different than the start of data if page align
; is set.  Same i/o as GetResource.

GetResourceMem subroutine
	psha
	pshb

	cmpb	resource_numresources	; does this resource exist?
	blo	.noerror		; if so, that's good
	sec				; if not, set the carry flag
	bra	.exit			; and leave.
.noerror
	ldx	#resource_resourcetable	; get start of resource table
	abx				; add 3 times the desired rez number
	abx
	abx
	ldd	1,x			; get the ptr to start of alloc'd mem
	xgdx				; put it in x
	clc				; no error
.exit
	pulb
	pula
	rts


; Resource_GetAttributes returns in a the attributes byte of the resource.
; carry is returned set if bad resource id.
; resource id in b. 

GetAttributes subroutine
	pshx

	cmpb	resource_numresources	; does this resource exist?
	blo	.noerror		; if so, that's good
	sec				; if not, set the carry flag
	bra	.exit			; and leave.
.noerror
	ldx	#resource_resourcetable	; get start of resource table
	abx				; add 3 times the desired rez number
	abx
	abx
	ldaa	0,x			; get the attributes byte
	clc				; no error
.exit
	pulx
	rts


; Resource_SetAttributes sets the attributes byte of the resource.
; carry is returned set if bad resource id.
; attributes byte in a, resource id in b. 

SetAttributes subroutine
	pshx

	cmpb	resource_numresources	; does this resource exist?
	blo	.noerror		; if so, that's good
	sec				; if not, set the carry flag
	bra	.exit			; and leave.
.noerror
	ldx	#resource_resourcetable	; get start of resource table
	abx				; add 3 times the desired rez number
	abx
	abx
	staa	0,x			; set the attributes byte
	clc				; no error
.exit
	pulx
	rts


; Resource_GetLength returns the length of the given resource file.
; If resource doesn't exist, carry is set on return
; length is returned in d
; resource id is in b

GetLength subroutine
	pshx

	jsr	GetResource		; get the pointer in x
	bcs	.exit			; if bad, no do
	stx	resource_scratch
	incb				; next resource
	jsr	GetResourceMem		; get its physical pointer
	bcc	.okay			; if no error, that cool
	ldx	resource_freepointer	; else, use free pointer
.okay
	xgdx				; put end pointer into d
	subd	resource_scratch	; subtract start pointer
	clc				; no error
.exit
	pulx
	rts



; Resource_FreeResources deallocates all of the resources after and including 
; the given number.  If the number is zero, all resources are freed.
; resource id is in b

FreeResources subroutine

.loop
	cmpb	resource_numresources	; is the rez id too high?
	bhs	.exit			; if so, go home

	pshx
	pshb

	ldab	resource_numresources
	decb				; put last resource id in b
	jsr	UninitializeResource	; let the kernel do what it wants
	jsr	GetResourceMem		; retrieve pointer from table (in x)
	stx	resource_freepointer	; deallocate the memory
	stab	resource_numresources	; store the new number of resources

	pulb
	pulx
	bra	.loop			; keep going until we're done
.exit
	rts



; Command_CheckSpace (type) (lobyte) (hibyte) checks if there is enough
; memory space for a given amount of data.
; If there is, COKE_ACK is returned, otherwise, COKE_FAIL is returned.

Command_CheckSpace subroutine
	ldx	resource_freepointer	; get end of alloc'd memory
	stx	resource_scratch	; stash it

	jsr	GetChar 		; get type
	bita	#RFLAG_PAGEALIGN	; want page aligned?
	beq	.noalign
	xgdx				; put freepointer in d
	tstb				; is low byte already zero?
	beq	.noalign		; if so, do nothing
	clrb				; if not, clear low byte 
	inca				; inc high byte
	std	resource_scratch	; store this as the start of new alloc

.noalign
	jsr	GetChar	; get lobyte of size
	tab
	jsr	GetChar	; get hibyte of size

	addd	resource_scratch	; add the ptr to the end of used memory
	bcs	.badmem			; if overflow, that's bad.
	cpd	#RAMTOP			; more mem than we have available?
	bhi	.badmem			; if so, that's bad
	ldaa	#COKE_ACK		; if not, then it's good
	rts
.badmem
	ldab	#ERROR_Coke_NotEnoughMemory
	ldaa	#COKE_FAIL
	rts


; Command_QuerySpace returns COKE_QUERY and then the amount of free bytes
; left, lobyte then hibyte.

Command_QuerySpace subroutine
	ldd	#RAMTOP			; get end of memory
	subd	resource_freepointer	; subtract used area 
	stab	resource_scratch		; store lo byte
	staa	resource_scratch+1	; then hi byte

	ldaa	#COKE_QUERY		; return COKE_QUERY
	ldab	#2			; two byte response
	ldx	#resource_scratch	; pointer to response
	rts


; Command_QueryResources returns the current number of resources (one byte).

Command_QueryResources subroutine
	ldaa	resource_numresources	; get number of resources
	staa	resource_scratch

	ldaa	#COKE_QUERY		; return query response
	ldab	#1			; one byte response
	ldx	#resource_scratch	; pointer to response
	rts


; Command_LoadResource loads a new resource into memory and puts it at the
; end of the resource table.  It assumes that memory has already been
; checked and there is enough space to load this.  The format is:
;    resource type   (byte)
;    resource size   (lobyte) (hibyte)
;    resource data   (size bytes)
;    16-bit checksum (lobyte) (hibyte)

Command_LoadResource subroutine
	ldab	resource_numresources	; get new resource number
	ldy	#resource_resourcetable	; get pointer to resource table
	aby				; add resource number thrice for ptr
	aby				; to resource pointer
	aby
	jsr	GetChar 		; get type byte
	staa	0,y			; put it into table
	bita	#RFLAG_PAGEALIGN	; is the page align bit set?
	beq	.noalign		; if not, don't worry
	ldd	resource_freepointer	; if so, get ptr to start of free mem
	std	1,y			; store it in the table
	tstb				; is it already aligned? 
	beq	.aligndone		; if so, do nothing
	clrb				; if not, clear low byte
	inca				; increment high byte
	std	resource_freepointer	; store new freepointer for adding size below
	bra	.aligndone		; use that pointer

.noalign
	ldd	resource_freepointer	; put the ptr to start of free memory
	std	1,y			; into the resource table
.aligndone
	xgdx				; put pointer to resource into x

	jsr	GetChar			; get lobyte of size
	tab				; put it in lobyte of accumulator
	jsr	GetChar			; get hibyte of size
	pshb				; stash size on stack
	psha
	addd	resource_freepointer	; add pointer to start of free memory
	std	resource_freepointer	; allocate the memory
	pula				; get size back in d to use as counter
	pulb

	clr	resource_checksumlo	; clear checksum lobyte
	clr	resource_checksumhi	; clear checksum hibyte
.loadloop
	psha				; stash hi byte of size on stack
	jsr	GetChar			; get data byte
	staa	0,x			; store it
	inx				; next memory location
	adda	resource_checksumlo	; add data to checksum lobyte
	staa	resource_checksumlo	; store it
	bcc	.nochecksumhi		; if no carry, no problem
	inc	resource_checksumhi	; if carry, increment hi byte
.nochecksumhi
	pula				; get hi byte of counter back
	subd	#1			; subtract one from counter
	bne	.loadloop		; if not zero, keep going.

	jsr	GetChar			; get checksum lobyte
	tab				; put in lobyte of accumulator
	jsr	GetChar			; get checksum hibyte
	cpd	resource_checksum	; does it match our checksum?
	beq	.gotmatch		; if they match, we in the money

	ldd	1,y			; if checksum doesn't match, dealloc.
	std	resource_freepointer	; store start of new mem as freepointer
	ldab	#ERROR_Coke_BadChecksum
	ldaa	#COKE_FAIL		; return a FAIL response
	rts

.gotmatch
	jsr	Command_QueryResources	; call the command handler directly
	inc	resource_numresources	; say we have another resource
	rts


; Command_FreeResources (resource num) deallocates all of the resources after
; and including the given number.  If the number is zero, all resources are
; freed.

Command_FreeResources subroutine
	jsr 	GetChar			; get the resource number
	tab				; put it in b
	jsr	FreeResources		; do it
	ldaa	#COKE_ACK
	rts


; Resource_CommandTable is the command table for resource commands.

Resource_CommandTable subroutine
	dc.b	CMD_Resource_CheckSpace
	dc.w	Command_CheckSpace
	dc.b	CMD_Resource_QuerySpace
	dc.w	Command_QuerySpace
	dc.b	CMD_Resource_QueryResources
	dc.w	Command_QueryResources
	dc.b	CMD_Resource_LoadResource
	dc.w	Command_LoadResource
	dc.b	CMD_Resource_FreeResources
	dc.w	Command_FreeResources	

	dc.b	0

