*****************************************************************************
;
;  PCGET - Transfer a file from the PC to a Poly-88xx running System-88
;	(Exec) using the XMODEM protocol.
;
;       For text files, Exec uses CR as the line terminator, not CR/LF.
;	Also, null (0) is used to mark end of file and fill to the end
;	of a sector, not ctrl-z (1Ah). For these reasons PCGET must
;	treat text and binary files differently.
;
;	For all file types, two 16 bit values must be written to the
;	directory for a new file: Load Address and Start Address. 
;	For an executable file, these fields must contain the load
;	and run address of the program as expected. However, these
;	fields have different uses for other file types. For text
;	files, both fields must be zero. For a relocatable file, the
;	start address must be zero and the load address contains the
;	offset to a relocation bitmap within the file. For some form
;	files, the start address contains a parameter used by the 
;	forms program.
;
;	Since none of the above information is present in the file
;	to be transferred from the PC, PCGET uses the receive file
;	extension the operator provides to make a best guess as to
;	the text/binary, load address, and start address parameters.
;	The user is then given the opportunity to confirm or change
;	these values as required.
;
;	Rev	 Date	    Desc
;	1.0	12/26/17    M. Douglas, Original
;
;	1.1	01/11/18    M. Douglas
;			    Allow an existing file to be overwritten if
;			    the new file is the same size or smaller than
;			    the original. Add '.AS' and '.BS' (assembly
;			    source and BASIC program) to the extension
;			    table as text files.
;
;*****************************************************************************

; Poly PROM equates

USER	equ	3200h		;start of user programs
Ioret	equ	0064h		;exit from interrupt
HEXC	equ	03AAh		;input hex value to HL
Deout	equ	03D1h		;print DE in hex on screen
CONOUT	equ	0C24h		;console output wormhole 1
SRA4	equ	0C16h		;serial port interrupt jump vector
PVEC	equ	2D93h		;contains ctrl-y processing vector
CMPTR	equ	2DC7h		;command line pointer

Dio	equ	0406h		;disk I/O
Msg	equ	040Ch		;display text string
Err	equ	040Fh		;display error from DE and enter Exec
Gover	equ	0415h		;load and execute overlay
R1we	equ	0427h		;prompt then read line from console

; XMODEM equates

PKTLEN	equ	128		;128 byte xmodem packet length
SOH	equ	01h
EOT	equ	04h
ACK	equ	06h
NAK	equ	15h
EOF	equ	1Ah		;ctrl-z character

; Poly serial port equates

SIODR	equ	00h		;8251 data register
SIOCR	equ	01h		;8251 control register

SIORDA	equ	02h		;8251 receive data available
SIOXCMP	equ	04h		;8251 transmit complete
SIORENA	equ	26h		;8251 receive enabled
SIOXENA	equ	27h		;8251 receive and transmit enabled

SETUP	equ	02ADh		;setup 8251 from table (in Poly PROM)	
ONESEC	equ	23332		;rcvByte loop count for 1 second

; Misc equates

MAXRAM	equ	0F800h		;max RAM address to check (e.g., video) 
CR	equ	0Dh		;ascii for carriage return
LF	equ	0Ah		;ascii for line feed
CTRLZ	equ	1Ah		;ascii for ctrl-z (end of file on PC)
IBLEN	equ	20h		;length of input buffer

;-----------------------------------------------------------------------------
;  Start of code
;-----------------------------------------------------------------------------
	org	USER		;standard Exec program load address

	jmp	start		;first entry jump vector
	jmp	start		;re-entry jump vector (keep this)

start	lxi	h,0		;save stack pointer in savStk
	dad	sp
	shld	savStk

	lhld	PVEC		;save current ctrl-y processor
	shld	savPvec

	lxi	h,pgmExit	;set ctrl-y processor to pgmExit
	shld	PVEC

	call	sizeRam		;get maximum RAM page to use
	call	serInit		;init serial I/O
	call	fOpen		;open filename specified on command line
	call	getType		;set params based on file type

	lxi	h,mStart	;display "start transfer" prompt
	call	Msg

	xra	a		;init msb of sector count to zero
	sta	numSecs+1	
	inr	a		;init xmodem block number to one
	sta	blkNum
	lxi	h,rcvBuf	;HL->receive buffer
	
	mvi	a,NAK		;send the starting NAK after ensuring
	call	purgSio		;   nothing is being received

	call	bufPkts		;buffer as many packets as possible
	call	write		;write them to disk
	call	fClose		;close the file

	lxi	h,mDone		;HL->'File written' message
	call	Msg
				;fall into program exit

