*******************************************************************
*
*  UIOROM - PROM for the Altair 680 to support a high speed
*     reader/punch on the UIO serial port. Reads an S-Record
*     file at the $FD00 entry point and reads a binary (BINLOAD
*     checksum format) at the $FD80 entry point.
*
*     16 bytes of RAM are used at the top of the 1st 16K of RAM
*     for stack and variables ($3FF0-$3FFF). If the file loaded
*     overwrites these locations, the load can fail.
*
*     1.0  11/29/23  Mike Douglas, Original
*
*     1.1  12/18/23  Mike Douglas, Remove punch entry point and
*		     replace with binary loader equivalent to
*		     BINLOAD.ASM.
*
*******************************************************************

ROMBASE	equ	$FD00

* 680 monitor equates

OUTCH	equ	$FF81		;display character from B
XHIB2X	equ	$FF68		;load XHI:B into X
MONITR	equ	$FFAB		;re-enter monitor

* UIO equates

SIOSR	equ	$F006		;UIO 6850 status register
SIODR	equ	$F007		;data register
RDRF	equ	$01		;receive data available
TDRE	equ	$02		;ready to transmit

* Binary file equates

DATAREC	equ	$3C		;flag that a data record follows
JUMPREC	equ	$78		;flag that a jump record follows

*----------------------------------------------------------------
*  S-Record load entry point is $FD00. Load S-Record data from
*     the UIO serial port into memory.
*----------------------------------------------------------------
	org	ROMBASE
	lds	#stack		;init stack
	bsr	initSio		;init 6850

srLoad	bsr	uioRead		;look for an S record
	subb	#'S
	bne	srLoad

	bsr	uioRead		;read next to see if S1 or S9
	cmpb	#'9
	beq	s9Rec		;S9
	cmpb	#'1		;S1?
	bne	srLoad		;no, ignore

	clra			;accumulate checksum in A
	bsr	rdByte		;get the byte count
	subb	#2		;take out the address bytes
	stab	byteCnt		;save the read counter
	bsr	rdWord		;read the address and put in X

rdLine	bsr	rdByte		;read a data byte
	dec	byteCnt		;repeat for all bytes on the line
	beq	valCsum		;done, go validate the checksum

	stab	0,x		;save the byte
	cmpb	0,x
	bne	memErr		;didn't save properly
	inx
	bra	rdLine

* rdNibl - Read an ASCII hex nibble into B in binary. To save space,
*    error checking is not done and hopefully the line checksum
*    will detect any errors.

rdNibl	bsr	uioRead		;get next character
	subb	#'0		;convert to binary
	cmpb	#9
	bls	rnExit		;valid 0-9
	subb	#7		;else adjust for A to F
rnExit	rts

* rdByte - Read an ASCII hex byte into B in binary

rdByte	bsr	rdNibl		;get msn in B
	aslb			;put in MSN
	aslb
	aslb
	aslb
	stab	temp
	bsr	rdNibl		;get lsn in B
	addb	temp		;form final byte
	aba			;update checksum
	rts

* rdWord - Read an ASCII hex word into X in binary

rdWord	bsr	rdByte		;read MSB into XHI
	stab	saveX
	bsr	rdByte		;read LSB into B
	stab	saveX+1
	ldx	saveX
	rts

* uioRead - Read a character from the UIO serial port into B

uioRead	ldab	SIOSR		;wait for new character
	rorb
	bcc	uioRead

	ldab	SIODR		;load and return char
	andb	#$7F
	rts

* valCsum - Validate the checksum. If good, go look for another line

valCsum	inca			;validate checksum
	beq	srLoad		;good - keep going

* hexErr, memErr - Die in a loop displaying the error code

hexErr	ldab	#'C		;indicate hex/checksum error
	fcb	$8C		;CPX skip trick
memErr	ldab	#'M

dieLoop	jsr	OUTCH		;die here sending error code
	bra	dieLoop

* s9Rec - Load complete, exit to the 680 monitor

s9Rec	jmp	MONITR

* initSio - Initialize the 6850 on the UIO

initSio	ldaa	#3		;reset
	staa	SIOSR
	ldaa	#$15		;8N1
	staa	SIOSR
	rts

*----------------------------------------------------------------
*  Binary load entry point is $FD80. Load BINLOAD checksum
*     format binary data into memory.
*----------------------------------------------------------------
	org	ROMBASE+$80
	lds	#stack		;init stack
	bsr	initSio		;init 6850

binLoad	bsr	binRead		;look for a sync byte
	cmpb	#DATAREC	;data record?
	beq	loadRec		;yes

	cmpb	#JUMPREC	;jump record?
	bne	binLoad		;no, keep looking

	bsr	getAddr		;get jump-to address in X
	ldx	saveX		;X=start adddress
	jmp	0,x		;jump to loaded code

* loadRec - Load a data record

loadRec	bsr	getAddr		;get pointer in X for data
	bsr	binRead		;get byte count to read
	stab	byteCnt
	clra			;init checksum

loadLp	bsr	binRead		;load, save, and verify byte
	stab	0,x
	cmpb	0,x
	bne	memErr

	aba			;update checksum
	inx
	dec	byteCnt
	bne	loadLp

	bsr	binRead		;read checksum
	sba
	bne	hexErr		;didn't match

	bra	binLoad		;else, look for next record

* getAddr - Read two byte address from the cassette interface
*   and return it in X

getAddr	bsr	binRead		;get MSB
	stab	saveX
	bsr	binRead		;get LSB
	stab	saveX+1
	ldx	saveX		;X contains the address
	rts

* binRead - Read a byte from the console serial port into B

binRead	ldab	SIOSR		;wait for new byte
	rorb
	bcc	binRead

	ldab	SIODR		;load and return byte
	rts

* Data and stack are in the top 16 bytes of the first 16K of RAM

	org	$3FF0
	rmb	11		;12 bytes of stack space
stack	rmb	1
saveX	rmb	2		;X register save
temp	rmb	1
byteCnt	rmb	1		;byte counter

	end
