*****************************************************************************
*
*  Flop2PC - Transfer floppy image to PC over a serial port
*
*	This program transmits an image of a Percom 5.25" SSSD disk
*	disk through an ACIA serial port in any of the SWTPC 6800
*	I/O ports. The XMODEM protocol is used for the serial transfer.
*	The disk is copied in raw format including most of the header
*	and postamble data. The sync, track and sector bytes at the start
*	of the header are not sent. The two byte CRC and two byte postamble
*	are sent at the end of the header data prior to the 256 bytes of
*	sector data. This transfers 12+256 (268) bytes per sector, 10
*	sectors per track, 40 tracks per disk.
*
*	The SWTBUG (or MIKBUG) PROM and the Percom MiniDOS PROM must be 
*	present in computer. The MiniDOS PROM is used for disk I/O.
*
*	Since a typical MiniDOS disk is not formatted before use, sectors
*	past the last sector written will generally result in a read
*	error. The MiniDOS PROM performs multiple retries and re-seeks
*	before returning an error, so once an error is reported, this
*	program stops reading the disk in the assumption that the last
*	sector written has been reached. In general, it is best to have
*       just run a PACK operation before transferring.
*
*	If using the console port for the XMODEM transfer, error messages
*	and prompts once the XMODEM transfer is started on the terminal
*	emulator won't be seen. This makes it difficult to notice and
*	determine disk I/O problems. 
*
*	Written by Mike Douglas
*
*	Rev	 Date	    Desc
*	1.0	01/13/20    Original
*	1.1	03/15/20    Add option to use any serial port for transfer
*
*****************************************************************************

* SWTBUG (MIKBUG) and MiniDOS equates

OUTEEE	equ	$E1D1		;SWTBUG display character
INEEE	equ	$E1AC		;SWTBUG input character
PDATA1	equ	$E07E		;SWTBUG display message
dispMsg	equ	PDATA1
OUT4HS	equ	$E0C8		;display 4 hex digits
STACK	equ	$A07F		;stack in SWTBUG RAM
CONTRL	equ	$E0E3		;re-enter SWTBUG

RDSEC	equ	$C00C		;read a sector
WTSEC	equ	$C00F		;write a sector
INITRK	equ	$C027		;init track registers
TYPERR	equ	$C01E		;display error code and abort

* Page 0 data maintained by MiniDOS

	org	0
drvNum	rmb	1		;drive number in MS 2 bits
trk	rmb	1		;desired track
sctr	rmb	1		;desired sector

HEADER	equ	*		;saved portion of header starts here

bakLnk	rmb	2		;file backward link
fwdLnk	rmb	2		;file forward link
bytCnt	rmb	1		;sector byte count
addres	rmb	2		;data address vector
filTyp	rmb	1		;file type code	

ckSum	rmb	2		;sector checksum
postAm	rmb	2		;post amble

HDRLEN	equ	*-HEADER	;saved portion of header ends here

crnTrk	rmb	1		;current track
trkTbl	rmb	4		;track each drive is on
wrtAddr	rmb	2		;write buffer address
rdAddr	rmb	2		;read buffer address

* Disk information equates. Note that some code assumes SECLEN is even.

NUMTRK	equ	40		;number of tracks
NUMSEC	equ	10		;number of sectors per track
SECLEN	equ	256+HDRLEN	;sector length (as transmitted)
MINDRV	equ	1		;first drive number
MAXDRV	equ	3		;max drive number 
TRKLEN	equ	NUMSEC*SECLEN	;length of track in bytes

* XMODEM equates

PKTLEN	equ	128		;128 byte xmodem packet length
SOH	equ	$01
EOT	equ	$04
ACK	equ	$06
NAK	equ	$15
EOF	equ	$1A		;ctrl-z character
XMTRIES	equ	10		;number of xmodem retries

; MP-S serial board equates

SIORDRF	equ	$01		;receive data register full
SIOTDRE	equ	$02		;transmit data register empty

* Misc equates

CR	equ	$0D		;ASCII for carriage return
LF	equ	$0A		;ASCII for line feed
DEL	equ	$7F		;ASCII DEL
BS	equ	$08		;ASCII backspace
CTRLC	equ	$03		;ASCII for control-c

	org	$0100		;load address