; pgmExit - Exit program. Disable serial interrupts, restore
;    stack, restore ctrl-y processor

pgmExit	xra	a		;disable serial port 
	out	SIOCR		;(prevents further interrupts)

	lhld	savPvec		;restore ctrl-y processor
	shld	PVEC

	lhld	savStk		;restore stack pointer
	sphl
	
	ei			;needed if entered via ctrl-y

	mvi	a,CR		;CR to console
	jmp	CONOUT		;display CR and exit

;------------------------------------------------------------------
; sizeRam - Determine how much RAM we have for buffering tracks.
;   Sets the lastPg variable which is the last page in RAM.
;------------------------------------------------------------------
sizeRam	lxi	h,rcvBuf+100h	;HL->next page after rcvBuf

ramLoop	mov	a,m		;a=current RAM content
	inr	m		;change RAM
	cmp	m		;did RAM change?
	mov	m,a		;restore RAM
	jz	ramEnd		;end of RAM found

	inr	h		;next page
	mov	a,h		;reached max allowed address?
	cpi	MAXRAM/256
	jnz	ramLoop		;loop if not

ramEnd	dcr	h		;subtract 256 bytes from end of RAM
	mov	a,h		;save max memory page number
	sta	lastPg

	ret

;----------------------------------------------------------------------------
; serInit - initialize serial port and serial interrupt logic
;-----------------------------------------------------------------------------
serInit	call	SETUP		;initialize 8251 from following table 
	db	01Fh		;device 1 (printer port), 9600 baud
	db	0AAh		;fake sync character
	db	040h		;reset gets us to mode level
	db	04Eh		;mode async, 8N1, 16x
	db	000h		;command: idle, also end of table

	xra	a		;A=0		
	sta	rcvFlag		;no receive byte present

	lxi	h,serIsr	;HL->local serial I/O ISR
	shld	SRA4		;store local ISR

	mvi	a,SIORENA	;A=enable receive (not xmit)
	out	SIOCR

	ret

;-----------------------------------------------------------------------------
; fOpen - Open output file specified on command line.
;    If no output file is provided, display program usage information
;    and exit. Otherwise, use Gfid to retrieve/create an FDE. Copy the 
;    filename extension, disk start address, file size, load address,
;    and start address from the FDE to a local copy of each.
;-----------------------------------------------------------------------------
fOpen	lhld	CMPTR		;HL->command line parameters
	mov	a,m		;anything entered?
	cpi	CR
	jz	errHelp		;no file specified, exit with help message

	mvi	a,81h		;init Gfid command to FDE replace
	sta	gfidCmd

	lxi	d,fdeBuf	;DE->buffer for FDE returned by Gfid
	mvi	a,40h		;lookup the file name pointed to by HL
	call	Gover		;call Gfid overlay
	db	'Gfid'
	jnc	foCopy		;file exists, go copy data from the FDE

	mvi	a,3		;only allowable error is 0300h, not found
	sub	d
	jnz	errExit		;exit to Exec with error
	
	cmp	e		;test lsb of 0300h (A is zero)
	jnz	errExit		;exit to Exec with error

	inr	a		;A=1
	sta	gfidCmd		;GFid command is to enter new FDE

foCopy	lxi	h,fdeBuf+1	;HL->1st byte of the FDE
	mov	a,m		;A=byte with filename length
	ani	1Fh		;A=filename length alone

	inx	h		;HL->1st byte of filename
	mvi	d,0		;form DE=offset to filename extension
	mov	e,a		;E=length of filename
	dad	d		;HL->filename extension
	shld	extPtr		;save the pointer

	lxi	d,fnExt		;DE->start of local copy of FDE fields
	mvi	b,10		;number of bytes to copy

foLoop	mov	a,m		;copy from FDE to local fields
	stax	d
	inx	h
	inx	d
	dcr	b
	jnz	foLoop
	
	ret

;-----------------------------------------------------------------------------
; getType - Look up the file extension in the extension table and set
;   the txtFile flag from the table. If this is a new file (file length
;   is zero), then set the start address and load address fields from
;   the table. Otherwise, leave their original values.
;-----------------------------------------------------------------------------
getType	lda	numSecs		;A=file size
	ora	a		;if zero, assume this is a new file
	jnz	gtInit		;existing file, leave load, start addr alone

	lxi	h,0		;set load and start address to zero
	shld	ldAddr		;  for new files
	shld	stAddr

