******************************************************************
*
* SENDS19 (for FLEX 1.0)
*    Convert a FLEX binary file to s-records and send through
*    an MP-S board in the I/O slot specified. The output is 
*    also sent to the console, so this program can be used to
*    create an s-record file using the 'O' command in FLEX. If
*    a serial port is not specified, output is only sent to the
*    console.
*
* Examples:
*    +++SENDS19 CAT.CMD 2
*	Sends CAT.CMD as s-records through serial port at $8008
*
*    +++O CAT.S19 SENDS19 CAT.CMD
*	Creates s-record version of CAT.CMD in CAT.S19
*
*    Ver    Date      Desc
*    1.0  12/9/22   Mike Douglas, Original
*
******************************************************************

* FLEX 1.0 equates

FCB	equ	$7740		;FLEX system FCB
EOL	equ	$7082		;EOL delimiter from TTYSET
FMS	equ	$7806		;entry point for FMS calls
FMSCLS	equ	$7803		;close all files
WARMS	equ	$7103		;FLEX re-entry address
GETCHR	equ	$710F		;get character from console
PUTCHR	equ	$7112		;output character to console
PSTRNG	equ	$7118		;display string on console
GETFIL	equ	$7127		;build FCB from command line parameter
NEXTCH	equ	$7121		;get next char from command line
RPTERR	equ	$713C		;report error from the FCB

* FMS equates

OPENRD	equ	$01		;open for read
FMSEOF	equ	$08		;FMS EOF error code

* Offsets into the FMS file control block (FCB)

fcbCMD	equ	0		;FMS command
fcbERR	equ	1		;error status
fcbEXT	equ	12		;file name extension
fcbBIN	equ	59		;binary/space compression flag

* MP-S serial board equates

SIOBASE	equ	$8000		;base address of serial ports
SIOCON	equ	$8004		;address of console port
SIORDRF	equ	$01		;receive data register full
SIOTDRE	equ	$02		;transmit data register empty

* Misc equates

SRECLEN	equ	32		;do 32 data bytes per s-record
EOT	equ	$04
CR	equ	$0D		;ASCII for carriage return
LF	equ	$0A		;ASCII for line feed
SPACE	equ	$20		;ASCII for space

*-----------------------------------------------------------------
* Start of code
*-----------------------------------------------------------------
	org	$0100
sendS19	bra	start
	fcb	10		;version 1.0

dspHelp	ldx	#mHelp		;display help message and exit
	jsr	PSTRNG
	jmp	WARMS

openErr	jsr	RPTERR		;display proper error message 
	jmp	pgmExit

* Validate command line file name and parameters

start	ldx	#FCB		;use the system FCB
	jsr	GETFIL		;get file name from command line
	bcs	dspHelp		;no file name - display help

	ldaa	#OPENRD		;FMS open for read
	staa	fcbCMD,x
	jsr	FMS
	bne	openErr		;open failed

	jsr	getParm		;get command line parameters
	ldaa	#$FF		;set binary mode for FMS
	staa	FCB+fcbBIN
	ldx	#0		;default transfer address = 0
	stx	xfrAddr

readLp	jsr	rdByte		;get next byte from file
	cmpa	#$16		;transfer address?
	beq	setXfer		;yes

	cmpa	#$02		;data record?
	bne	readLp		;no

* Process a data record

	jsr	rdByte		;A=load address MSB
	staa	dataPtr		
	jsr	rdByte		;A=load address LSB
	staa	dataPtr+1

	jsr	rdByte		;A=length of data record
	staa	dataLen

srecLp	ldaa	dataLen		;A=bytes left to write
	beq	readLp		;zero bytes left

	ldab	#SRECLEN	;B=default s-record data bytes
	cba			;>= full line remaining?
	bhs	wrtSrec		;yes
	tab			;else use bytes remaining

wrtSrec	stab 	byteCnt		;number of data bytes in s-record
	sba			;update count remaining
	staa	dataLen
	
	clr	chkSum		;init checksum
	ldx	#mS1		;write CR,LF,'S1'
	jsr	wrtStr
	
	ldaa	byteCnt		;write s-record byte count
	adda	#3		;3 overhead bytes
	jsr	wrtByte
	
	ldaa	dataPtr		;write s-record address
	jsr	wrtByte
	ldaa	dataPtr+1
	jsr	wrtByte

	ldaa	dataPtr+1	;update s-rec address by s-rec length
	adda	byteCnt
	staa	dataPtr+1	;LSB
	bcc	byteLp
	inc	dataPtr		;MSB if needed

byteLp	bsr	rdByte		;write the data record bytes
	bsr	wrtByte
	dec	byteCnt
	bne	byteLp
	
	ldaa	chkSum		;send the checksum
	coma			;checksum is 1's compliment of sum
	bsr	wrtByte

	bra	srecLp		;repeat until data record empty

* setXfer - Save the transfer address in xfrAddr

setXfer	jsr	rdByte		;get transfer address
	staa	xfrAddr		;MSB
	jsr	rdByte		;LSB
	staa	xfrAddr+1
	bra	readLp

*-----------------------------------------------------------------
* rdByte - Issue FMS read byte call and return next byte in A
*-----------------------------------------------------------------
rdByte	ldx	#FCB		;issue read byte command to FMS
	jsr	FMS		;get next byte
	bne	readErr		;EOF most likely
rdRts	rts