*-----------------------------------------------------------------------------
*   Initialize for transfer
*-----------------------------------------------------------------------------
init	lds	#STACK		;initialize stack pointer
	jsr	sizeRam		;determine amount of RAM available
	jsr	INITRK		;MiniDOS, initialize drives

* Display welcome message, then get the drive number

	ldx	#mWelcom	;display welcome message
	jsr	dispMsg

getDrv	ldx	#mDrive		;display drive number prompt
	jsr	dispMsg

	jsr	rcvCon		;get byte from the console
	oraa	#$20		;upper case letters to lower case
	cmpa	#'x'		;exit requested?
	bne	chkDrv		;no, check the drive number entered

	jmp	pgmExit		;else, exit as requested

chkDrv	suba	#MINDRV+'0'	;ASCII 1st drive goes to binary 0
	cmpa	#MAXDRV-MINDRV+1   ;validate <= max drive
	bcc	getDrv		;invalid drive number, prompt again
	
	adda	#MINDRV		;restore offset of 1st drive (if any) 
	rora			;put drive num into two MSbits
	rora
	rora
	anda	#$C0		;be sure drive bits are alone
	staa	drvNum		;save the drive number to use
	
* Get the serial port to use for the transfer

getPort	ldx	#mPort		;display port number prompt
	jsr	dispMsg

	jsr	rcvCon		;get byte from the console
	oraa	#$20		;upper case letters to lower case
	cmpa	#'x'		;exit requested?
	bne	chkPort		;no, check the port number entered
	jmp	pgmExit		;else, exit as requested

chkPort	suba	#'0'		;subtract ACII offset
	cmpa	#8		;must be 0-7
	bcc	getPort		;invalid port, re-enter

	asla			;convert to port address
	asla
	staa	portAdr+1	;LSB of port address
	ldab	#$80		;MSB of port address
	stab	portAdr

	ldx	portAdr		;initialize transfer port
	cmpa	#$04		;using console?
	beq	skipRst		;yes, skip port reset
	
	ldaa	#3		;reset serial port
	staa	0,x

skipRst	ldaa	#$15		;8N1
	staa	0,x

* Prompt the user to start the XMODEM receive operation on the PC. 
*    Wait for a clear receive line for a least one second, then start
*    looking for NAK from the PC.

	ldx	#mStart		;display start file transfer prompt
	jsr	dispMsg

clrRcv1	jsr	rcvByte		;loop until input clear for 1 second
	bne	clrRcv1
	
waitNak	jsr	rcvByte		;wait for a character
	cmpa	#NAK
	bne	waitNak		;have a NAK, use checksum protocol

* NAK received meaning the XMODEM receive has started. Initialize
*     for the transfer.

	ldx	#0		;init total sector count to zero
	stx	secCnt

	clr	trkNum		;starting on track 0
		
	ldaa	#1		;init xmodem block number to one
	staa	blkNum
	
	ldx	#trkBuf		;trkBuf is initial starting point for
	stx	sndPtr		;   XMODEM send

*-----------------------------------------------------------------------------
*  mainLp - Loop through all tracks on the disk buffering as many tracks
*    as will fit in RAM before sending via xmodem, then repeat.
*-----------------------------------------------------------------------------
mainLp	ldx	#trkBuf		;disk buffering always starts at trkBuf
	stx	trkPtr

* Read and buffer tracks until RAM is full or all tracks have 
*    been read.

bufLoop	bsr	readTrk		;read into trkBuf

	inc	trkNum		;increment track number
	ldaa	trkNum		;done all tracks on the disk?
	cmpa	#NUMTRK		;done all tracks on the disk?
	beq	sendBuf		;yes, go send the buffered tracks
	
	ldaa	trkPtr+1	;compare trkPtr-bufEnd
	suba	bufEnd+1
	ldaa	trkPtr		;MSBs
	sbca	bufEnd
	bcs	bufLoop		;still room, keep going

* Track buffer is full or all tracks have been read. Send the buffered
*    tracks via xmodem.

sendBuf	jsr	sndTrks		;send the buffered tracks via xmodem

	ldaa	trkNum
	cmpa	#NUMTRK		;done all tracks?
	bne	mainLp		;no, not done yet

* The entire disk has been read and (mostly) transmitted. See if there are
*    left over bytes to send. If so, send them.

	ldaa	sndPtr+1	;A=LSB of transmit pointer
	cmpa	#trkBuf&$FF	;pointing to LSB of address of trkBuf?
	beq	noExtra		;same, no extra bytes to send
	
	jsr	sndPkt		;otherwise, send one last packet