gtInit	xra	a		;init text file flag to false
	sta	txtFile

	lhld	fnExt		;HL=filename extension
	xchg			;DE=filename extension (in E,D)
	lxi	b,3		;adder to HL after each check
	lxi	h,extTbl	;HL->extension table
	
gtLoop	mov	a,m		;A=1st byte of extension
	ora	a		;test for zero (end of table)
	jz	gtVfy		;match not found, go to user verify step

	cmp	e		;compare 1st byte of extension
	inx	h
	jnz	gtNext		;no match, check next extension

	mov	a,m		;compare 2nd byte of extension
	cmp	d

gtNext	inx	h		;HL points past extension
	jz	gtMatch		;jump if extension matches table
	
	dad	b		;HL->next entry in extTbl
	jmp	gtLoop

gtMatch	mov	a,m		;A=text file flag from table
	sta	txtFile
	
	lda	numSecs		;A=file size
	ora	a		;if zero, assume this is a new file
	jnz	gtVfy		;existing file, don't update load & start
	
	inx	h		;HL->load address
	mov	e,m		;lsb
	inx	h
	mov	d,m		;DE=load address
	xchg			;HL=load address
	shld	ldAddr		;save as load and start address
	shld	stAddr

; gtVfy - Verify the text file and address parameters with the user

gtVfy	lxi	h,mText		;HL->text mode transfer message
	lda	txtFile		;test text file flag
	ora	a
	jnz	gtVfy2		;using text mode transfer
	
	lxi	h,mBinary	;HL->binary mode message
	
gtVfy2	call	Msg		;display text/binary mode

	lxi	h,mAddr		;HL->addresses message
	call	Msg		;display it
	
	lhld	ldAddr		;HL=load address
	xchg			;DE=load address
	call	DEOUT		;display load address
	
	mvi	a,' '		;space between addresses
	call	CONOUT
	
	lhld	stAddr		;HL=start address
	xchg			;DE=start address
	call	DEOUT		;display start address

gtVfy3	lxi	d,mChange	;prompt for settings change?
	call	rdLineA		;display message and get response
	cpi	'n'
	rz			;no changes requested
	
	cpi	'y'
	jnz	gtVfy3		;invalid response, ask again
	
; gtChg - User wants to change the values

gtChg	lxi	d,mNewTxt	;prompt for text or binary mode
	call	rdLineA		;get first character of input from user
	cpi	't'		;text mode?
	jz	gtChg2		;yes
	
	sui	'b'		;binary?
	jnz	gtChg		;invalid response, start over
	
gtChg2	sta	txtFile		;save text file flag

	lxi	h,mNewLd	;new load address prompt
	call	Msg	
	call	HEXC		;get address in HL
	shld	ldAddr
	
	lxi	h,mNewSt	;new start address prompt
	call	Msg	
	call	HEXC		;get address in HL
	shld	stAddr

	mvi	a,CR		;output a CR
	call	CONOUT
	
	jmp	gtVfy		;re-verify values with iser

;-----------------------------------------------------------------------------
; bufPkts - Receive XMODEM packets until EOT marks the end of the
;    transmission or we run out of RAM.
;-----------------------------------------------------------------------------
bufLoop	mvi	a,ACK		;ACK the previous packet
	call	serOut

bufPkts	call	rcvPkt		;receive a packet
	cpi	EOT		;EOT received?
	jz	bufDone		;yes, buffering is done

	lda	lastPg		;A=last page of RAM
	cmp	h		;are we in the last page?
	jnz	bufLoop		;no, buffer some more
	
	mvi	a,EOT		;tell sender we give up
	call	serOut

	lxi	h,mRam		;HL->out of RAM message
	call	Msg		;display the message
	jmp	pgmExit		;exit
	
bufDone	mvi	a,ACK		;ACK the EOT
	call	serOut

	mov	a,l		;need to zero fill to page boundary?
	ora	a
	rz			;no

	xra	a
bufFill	mov	m,a		;zero fill 256 byte sector
	inr	l
	jnz	bufFill

	inr	h		;make HL correct
	ret
	
;-----------------------------------------------------------------------------
; rcvPkt - receive an XMODEM format 128 byte packet. HL points to the
;    buffer to write into. HL is updated by the number of bytes written
;    after each successful reception. This may be less than 128 bytes
;    for text files.
;-----------------------------------------------------------------------------
nakPkt	pop	h		;get HL back
	mvi	a,NAK
	call	purgSio		;purge receive for 1s then transmit NAK