*-----------------------------------------------------------------
* wrtStr - Send EOT terminated string pointed to by X.
*    Clobbers all.
*-----------------------------------------------------------------
wrtStr	ldaa	0,x		;send next character
	cmpa	#EOT		;end of string?
	beq	rdRts		;yes, exit

	bsr	wrtChar		;else, write the charcter
	inx
	bra	wrtStr

*-----------------------------------------------------------------
* wrtByte - Write binary value in A as two ASCII-hex digits.
*    chkSum is updated with value in A. Clobbers A and B.
*-----------------------------------------------------------------
wrtByte	tab			;update checksum with the new byte		
	addb	chkSum
	stab	chkSum

	psha			;send upper nibble
	lsra
	lsra
	lsra
	lsra
	bsr	wrtNib

	pula			;send lower nibble
	anda	#$0F

wrtNib	adda	#'0
	cmpa	#'9		;0-9?
	bls	wrtChar		;yes
	adda	#7		;generate A-F
*				;fall into wrtChar

*-----------------------------------------------------------------
* wrtChar - Display character in A on the console and send
*   through the serial port in portAdr if not zero. 
*   Clobbers B.
*-----------------------------------------------------------------
wrtChar	stx	saveX		;preserve X
	ldx	portAdr		;serial port specified?
	beq	wcExit		;no

wcLoop	ldab	0,x		;wait for OK to send
	andb	#SIOTDRE
	beq	wcLoop

	staa	1,x		;send the character

wcExit	ldx	saveX		;restore X
	jmp	PUTCHR		;display on console, exit

*-----------------------------------------------------------------
* readErr - Process read error, most likely end of file.
*-----------------------------------------------------------------
readErr	ldaa	fcbERR,x	;is the error code EOF?
	cmpa	#FMSEOF
	beq	readEOF		;yes, EOF as expected
	
	jsr	RPTERR		;report the file error
	bra	pgmExit		;and exit

readEOF	ldx	#mS9		;send CR,LF,'S9'
	bsr	wrtStr

	ldaa	#3		;init chkSum (length already written)
	staa	chkSum

	ldaa	xfrAddr		;send the transfer address
	bsr	wrtByte
	ldaa	xfrAddr+1
	bsr	wrtByte

	ldaa	chkSum		;send the checksum
	coma
	bsr	wrtByte

	ldx	#mCrLf		;send trailing CR/LF
	bsr	wrtStr

pgmExit	jsr	FMSCLS		;close open files
	jmp	WARMS		;exit to FLEX

*-----------------------------------------------------------------------------
* getParm - Get command line parameters. Look for optional digit specifying
*    a transfer port number
*-----------------------------------------------------------------------------
getParm	ldx	#0		;assume port not specified
	stx	portAdr
	
gpLoop	jsr	NEXTCH		;get next command line character
	cmpa	#CR		;terminate with CR or EOL character	
	beq	gpExit
	cmpa	EOL
	beq	gpExit

	cmpa	#SPACE		;skip delimiters
	beq	gpLoop
	cmpa	#',
	beq	gpLoop

	suba	#'0		;look for digit (port number)
	cmpa	#4		;accept 0-4
	bhi	gpExit		;not a port, ignore

	asla			;convert to port address
	asla
	staa	portAdr+1	;LSB of port address
	ldab	#SIOBASE/256	;MSB of port address
	stab	portAdr

	ldx	portAdr		;initialize transfer port
	cmpa	#SIOCON AND $FF	;using console?
	beq	gpNoRes		;yes, skip port reset
	ldaa	#3		;else, reset serial port
	staa	0,x

gpNoRes	ldaa	#$15		;use 8N1 instead of 8N2
	staa	0,x
gpExit	rts


*-----------------------------------------------------------------
* Message constants 
*-----------------------------------------------------------------
mHelp	fcb	CR,LF
	fcc	'SENDS19 v1.0 - Convert binary file to s-records'
	fcb	CR,LF,LF
	fcc	'Usage:'
	fcc	'  SENDS19 FILE.EXT [PORT]'
	fcb	CR,LF,LF
	fcc	'Where:'
	fcb	CR,LF
	fcc	'  PORT = MP-S I/O slot number for transfer (0-4)'
	fcb	CR,LF,LF
	fcc	'Example:'
	fcb	CR,LF
	fcc	'  +++SENDS19 CAT.CMD 2'
	fcb	CR,LF
	fcc	'     Sends CAT.CMD as s-records through an MP-S board in'
	fcb	CR,LF
	fcc	'     I/O slot 2. S-records sent to console as well.'
	fcb	CR,LF,LF
	fcc	'  +++O CAT.S19 SENDS19 CAT.CMD'
	fcb	CR,LF
	fcc	'     Writes CAT.S19 as the s-record version of CAT.CMD.'
mCrLf	fcb	CR,LF,EOT

mS1	fcb	CR,LF
	fcc	'S1'
	fcb	EOT

mS9	fcb	CR,LF
	fcc	'S903'
	fcb	EOT	

*-----------------------------------------------------------------
* Data area
*-----------------------------------------------------------------
dataPtr	rmb	2		;pointer to data record address
dataLen	rmb	1		;length of data record
xfrAddr	rmb	2		;transfer address
byteCnt	rmb	1		;s-record data byte counter
chkSum	rmb	1		;checksum
portAdr	rmb	2		;address of serial port
saveX	rmb	2		;save area for X

	end	sendS19