* Send EOT and wait for response. Then display the success message and
*    start the program over.

noExtra	jsr	sndEot		;send and get response for EOT

	ldx	#mDone		;print the all done message
	jsr	dispMsg

	ldx	#secCnt		;X->count of sectors read
	jsr	OUT4HS		;display count

	ldx	#mSects		;"$xxxx disk sectors transferred."
	jsr	dispMsg

	jmp	getDrv		;start over asking for a drive
	
*-----------------------------------------------------------------------------
* readTrk - read NUMSEC sectors from the current track into a trkBuf as
*   pointed to by trkPtr. After the track is read, trkPtr is updated
*   by the length of a track to point to the next track buffer.
*-----------------------------------------------------------------------------
readTrk	clr	secNum		;start with sector 0

rtLoop	ldx	trkNum		;set track and sector in header
	stx	trk

	bsr	secAddr		;get buffer address based on sector
	jsr	RDSEC		;call read sector in MiniDOS PROM
	bcs	rtError		;branch if error occurred

	bsr	moveHdr		;move header data to start of sector
	
	ldx	secCnt		;increment total sector count
	inx
	stx	secCnt
	
	jsr	nextSec		;bump to next sector
	bne	rtLoop		;read entire track

* Entire track read. Increment trkPtr by TRKLEN and exit
	
rtExit	ldaa	trkPtr+1	;increment by TRKLEN
	adda	#TRKLEN&$FF
	staa	trkPtr+1
	
	ldaa	trkPtr		;MSB
	adca	#TRKLEN/256
	staa	trkPtr
	
	rts	
	
* rtError - Read error. Assume this is the end of data on the disk. Clear
*    all unread sectors and headers to zeros.

rtErrLp	bsr	secAddr		;set rdAddr to point to sector buf

rtError	dec	rdAddr		;back up data pointer by $100
	ldx	rdAddr		;X->failed sector address-$100
	
	ldab	#SECLEN/2	;B=num words to clear (ASSUMES EVEN SECLEN)
	clra			;A=0 to clear sector data

clrSec	staa	-HDRLEN,x	;clear header and sector data
	staa	-HDRLEN+1,x
	inx
	inx
	decb
	bne	clrSec

	bsr	nextSec		;get next sector number
	bne	rtErrLp		;clear remaining sectors on track

	ldaa	#NUMTRK-1	;force end of reading by caller
	staa	trkNum

	bra	rtExit

*------------------------------------------------------------------
* secAddr - Given sector number in secNum, return a pointer in X
*    to the data portion of this sector in the track buffer
*------------------------------------------------------------------
secAddr	ldx	#multTbl	;X->multiply by sector length table

	ldaa	secNum		;A=sector to read
	asla			;two bytes per table entry
	staa	*+4		;modify offset of following ldx
	ldx	0,x		;offset modified by previous instruction
	stx	rdAddr		;rdAddr=secNum*SECLEN+HDRLEN

	ldaa	trkPtr+1	;add in base from trkPtr
	adda	rdAddr+1
	staa	rdAddr+1

	ldaa	trkPtr		;MSB
	adca	rdAddr
	staa	rdAddr
	
	rts

* multTbl - Used to multiply sector number by sector length and
*    add the header length to point to the 1st data byte.

multTbl	fdb	0*SECLEN+HDRLEN,1*SECLEN+HDRLEN,2*SECLEN+HDRLEN
	fdb	3*SECLEN+HDRLEN,4*SECLEN+HDRLEN,5*SECLEN+HDRLEN
	fdb	6*SECLEN+HDRLEN,7*SECLEN+HDRLEN,8*SECLEN+HDRLEN
	fdb	9*SECLEN+HDRLEN

*------------------------------------------------------------------
* movHdr - Move the 12 header bytes from page 0 into the first
*    12 bytes of the sector in trkBuf.
*------------------------------------------------------------------
moveHdr	dec	rdAddr		;back up data pointer by $100
	ldx	#HEADER		;saveX->header bytes read from disk	
	stx	saveX		
	ldab	#HDRLEN		;B=number of bytes to move
	
mhLoop	ldx	saveX		;X->header byte
	ldaa	0,x		;A=header byte
	inx
	stx	saveX
	
	ldx	rdAddr		;X->sector in track buffer-$100
	staa	-HDRLEN,x
	inx
	stx	rdAddr
	
	decb			;decrement byte count
	bne	mhLoop		;loop for all ten bytes

	rts

