****************************************************************
*
* QLOAD
*    Quick binary loader for FLEX 2/3. Patches FLEX to replace
*    the FLEX binary load routine. Only provides benefit when
*    used with the track buffered disk driver, so this program
*    is located and stays resident in the 4K RAM block assigned
*    to the track buffered driver.
*
*    Ver    Date      Desc
*    1.0  12/18/22   Mike Douglas, Initial version
*
****************************************************************

* FLEX equates

DRVRRAM	equ	$D000		;start of disk driver RAM space

SYSFCB	equ	$A840		;FLEX system FCB
XFRFLAG	equ	$AC1D		;0=no transfer address found
XFRADDR	equ	$AC1E		;transfer address, if found
OFFSET	equ	$AC1B		;load address offset
ERRTYP	equ	$AC20		;error type

WARMS	equ	$AD03		;FLEX re-entry address
RENTER	equ	$AD06		;re-enter DOS
PSTRNG	equ	$AD1E		;display string on console
LOAD	equ	$AD30		;binary loader
RPTERR	equ	$AD3F		;report error from the FCB

* FMS equates

FMS	equ	$B406		;entry point for FMS calls
CLOSE	equ	4		;FMD close file
READSS	equ	9		;FMS read single sector
SECLEN	equ	252		;length of user data in sector

* FMS file control block (FCB)

	org	SYSFCB
FCB	equ	*
fCmd	rmb	1		;FMS command
fErr	rmb	1		;error status
	rmb	28
fCurTrk	rmb	1		;current track
fCurSec	rmb	1		;current sector
	rmb	2
fSecCnt	rmb	1		;data index used as count
	rmb	29
fSector	rmb	256		;sector data

*---------------------------------------------------------------
* Installer
*---------------------------------------------------------------
	org	DRVRRAM+$E00	;locate in track buffer RAM

start	bra	install
	fcb	10		;version

install	ldx	LOAD+1		;patch FLEX binary load
	ldaa	#$7E		;jump instruction
	staa	0,x
	ldaa	#qLoad/256	;MSB of jump to here
	staa	1,x
	ldaa	#qLoad AND $FF	;LSB of jump to here
	staa	2,x

	ldx	#mInstal	;display "installed" message
	jsr	PSTRNG
	jmp	WARMS		;exit

mInstal	fcc	"QUICK LOADER INSTALLED"
	fcb	$04

*---------------------------------------------------------------
* Memory resident replacement for the FLEX binary loader
*---------------------------------------------------------------
qLoad	sts	saveStk		;save SP as entered
	clr	XFRFLAG		;transfer address not found yet

main	bsr	getByte		;get next byte
	cmpa	#$02		;data segment?
	beq	dataSeg		;yes
	cmpa	#$16		;transfer address?
	bne	main		;no

	bsr	getByte		;get transfer address
	staa	XFRADDR		;save for FLEX
	bsr	getByte
	staa	XFRADDR+1

	ldaa	#1		;indicate an address was found
	staa	XFRFLAG
	bra	main

* dataSeg - Move a data segment to RAM

dataSeg	bsr	getByte		;get load address of data seg
	tab			;B=MSB of address
	bsr	getByte		;A=LSB of address

	adda	OFFSET+1	;compute final load address
	staa	segAddr+1	;save in segAddr
	adcb	OFFSET
	stab	segAddr

	bsr	getByte		;A=length of data segment
	staa	segCnt		;segment length kept in segCnt

* segLoop - Move all (or portions) of a data segment to RAM

segLoop	ldab	segCnt		;B=bytes left in data segment
	beq	main		;no bytes left

	ldaa	fSecCnt		;A=bytes left in sector
	bne	notEmpt		;not empty	
	bsr	getSec		;no bytes left, need a sector
	
notEmpt	cba			;bytes in sector >= bytes to move?
	bhs	move		;yes
	tab			;else, use smaller value

* move - Move a group of bytes from the sector to RAM

move	sba			;A=sector bytes left after move
	staa	fSecCnt
	ldaa	segCnt		;update bytes left in segment
	sba
	staa	segCnt

	sei			;protect while SP is invalid
	txs			;SP->data in sector 
	ldx	segAddr		;X->destination in RAM

byteLp	pula			;A=next byte from sector
	staa	0,x		;store in destination
	inx
	decb			;byte count to move 
	bne	byteLp

	stx	segAddr		;save current RAM pointer
	tsx			;X->sector data
	lds	saveStk		;restore stack pointer
	cli
	bra	segLoop		;continue

*---------------------------------------------------------------
* getByte - Get next byte from sector. This is normally the
*    next byte pointed to by X unless all bytes in the sector
*    have been read. In this case, read next sector.
*---------------------------------------------------------------
getByte	ldaa	fSecCnt		;any bytes left?
	bne	retByte		;yes
	bsr	getSec		;else, get a new sector

retByte	ldaa	0,x		;return next byte
	inx
	dec	fSecCnt
	rts

*---------------------------------------------------------------
* getSec - Read next sector from file. Returns with
*    X->sector data and A=sector data length.
*---------------------------------------------------------------
getSec	ldx	fSector		;X=next track,sector
	beq	gsEof		;end of file reached

	stx	fCurTrk		;set track, sector to read
	ldx	#FCB		;read the next sector
	ldaa	#READSS
	staa	0,x
	jsr	FMS
	bne	gsErr		;error
	
	ldx	#fSector+4	;X->1st data byte
	ldaa	#SECLEN		;set bytes available
	staa	fSecCnt
	rts

* Normal end of file reached

gsEof	ldx	#FCB		;close the file
	ldaa	#CLOSE
	staa	0,x
	jsr	FMS
	bne	gsErr

	lds	saveStk		;restore SP at entry
	clc			;no error
	rts			;return to original caller

* File I/O error

gsErr	ldaa	1,x		;A=error code
	staa	ERRTYP		;give to system
	jsr	RPTERR		;report the error
	jmp	RENTER		;re-enter FLEX

*---------------------------------------------------------------
* Data area
*---------------------------------------------------------------
	rmb	2		;pad bytes for SAVE issues
segAddr	rmb	2		;load address of data segment
segCnt	rmb	1		;segment bytes remaining
saveStk	rmb	2		;stack pointer save

	end	start