; Wait for SOH or EOT

rcvPkt	push	h		;save HL for retries

waitSoh	mvi	a,3		;3 second timeout for soh
	call	rcvByte		;receive character with timeout
	jz	nakPkt		;timeout

	cpi	SOH		;SOH received?
	jz	haveSoh

	cpi	EOT		;EOT to say we're done?
	jnz	waitSoh		;no, keep looking for SOH

	pop	h		;undo the entry push
	ret			;return with EOT

; Have SOH, receive block number and not block number

haveSoh	mvi	a,1		;one second timeout once inside a packet
	call	rcvByte		;get the block number
	jz	nakPkt		;timeout, NAK and start over

	sta	rcvBlk		;save the received block number
	mvi	a,1		;one second timeout
	call	rcvByte		;get not block number
	jz	nakPkt		;timeout, NAK and start over

	sta	rcvNBlk		;save not block number

; Receive the 128 byte block

	mvi	e,0		;init checksum
	mvi	d,pktLen	;d is byte counter

pktLoop	mvi	a,1		;one second timeout
	call	rcvByte		;get next data byte
	jz	nakPkt		;timeout

	mov	b,a		;save character in b
	add	e		;update the checksum
	mov	e,a

	lda	txtFile		;text file?
	ora	a
	mov	a,b		;restore character
	jz	notText		;no

	cpi	LF		;line feed?
	jz	skipChr		;yes, don't store it

	cpi	CTRLZ		;end of file character?
	jnz	notText		;no, go on

	xra	a		;store zero instead of ctrl-z

notText	mov	m,a		;store the character
	inx	h		;point to next byte

skipChr	dcr	d		;decrement bytes remaining
	jnz	pktLoop

; Verify the checksum

	mvi	a,1		;one second timeout
	call	rcvByte		;get the checksum
	jz	nakPkt		;timeout

	cmp	e		;checksum match?
	jnz	nakPkt		;no, packet error