*-----------------------------------------------------------------------------
* nextSec - Update secNum to next sector to read using interleave of 2.
*   Exits with zero status when sector 0 is reached (full track done)
*-----------------------------------------------------------------------------
nextSec	ldaa	secNum		;move to next sector, interleave=2
	adda	#2
	staa	secNum

	cmpa	#NUMSEC		;past last sector?
	bcs	nsExit		;no, exit with non-zero status
	
	suba	#NUMSEC+1	;10->$FF, 11->0
	anda	#1		;10->1, 11->0
	staa	secNum		;return to sector 0 means done

nsExit	rts

*-----------------------------------------------------------------------------
* sndTrks - Send the tracks buffered in trkBuf using XMODEM until the next
*    packet will go past the end of queued data (sndptr+PKTLEN > trkPtr).
*-----------------------------------------------------------------------------
sndTrks	bsr	sndPkt		;send a packet

	ldaa	sndPtr+1	;compute sndPtr+PKTLEN
	adda	#PKTLEN
	staa	saveX+1

	ldaa	sndPtr		;MSB
	adca	#0
	staa	saveX

	ldaa	trkPtr+1	;compute trkPtr-(sndPtr+PKTLEN)
	suba	saveX+1
	ldaa	trkPtr
	sbca	saveX

	bcc	sndTrks		;next packet will not pass the end, continue

* All the packets we can send have been sent. Move any bytes left
*    over to just before the start of trkBuf. The next group of
*    xmodem packets will be transmitted starting there.

	ldx	sndPtr		;duplicate sndPtr in saveX
	stx	saveX

	ldab	trkPtr+1	;compute trkPtr-sndPtr = bytes left
	subb	sndPtr+1	;B=bytes to move (always < 128)

	ldaa	#trkBuf&$FF	;compute B bytes before trkBuf
	sba
	staa	trkPtr+1
	staa	sndPtr+1	;will also be sndPtr for next round

	ldaa	#trkBuf/256	;MSB
	sbca	#0
	staa	trkPtr		;trkPtr->trkBuf-B (bytes to move)
	staa	sndPtr		;will also be sndPtr for next round

	tstb			;any bytes to move?
	beq	mvExit		;no, exit

moveLp	ldx	saveX		;X->leftover byte to move
	ldaa	0,x
	inx
	stx	saveX

	ldx	trkPtr		;X->space before trkBuf
	staa	0,x
	inx	
	stx	trkPtr

	decb			;decrement bytes to mvoe
	bne	moveLp

mvExit	rts

*-----------------------------------------------------------------------------
* sndPkt - send an xmodem format 128 byte packet. sndPtr points to the 
*    128 byte buffer to send. On exit, sndPtr is updated to point to the
*    next 128 byte boundary.
*-----------------------------------------------------------------------------
sndPkt	ldaa	#XMTRIES	;init retry counter
	staa	xmRetry

* Send header bytes

reSend	ldaa	#SOH		;1st byte is SOH
	bsr	sndByte
	
	ldaa	blkNum		;2nd byte is the block number
	bsr	sndByte
	
	coma			;send complement of block number
	bsr	sndByte

* Send the 128 byte data portion of the packet followed by checksum

	ldx	sndPtr		;X->packet data
	ldaa	#PKTLEN		;set transfer length
	staa	byteCnt
	clr	chkSum		;init checksum

pktLoop	ldaa	0,x		;get next byte to send
	bsr	sndByte		;send and update checksum
	inx			;point to next byte
	dec	byteCnt		;decrement bytes remaining
	bne	pktLoop

	ldaa	chkSum		send the checksum
	bsr	sndByte

* All bytes sent. Wait for the response.

	stx	saveX		;save current data pointer
	bsr	rcvByte		;get the response character
	beq	sndFail		;timeout on response
	
	cmpa	#ACK		;ack received?
	bne	sndFail		;no, send failed
	
	ldx	saveX		;update sndPtr
	stx	sndPtr
	inc	blkNum		;increment block number
	rts

* sndFail - ACK not received, decrement retry count and try again.

sndFail	dec	xmRetry		;decrement retry counter
	beq	xmFail		;retries used up, failed xmodem transfer

clrRcv2	bsr	rcvByte		;wait for 1 second of clear line
	bne	clrRcv2

	bra	reSend		;re-send the packet

* xmFail - Display failure message then restart program

xmFail	ldx	#mXmdm		;xmodem failure message
	jsr	dispMsg
	
	lds	#STACK		;re-init stack pointer
	jmp	getDrv		;start over asking for drive

*-----------------------------------------------------------------------------
*  sndEot - send EOT character and wait for ACK response
*-----------------------------------------------------------------------------
sndEot	ldaa	#XMTRIES	;init retry counter
	staa	xmRetry

reEot	ldaa	#EOT		;send the EOT
	bsr	sndByte

	bsr	rcvByte		;wait for response
	beq	eotFail		;timeout

	cmpa	#ACK		;ACK received?
	bne	eotFail		;no, EOT send failed

eotExit	rts			;otherwise, we're done.

* Timeout waiting for ACK to EOT. Decrement retry counter and try again.

eotFail	dec	xmRetry		;decrement retry counter
	beq	eotExit		;just give up - xfer was probably good

clrRcv3	bsr	rcvByte		;wait for 1 second of clear line
	bne	clrRcv3

	jmp	reEot		;try again

*-----------------------------------------------------------------------------
* sndByte - send the byte in A through the serial port. Adds the byte 
*     to chkSum. XMODEM send routine assumes serOut returns with A
*     containing the character sent. Clobbers B.
*-----------------------------------------------------------------------------
sndByte	tab			;B=character to send
	addb	chkSum		;update the checksum
	stab	chkSum

	stx	ioSaveX		;preserve X
	ldx	portAdr		;X->serial port
		
sbLoop	ldab	0,x		;wait for OK to transmit
	andb	#SIOTDRE
	beq	sbLoop	

	staa	1,x		;send the character
	ldx	ioSaveX		;restore X
	rts

*-----------------------------------------------------------------------------
* rcvByte - Receive a byte from the serial port with a one second
*     timeout. If a timeout occurs, zero is returned in A and the zero 
*     flag is true. Otherwise, the character is returned in A and zero 
*     flag is false. 
*-----------------------------------------------------------------------------
rcvByte	ldx	#29412		;one second, 34 cycles per loop

rbLoop	stx	ioSaveX		;(6) preserve counter
	ldx	portAdr		;(5) X->serial port
	
	ldaa	0,x		;(4) see if a new character is present
	anda	#SIORDRF	;(2)
	bne	rbRead		;(4) new character present

	ldx	ioSaveX		;(5) restore counter
	dex			;(4) decrement timeout
	bne	rbLoop		;(4)

	rts			;timeout, exit with Z true
	
rbRead	ldaa	1,x		;return character in A
	inx			;ensure Z is false
	rts

*-----------------------------------------------------------------------------
* rcvCon - Receive a character from the console device, echo it, then
*   wait for a CR. Exits program if Ctrl-c typed. Returns with invalid
*   character (null) if BS or DEL pressed after 1st character
*   Returns character in a, clobbers b.
*-----------------------------------------------------------------------------
rcvCon	jsr	INEEE		;use SWTBUG to get character from console
	cmpa	#CTRLC		;abort requested?
	beq	pgmExit		;yes, exit
	
	cmpa	#CR		;return pressed?
	beq	rcExit		;yes, exit
	
	tab			;save character in B

* Wait for CR, then return 1st character typed

rcWtCr	jsr	INEEE		;use SWTBUG to get character from console
	cmpa	#CTRLC		;abort requested?
	beq	pgmExit		;yes
	
	cmpa	#DEL		;delete?
	beq	rcExit		;yes, return DEL character
	
	cmpa	#BS		;backspace?
	beq	rcExit		;yes, return the backspace
	
	cmpa	#CR		;return pressed?
	bne	rcWtCr		;no, keep waiting
	
	tba			;return 1st character typed in A
rcExit	rts

*------------------------------------------------------------------
* sizeRam - determine how much RAM we have for buffering tracks.
*   Sets the bufEnd variable which points to end address of 
*   the last possible track buffer + 1
*------------------------------------------------------------------
sizeRam	ldx	#trkBuf+$100	;look for end of RAM starting
	stx	saveX		;    at trkBuf+$100