; Checksum good. Verify block numbers

	lda	rcvNBlk		;verify rcvBlk = not (rcvNBlk)
	cma			;A should = block number now
	mov	b,a		;save in b
	lda	rcvBlk
	cmp	b		;compare rcvBlk = not(rcvNBlk)?
	jnz	nakPkt		;no, error

	lda	blkNum		;compare rcvBlk = expected blkNum
	cmp	b
	jnz	nakPkt		;nak if not the same (also nak's re-send)

	inr	a		;increment expected block number
	sta	blkNum

	pop	b		;get HL off stack, but don't clobber HL
	xra	a		;return a zero
	ret

;-----------------------------------------------------------------------------
; write - Write the receiver buffer to disk. Dio does a write verify.
;    On entry, HL is expected to point to the next byte after the end
;    of received data.
;-----------------------------------------------------------------------------
write	mov	a,h		;A=end of data page
	sui	rcvBuf/256	;A=number of pages to write
	mov	b,a		;B=number of pages to write
	
	lxi	h,numSecs	;HL->numSecs
	mov	a,m		;currently zero? (new file)
	ora	a
	jz	wrNew		;yes, this is a new file
	
	cmp	b		;compare old size - new size
	jnc	wrNew		;new is same size or smaller
	
	lxi	h,mNoRepl	;else, HL->can't replace file message
	call	Msg		;display the message
	jmp	pgmExit		;and exit
	
wrNew	mov	m,b		;numSecs=number of sectors to write

	lda	fdeBuf		;1st byte contains drive number
	ani	0Fh		;get drive number alone
	mov	c,a		;C=drive to use
	
	lxi	d,rcvBuf	;DE->received file in memory	
	lhld	dskAddr		;HL=disk address (sector) from the FDE
	mov	a,b		;A=number of sectors to write
	mvi	b,0		;B=write command
	call	DIO		;issue write command (in PROM)
	rnc			;exit if no error

	mov	a,d		;check for 0101 error
	add	e
	cpi	2		;only 0101 error will give 2
	jnz	errExit		;not 0101, go display the error

	lxi	d,0505h		;assume 0101 error was because we passed
	jmp	errExit		;  more sectors than room left on disk

;-----------------------------------------------------------------------------
; fClose - Close the output file. Updates the FDE buffer with the 
;    new file flag, number of sectors, load address, start address,
;    and then calls Gfid to enter the new FDE in the directory.
;-----------------------------------------------------------------------------
fClose	lda	fdeBuf+1	;A=FDE flags and filename length
	ani	1Fh		;clear all flags
	ori	20h		;set the "new" (archive) bit
	sta	fdeBuf+1

	lhld	extPtr		;HL->filename extension in FDE
	lxi	d,numSecs-fnExt	;DE=offset to numSecs field
	dad	d		;HL->number of sectors in FDE

	lxi	d,numSecs	;DE->local number of sectors field
	mvi	b,6		;number of bytes to move
		
fdeMove	ldax	d		;copy data to fdeBuf
	mov	m,a
	inx	h	
	inx	d
	dcr	b
	jnz	fdeMove

	lxi	h,fdeBuf	;HL->fdeBuf
	lda	gfidCmd		;A=replace or enter new FDE
	call	Gover		;call Gfid overlay
	db	'Gfid'
	jc	errExit		;exit with error

	ret
	
;-----------------------------------------------------------------------------
; Error processing
;-----------------------------------------------------------------------------
; errHelp - Display program info and usage, exit to Exec

errHelp	lxi	h,mHelp		;HL->help message 
 	call	Msg		;display it
 	jmp	pgmExit		;exit
 	
; err0702 - Display file exists error, exit to Exec

err0702	lxi	d,0702h		;error=file exists

errExit	xra	a		;disable serial port
	out	SIOCR		;(prevents further interrupts)

	jmp	Err		;display error and exit to Exec

;-----------------------------------------------------------------------------
; rdLineA - Display prompt message pointed to by DE, then read a line of
;    input from the console. The input line is read into inBuf using
;    R1we. Return the 1st character in A forced to lower case. If nothing
;    is typed in, zero is returned in A.
;-----------------------------------------------------------------------------
rdLineA	lxi	h,inBuf		;HL->user input buffer
	lxi	b,100h+IBLEN	;B=1=don't echo terminator, C=inBuf length
	call	R1we		;get input from user

	mov	a,b		;A=read length	
	ora	a		;anything typed?
	rz			;nothing typed, return zero

	lda	inBuf		;A=first character typed
	ori	20h		;upper case letters to lower case
	ret

;-----------------------------------------------------------------------------
; purgSio - Wait for 1 second of idle time on receive line, then transmit the
;    character passed in A (usually ACK or NAK).
;-----------------------------------------------------------------------------
purgSio	push	a		;preserve character to send

purge	mvi	a,1		;1 second timeout
	call	rcvByte		;wait for 1 second without activity
	jnz	purge

	pop	a		;get back the character to send
	jmp	serOut		;send one character

;-----------------------------------------------------------------------------
; rcvByte - Receive a byte from the serial port. A wait timeout in second
;     is specified in A. If a timeout occurs, zero is returned in A and
;     the zero flag is true. Otherwise, the character is returned in A
;     (could be zero) and the zero flag is false. ONESEC must be set based
;     on processor speed and the number of cycles in the serIn call + 51.
;     Clobbers B and C.
;-----------------------------------------------------------------------------
rcvByte	push	d		;save DE
	mov	d,a		;save timeout in D

initSec	lxi	b,ONESEC	;BC=one second timeout

rcvWait	call	serIn		;(17+cycles in serIn) look for a byte
	jnz	haveChr		;(10) byte received

	dcx	b		;(5) otherwise, decrement timer
	mov	a,b		;(5) one second expire?
	ora	c		;(4)
	jnz	rcvWait		;(10) no, keep waiting

; one second timeout has occured. Decrement the seconds counter.

	dcr	d		;decrement seconds counter
	jnz	initSec		;initialize for another 1 second count

haveChr	pop	d		;restore d, e
	ret

;----------------------------------------------------------------------------
; serIn - input character from serial port and return in A
;    inputs:
;    outputs: Z true if no character present
;	      Z false if character returned in A
;    clobbers none
;    28 cycles with no character 
;-----------------------------------------------------------------------------
serIn	lda	rcvFlag		;(13) serial data available ?
	ora	a		;(4)
	rz			;(11) no

	mvi	a,0		;A=0, preserve non-zero status
	sta	rcvFlag		;clear data present flag

	lda	rcvData		;A=byte received
	ret			;return with Z false

;-----------------------------------------------------------------------------
; serOut - Transmit byte through serial port. This code assumes only single
;    byte transmissions in response to received messages
;    inputs: A = character to send
;    clobbers A
;-----------------------------------------------------------------------------
serOut	sta	xmtData		;store the byte for the ISR to pick up
	mvi	a,SIOXENA	;enable transmitter
	out	SIOCR		;ISR will immediately run
	ret

;--------------------------------------------------------------------------
; serIsr - Serial port interrupt service routine
;--------------------------------------------------------------------------
serIsr	in	SIOCR		;A=serial status register
	ani	SIORDA		;data received?
	jz	isrXmt		;no, must be a transmit interrupt

	sta	rcvFlag		;set received data flag true (non-zero)
	in	SIODR		;A=byte received
	sta	rcvData		;rcvByte = byte received

	jmp	Ioret		;restore registers and exit

; isrXmt - process a transmit interrupt

isrXmt	lda	xmtData		;get byte to transmit
	out	SIODR		;send it

isrCmp	in	SIOCR		;A=serial status register
	ani	SIOXCMP		;test for empty xmit shift register
	jz	isrCmp		;wait until character completely sent

	mvi	a,SIORENA	;disable xmit (rcv still enabled)
	out	SIOCR		;xmit off disables xmit interrupt
	
	jmp	Ioret		;restore registers and exit

;-----------------------------------------------------------------------------
;   Data Area
;-----------------------------------------------------------------------------
mHelp	db	CR
	db	'PCGET Ver 1.1 for System-88 (Exec)',CR
	db	'  Receives a file from a PC through the Poly serial',CR
	db	'  port using the XMODEM protocol.',CR
	db	CR
	db	'Usage: PCGET file.ext',CR,0
	
mText	db	CR,'Transferring as TEXT file.',CR,0
mBinary	db	CR,'Transferring as BINARY file.',CR,0
mAddr	db	'Load address, start address in hex: ',0
mChange	db	CR,CR,'Change these settings? ',0

mNewTxt	db	CR,CR,'Text or binary transfer? ',0
mNewLd	db	CR,'Enter load address in hex: ',0
mNewSt	db	CR,'Enter start address in hex: ',0

mStart	db	CR,CR,'Start XMODEM send now...',CR,0
mDone	db	'File received and written',CR,0
mRam	db	CR,'File too big, out of RAM',CR,0
mNoRepl	db	CR,'Can''t replace file, new file too big',CR,0

;**************************************************************************
; 
;  Data area
;
;**************************************************************************

; extTbl - Table of file extensions used to determine whether a file
;    is a text file or not, and the start and load address fields. If
;    an extension is not in this table, txtFile is set to false and
;    the load and start address are both set to zero. Extensions
;    that match these default parameters do not need to be included
;    in this table.

extTbl	db	'OV',0		;overlay file, binary
	dw	2000h		;start and load address

	db	'GO',0		;user executable file, binary
	dw	3200h		;start and load address

	db	'TX',1		;text file, text
	dw	0000h		;start and load address

	db	'ED',1		;editor definitions, text
	dw	0000h		;start and load address

	db	'PS',0		;printer driver, binary
	dw	3000h		;start and load address

	db	'HF',1		;help file, text
	dw	0000h		;start and load address

	db	'AS',1		;assembly source file, text
	dw	0000h		;start and load address

	db	'BS',1		;BASIC program, text
	dw	0000h		;start and load address

	db	0		;mark end of table

txtFile	ds	1		;non-zero for text file, zero for binary

; Xmodem data

blkNum	ds	1		;current xmodem block number
rcvBlk	ds	1		;block number received
rcvNBlk	ds	1		;"not" block number received
rcvPtr	ds	2		;pointer for start of XMODEM send

; Serial I/O data

rcvFlag	ds	1		;non-zero = received byte available
rcvData	ds	1		;byte received from serial port
xmtData	ds	1		;byte to transmit

; File data

fdeBuf	ds	44		;FDE as returned by Gfid
extPtr	ds	2		;pointer to filename ext in fdeBuf
gfidCmd	ds	1		;create or replace FDE command

; Copy of data from the FDE. These fields MUST REMAIN IN THIS ORDER

fnExt	ds	2		;filename extension 
dskAddr	ds	2		;starting disk address of file (FDA)
numSecs	ds	2		;number of sectors in file (DNS)
ldAddr	ds	2		;file load address (LA)
stAddr	ds	2		;start (entry) address (SA)

; Misc data

savStk	ds	2		;saved entry stack pointer
savPvec	ds	2		;saved entry PVEC pointer
lastPg	ds	1		;last useable RAM page

; Receive buffer runs from here to the end of memory

	org	($+0FFh) AND 0FF00h	;align to next page boundary

rcvBuf	equ	$
inBuf	equ	$		;console input buffer uses this area too

	end