ramLoop	ldaa	0,x		;A=current RAM content
	inc	0,x		;change value in RAM
	ldab	0,x		;B=new value from RAM
	staa	0,x		;restore original value
	
	cba			;did RAM change?
	beq	ramEnd		;no, so end of RAM found
	
	inc	saveX		;increment to next page
	ldx	saveX		;X->next page in RAM	
	bpl	ramLoop		;loop until $80xx reached

* ramEnd - end of RAM found. Now determine the end address + 1 of the
*   last track buffer that will fit in RAM. Store in bufEnd

ramEnd	ldab	saveX		;B=1st page not RAM
	ldx	#trkBuf		;start saveX at trkBuf
	stx	saveX

bfEndLp	ldaa	saveX+1		;increment saveX by TRKLEN
	staa	bufEnd+1	;bufEnd has previous value of saveX
	adda	#TRKLEN&$FF
	staa	saveX+1

	ldaa	saveX		;MSB
	staa	bufEnd		;bufEnd has previous value of saveX
	adca	#TRKLEN/256
	staa	saveX

	cba			;saveX MSB < RAM end MSB?
	bcs	bfEndLp		;yes, still more room,

	rts			;exit with bufEnd properly set

*--------------------------------------------------------------
* pgmExit - Exit to SWTBUG
*--------------------------------------------------------------
pgmExit	ldx	#mExit		;display "exiting" message
	jsr	dispMsg
	jmp	CONTRL		;jump to SWTBUG

*---------------------------------------------------------------------
* Message constants
*---------------------------------------------------------------------
mWelcom	fcb	CR,LF,LF
	fcc	'===== Floppy to PC Image Transfer, Version 1.1 ====='
	fcb	CR,LF,LF
	fcc	'Sends an image of a Percom 5.25" disk to a PC through'
	fcb	CR,LF
	fcc	'an MP-S serial port using the XMODEM protocol.'
	fcb	EOT

mDrive	fcb	CR,LF,LF
	fcc	'Insert and specify source drive (1-3) or X to exit: '
	fcb	EOT

mPort	fcb	CR,LF,LF
	fcc	'Specify the port to use for file transfer'
	fcb	CR,LF
	fcc	'  0) MP-S at $8000'
	fcb	CR,LF
	fcc	'  1) MP-S at $8004 (console port)'
	fcb	CR,LF
	fcc	'  2) MP-S at $8008'
	fcb	CR,LF
	fcc	'  3) MP-S at $800C'
	fcb	CR,LF
	fcc	'  4) MP-S at $8010'
	fcb	CR,LF
	fcc	'  5) MP-S at $8014'
	fcb	CR,LF
	fcc	'  6) MP-S at $8018'
	fcb	CR,LF
	fcc	'  7) MP-S at $801C'
	fcb	CR,LF
	fcc	'Enter port or X to exit: '
	fcb	EOT

mStart	fcb	CR,LF,LF
	fcc	'Start XMODEM receive operation on the PC now...'
	fcb	EOT

mDone	fcb	CR,LF,LF
	fcc	'Transfer complete.'
	fcb	CR,LF,EOT

mSects	fcc	'disk sectors transferred (count in hex)'
	fcb	CR,LF,EOT

mXmdm	fcb	CR,LF,LF
	fcc	'XMODEM communication failure'
	fcb	CR,LF,EOT

mExit	fcb	CR,LF,LF
	fcc	'Exiting...',CR,LF
	fcb	EOT

***************************************************************************
* 
*  Data area
*
***************************************************************************
* disk variables

trkNum	rmb	1		;track number to read
secNum	rmb	1		;sector number to read (MUST follow trkNum)
trkPtr	rmb	2		;pointer into trkBuf at track boundaries
bufEnd	rmb	2		;end address + 1 of last track spot in RAM
secCnt	rmb	2		;total count of sectors read

* xmodem variables

portAdr	rmb	2		;serial port address
xmRetry	rmb	1		;xmodem retry counter
blkNum	rmb	1		;current xmodem block number
sndPtr	rmb	2		;pointer for start of XMODEM send
chkSum	rmb	1		;packet checksum
byteCnt	rmb	1		;count of bytes to send

* misc variables

saveX	rmb	2		;generic save location for X
ioSaveX	rmb	2		;save X in I/O subroutines

* track buffer runs from here to the end of memory

	rmb	PKTLEN		;space for pre-pended unsent data
trkBuf	equ	*

	org	$A048		;auto-jump address for "G" command
	fdb	init

	end


