;zm.asm
;ZMODEM receive file protocol for the Commodore 64
;Copyright (C) 1992  Daniel Fandrich
;<shad04@ccu.umanitoba.ca>
;
;Version 90-08-29
;
;   This program is free software; you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation; either version 1, or (at your option)
;   any later version.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program; if not, write to the Free Software
;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;
;
;Source for Thomas J. Klehr's MASM6502 MS-DOS 6502 cross-assembler (ver 1.03)
;Ported from CBM PAL version 88-07-07 by Daniel Fandrich
;Original PAL version by Daniel Fandrich
;
;Things to do:
;	- add number of files received count
;	- there is a weakness in rzahdr -- if a line is very bad, every
;	  header could be sent wrong and the procedure will never exit --
;	  no maximum number of tries
;	- add check in zrdata so the end of the buffer is not overwritten
;Bugs:
;	- characters are often written before the start of the proper buffer
;	  in memory, esp. when line errors occur
;
;Macros ---------------------------
;
_ascii	.macro	string		;_ascii is similar to PAL's .ascii directive
	.byte	&string&
	.endm
;
;Equates --------------------------
;
true	 =	$ff		;true for boolean zmodem options
false	 =	$00		;false for boolean zmodem options
;
crc      =	$fb		;16 bits storage for crc (0-page for speed)
temp     =	$fd		;miscellaneous 0-page storage
buffer   =	$9e		;0-page storage for buffer pointer
fa       =	$ba		;disk device number (read only)
enabl	 =	$2a1		;RS-232 enables (read only)
;
fterr    =	-1		;fatal (nonrecoverable) error
timeout  =	-2		;timeout
baddata  =	-3		;bad data received
crcerr   =	-4		;crc error
speczdle =	-5		;special zdle encoded char
usercan  =	-6		;user cancelled with run/stop
nofilen  =	-7		;no file name in zfile packet
reczcan	 =	-8		;received zcan error
buffovr	 =	-9		;buffer overflow
diskerr	 =	-10		;disk error
;
zrqinit  =	0
zrinit   =	1
zsinit   =	2
zack     =	3
zfile    =	4
zskip    =	5
znak     =	6
zabort   =	7
zfin     =	8
zrpos    =	9
zdata    =	10
zeof     =	11
zferr    =	12
zcrc     =	13
zchalleng =	14
zcompl   =	15
zcan     =	16
zfreecnt =	17
zstderr  =	19
zdle     =	24
;
zdlee    =	'X
zpad     =	'*
zhex     =	'B
zbin     =	'A
zrub0    =	$6c
zrub1    =	$6d
zcrce    =	$68
zcrcg    =	$69
zcrcq    =	$6a
zcrcw    =	$6b
zcommand =	$12		;<-- check this
zcbin	 =	1		;file management options
zcnl	 =	2
;
ht       =	9
lf       =	10
cr       =	13
xon      =	17
space	 =	32
rub0     =	127
rub1     =	255
;
chrin    =	$ffcf
chrout   =	$ffd2
chkin    =	$ffc6
chkout   =	$ffc9
close    =	$ffc3
clrchn   =	$ffcc
getin    =	$ffe4
open     =	$ffc0
plot	 =	$fff0
readst   =	$ffb7
setlfs   =	$ffba
setnam   =	$ffbd
stop     =	$ffe1
;
memfac   =	$bba2		;move floating point number to fp acc
facasc   =	$bddf		;convert fp number in fac to ascii digits
;
;******************************************************************************
;Entry points for zmodem file transfers
;******************************************************************************
;
*	 =	$c000
;
;External jump table --------------
;
	 jmp	rzprog		;receive prog
	 jmp	rzauto		;receive prog (auto d/l)
	 jmp	szprog		;send program (not implemented)
	 jmp	szcmd		;send a command (not implemented)
	 jmp	initopt		;initialize options (not implemented)
	 jmp	aberr		;future expansion
	 jmp	aberr		;future expansion
	 jmp	aberr		;future expansion
	 jmp	aberr		;future expansion
	 .word	opttabl		;pointer to the option table
	 .word	0		;future expansion
	 .word	0		;future expansion
	 .word	0		;future expansion
	 .word	0		;future expansion
;
opttabl	 =	*
;
version	 .byte	1		;application program interface version
progver	 .byte	1		;zmodem driver version number*10 (= 0.1)
cps	 .word	110		;effective data rate in characters per second
bufsiz   .word	256		;size of receive buffer
				;also restrains size of zmodem data packet
bufloc   .word	buffl		;location of buffer
device	 .byte	0		;disk drive device number (0 means use previous)
drive	 .byte	'0		;drive number ('0' or '1')
quiet	 .byte	false		;don't display messages while operating
debug	 .byte	false		;display debugging messages
sendzch  .byte	0		;send zchallenge header (not implemented)
escctl   .byte	0		;zdle all control chars
zcnlcvt	 .byte	0		;true = convert all to petscii & use seq file
; Something should be added to allow forcing all files to be seq, prg or usr
; and changing the previous definition to mean force convert ascii to petscii &
; vice-versa, or automatically select, or force no conversion
; e.g.
; zfiltyp .byte	'P or 'S or 'U or 0
;  either force specific type for all files or 0 to use 'p for binary files and
;  's for text files as specified in zfile header
maxtmo   .byte	6		;max timeouts or errors in a row
maxdly   .byte	240		;max time waiting for byte
;
dispdat  .byte	false		;display other bytes while waiting for header
;

;******************************************************************************
;Main entry points
;******************************************************************************
;
;Receive program to disk
;
;Auto-download
;
rzauto	 jsr	rzauthdr	;get partial zrqinit
	 jmp	rzprog
;
;	 bcc	rzprog		;go check out header if it's ok
;
;	 jsr	showierr
;	 cmp	#crcerr		;crc error can be corrected
;	 bne	rzautex		;exit if any other error
;	 jsr	sznak		;request zrqinit again
;	 jmp	rzprog1
;
;rzautex  jsr	szcan		;cancel remote
;	 sec
;	 lda	#fterr
;	 rts			;exit with error
;
;------------------------------------------------------------------------------
;
;Manually-initiated download
;
rzprog	 jsr	szrinit		;send ready to receive
rzprog1  jsr	rzahdr		;expecting zfile
	 bcc	rzprog3
	 ldx	#<abortmes	;display "aborting" message
	 ldy	#>abortmes
	 jsr	printst
	 lda	#$01		;position in the code that caused an abort
	 jsr	sendhx
	 jsr	szcan		;abort
	 sec
	 rts
;
rzprog3	 cmp	#zfile		;continue if on track
	 beq	rzprog10
	 cmp	#zrqinit	;check for command
	 beq	rzprog5
	 cmp	#zeof		;holdover from an already finished file
	 beq	rzprog
	 cmp	#zfin		;no more files
	 bne	rzprog4
	 jmp	rzprog80
;
rzprog4	 ldx	#<nosupmes	;display "not supported" message
	 ldy	#>nosupmes
	 jsr	printst
	 lda	#$04		;position in the code that caused an abort
	 jsr	sendhx
	 jsr	szcan		;abort
	 sec
	 rts
;
rzprog5  lda	zf0
	 cmp	#zcommand	;a command?
	 bne	rzprog		;no
rzcmd    ldx	#<rcmdmess	;commands not supported message
	 ldy	#>rcmdmess
	 jsr	printst
	 jsr	szcan		;abort
	 clc
	 rts
;
;Got ZFILE
;
rzprog10 ldy	#3
rzprog11 lda	zf3,y		;get flag byte
	 sta	zfilf3,y	;save it
	 dey
	 bpl	rzprog11
;
;Receive ZFILE data packet
;
	 lda	bufloc		;point to beginning of receive buffer
	 sta	buffer
	 lda	bufloc+1
	 sta	buffer+1
	 jsr	zrdata		;get file information
	 bcc	rzprog14	;no error
	 ldx	#<zfermes	;error in zfile data
	 ldy	#>zfermes
	 jsr	printmess
	 jmp	rzprog		;back to very beginning
;
rzprog14 jsr	decodfp		;decode file info block & management options
	 bcc	rzprog20	;continue if file name block was good
	 jsr	szskip		;skip file
	 jmp	rzprog1		;await next file
;
;Open disk file for writing
;
rzprog20 ;jsr	openzfil
;
;Receive file contents
;
rzprog30 lda	#0		;always start file transmission at byte 0
	 sta	filepos		;(crash recovery not implemented)
	 sta	filepos+1
	 sta	filepos+2
	 sta	filepos+3
;
rzprog31 jsr	szrpos		;initiate transmission
rzprog32 jsr	rzahdr		;get response
	 bcc	rzprog33
	 ldx	#<abortmes	;display "aborting" message
	 ldy	#>abortmes
	 jsr	printst
	 lda	#$32		;position in the code that caused an abort
	 jsr	sendhx
	 jsr	szcan		;abort
	 sec
	 rts
;
rzprog33 cmp	#zdata		;got a data packet header?
	 beq	rzprog40
	 cmp	#zeof		;got an end-of-file packet?
	 bne	rzprog34
	 jmp	rzprog50
rzprog34 ldx	#<nosupmes	;display "not supported" message
	 ldy	#>nosupmes
	 jsr	printst
	 lda	#$33		;position in the code that caused an abort
	 jsr	sendhx
	 jsr	szcan		;abort
	 sec
	 rts
;
rzprog40 lda	zp0		;is this the correct data packet?
	 cmp	filepos
	 bne	rzprog31
	 lda	zp1
	 cmp	filepos+1
	 bne	rzprog31
	 lda	zp2
	 cmp	filepos+2
	 bne	rzprog31
	 lda	zp3
	 cmp	filepos+3
	 bne	rzprog31
;
	 lda	bufloc		;point to beginning of receive buffer
	 sta	buffer
	 lda	bufloc+1
	 sta	buffer+1
rzprog41 jsr	zrdata		;get a data packet
;** this should be changed -- a set carry might mean a zcan **
	 bcc	rzprog41b
	 jsr	showierr	;display error
	 jmp	rzprog31
;
rzprog41b pha			;save data packet type
;	 clc			;redundant
	 lda	filepos
	 adc	numdata		;add in low byte of bytes received
	 sta	filepos
	 lda	filepos+1
	 adc	#0		;add in carry
	 sta	filepos+1
	 lda	filepos+2
	 adc	#0
	 sta	filepos+2
	 lda	filepos+3
	 adc	#0
	 sta	filepos+3
	 clc
	 lda	filepos+1	;add in high byte
	 adc	numdata+1
	 sta	filepos+1
	 lda	filepos+2
	 adc	#0
	 sta	filepos+2
	 lda	filepos+3
	 adc	#0
	 sta	filepos+3
	 pla
;
	 jsr	sendhx		;** temporary -- display type of packet
	 cmp	#zcrce		;possibly the last packet -- don't respond
	 beq	rzprog43
	 cmp	#zcrcg		;don't respond
	 beq	rzprog43
	 cmp	#zcrcq		;send an ACK but don't write to disk yet
	 beq	rzprog44
;	 cmp	#zcrcw		;send an ACK and get next ZDATA header
;	 beq	rzprog42
;
rzprog42 jsr	writedsk	;write entire buffer to disk
	 jsr	szack
rzprog43 jmp	rzprog32	;go get ZDATA or ZEOF header
;
rzprog44 jsr	szack
	 jmp	rzprog41	;go get next data frame
;
rzprog50 lda	zp0
	 cmp	filepos
	 bne	rzprog43
	 lda	zp1
	 cmp	filepos+1
	 bne	rzprog43
	 lda	zp2
	 cmp	filepos+2
	 bne	rzprog43
	 lda	zp3
	 cmp	filepos+3
	 bne	rzprog43
;
;Close disk file
;
rzprog52
;	 jsr	closzfil	;close file
;	 bcs	rzprog53	;close was unsuccessful
	 jmp	rzprog		;go send ZRINIT and get next file
;
rzprog53 jsr	szferr
	 jmp	rzprog1
;
;Got ZFIN from tx
;
rzprog80 jsr	szfin
	 ldx	#<endmess	;zmodem finished message
	 ldy	#>endmess
	 jsr	printst
	 jsr	waitbufc	;wait for RS-232 transmit buffer to clear
	 clc			;transfer successful
	 lda	#0		;indicates number of files received
	 rts			;exit zmodem
;
	 nop			;debugging space
	 nop
	 nop
	 nop
	 nop
	 nop
	 nop
	 nop
	 nop
	 nop
	 nop
;
;------------------------------------------------------------------------------
;
;Send program from disk
;
; a = length of program name (perhaps change name to be null-terminated)
; x & y = address of name
;
szprog	 jmp	aberr
;
;------------------------------------------------------------------------------
;
;Send command
;
; a = length of command (perhaps change command to be null-terminated)
; x & y = address of command
;
szcmd	 jmp	aberr
;
;------------------------------------------------------------------------------
;
;Initialize user options
;
initopt  jmp	aberr
;
;------------------------------------------------------------------------------
;
;Abort with generic error code
;
aberr    lda	#fterr
	 sec
	 rts
;
;
;******************************************************************************
;High level zmodem routines
;******************************************************************************
;
;Get ZMODEM auto download header
;
rzauthdr txa
	 pha
	 ldx	#0
	 stx	hdrtyp		;0=hex header, $ff=binary
	 jmp	rhdr
;
;------------------------------------------------------------------------------
;
;Get a good ZMODEM header
;
rzahdr	 lda	maxtmo		;clear timeout count
	 sta	timeouts
rzahdr1	 jsr	zgethdr		;try to get a header
	 bcc	rzahdr7		;got one
;
	 jsr	showierr	;display error
	 cmp	#timeout	;header receive timeout?
	 bne	rzahdr4
rzahdr2	 dec	timeouts	;decrement timeout counter
	 beq	rzahdr3		;too many timeouts
;	 lda	stype
;	 cmp	#zack		;last header was a zack
;	 bne	rzahdr2a
;	 jsr	szrpos		;send a ZRPOS instead of a ZACK
;	 jmp	rzahdr1
rzahdr2a jsr	zshhdr		;re-send the last header in hex
	 jmp	rzahdr1
rzahdr3	 ldx	#<tmomess	;print aborting message
	 ldy	#>tmomess
	 jsr	printst
	 sec
	 lda	#timeout	;exit with timeout error
	 rts
;
rzahdr4  cmp	#crcerr		;bad header?
	 beq	rzahdr5
	 cmp	#baddata	;bad data byte?
	 bne	rzahdr6
rzahdr5	 lda	stype		;copy the old header that the sznak will erase
	 sta	stypebak
	 lda	szp0
	 sta	szp0bak
	 lda	szp1
	 sta	szp1bak
	 lda	szp2
	 sta	szp2bak
	 lda	szp3
	 sta	szp3bak
	 jsr	sznak		;request header again
	 lda	stypebak	;restore the old header
	 sta	stype
	 lda	szp0bak
	 sta	szp0
	 lda	szp1bak
	 sta	szp1
	 lda	szp2bak
	 sta	szp2
	 lda	szp3bak
	 sta	szp3
	 jmp	rzahdr
;
rzahdr6	 sec			;user break or other error
	 rts
;
rzahdr7  cmp	#znak		;got bad header indication
	 beq	rzahdr2
	 clc
	 rts
;
;------------------------------------------------------------------------------
;
;Get ZMODEM header
;
;This routine doesn't require a ZPAD before the ZDLE.
;
zgethdr	 txa
	 pha
	 ldx	#0
	 stx	hdrtyp		;0=hex header, $ff=binary header
	 stx	garbbyt		;no garbled bytes received yet
zgethdr1 jsr	readline	;wait for zdle
	 bcs	rzerr		;if bad, ignore & keep checking
	 cmp	#zdle
	 bne	nothdr		;check for real header
;
zgethdr2 jsr	readline	;get header type (A, B or C (not supported))
	 bcs	rzerr		;if bad, ignore & keep checking
	 cmp	#zdle		;are we getting a zcan?
	 beq	rzcan
chkhdrt  cmp	#zbin		;crc-16 binary header
	 beq	rzbin
	 cmp	#zhex		;hex header
	 beq	rhdr
;
nothdr	 ldx	#true
	 stx	garbbyt		;this was a garbled byte
	 bit	dispdat		;should we print the char?
	 bpl	zgethdr1	;no, keep going
	 cmp	#zpad
	 beq	zgethdr1	;don't show zpad
	 jsr	asc2pet		;convert to petscii
	 jsr	chrout		;and display
	 jmp	zgethdr1
;
;
;General error routine
;
rzerr    cmp	#timeout	;timeout error?
	 beq	rzexit		;exit on timeout
	 cmp	#usercan	;did the user cancel?
	 beq	rzexit
	 ldx	#true
	 stx	garbbyt		;this was a garbled byte
	 bne	zgethdr1	;no, keep waiting for header (always branch)
;
rzexit   sta	temp
	 pla
	 tax
	 lda	temp
	 rts
;
;Check for ZCAN (session abort)
;
rzcan    lda	#3
	 sta	cancount	;need 3 more can's for a zcan
rzcan1   jsr	readline
	 bcs	rzerr
	 cmp	#zdle
	 bne	chkhdrt		;check for the real header type
	 dec	cancount	;got another can
	 bne	rzcan1		;look for more
	 lda	#reczcan	;received zcan error
	 sec
	 bcs	rzexit		;got a zcan, exit
;
;Select binary header
;
rzbin    dec	hdrtyp
;
;Get header bytes & crc
;
rhdr     ldx	#0
	 stx	crc		;clear crc
	 stx	crc+1
rhdr1    jsr	getheadb
	 bcs	badcrc		;bad byte means bad header
	 sta	type,x
	 jsr	updcrc
	 inx
	 cpx	#5		;get 5 bytes
	 bne	rhdr1
;
;Get CRC
;
	 jsr	getheadb	;get high crc byte
	 bcs	rzexit
	 jsr	updcrc
	 jsr	getheadb	;get low crc byte
	 bcs	rzexit
	 jsr	updcrc
	 lda	crc		;are low and
	 ora	crc+1		;  high crc bytes 0?
	 bne	badcrc		;no, bad crc
;
	 bit	debug		;display received header?
	 bpl	zgethdrgd	;no
	 ldx	#<gothdr	;display got header message
	 ldy	#>gothdr
	 jsr	printst
	 ldx	#0
rzdiag   lda	type,x		;get byte
	 jsr	sendhx		;display in hex
	 lda	#space
	 jsr	chrout		;display space
	 inx
	 cpx	#5		;display all 5 bytes
	 bne	rzdiag
	 lda	#cr
	 jsr	chrout
;
zgethdrgd clc			;clear error flag
	 lda	type		;return with header type
	 jmp	rzexit
badcrc   sec
	 lda	#crcerr
	 jmp	rzexit
;
;------------------------------------------------------------------------------
;
;Get ZMODEM data packet
;
;Output: carry clear & special ZDLE character in A for good packet
;	 carry set & error number in A for bad packet
;
zrdata   tya
	 pha
	 ldy	#0
	 sty	crc		;clear crc
	 sty	crc+1
	 sty    numdata         ;clear packet byte count
	 sty    numdata+1
;
zrdata1  jsr	zdlread		;get zdle encoded byte
	 bcs	rzdatexi	;exit if error or special zdle char
;** Should check for buffer overflow here & abort on error
	 sta	(buffer),y	;put byte in 
	 jsr	updcrc		;update crc
	 iny			;increment low byte
	 bne	zrdata1
	 inc	numdata+1	;inc buffer count
	 inc	buffer+1	;inc buffer location
	 bne	zrdata1		;always jump
;
rzdatexi sty	numdata		;save packet length
	 pha
	 tya
	 clc
	 adc	buffer		;save end of buffer position
	 sta	buffer
	 lda	#0
	 adc	buffer+1
	 sta	buffer+1
	 pla
	 cmp	#speczdle	;is it special zdle char?
	 bne	rzderr		;no, it's an error
	 lda	specchar	;get zdle'd char
	 cmp	#zdle		;is it first part of ZCAN?
	 beq	rzdcan		;probably
	 jsr	updcrc		;update crc with frameend
	 jsr	zdlread		;get crc-1
	 bcs	rzderr
	 jsr	updcrc		;update crc
	 jsr	zdlread		;get crc-2
	 bcs	rzderr
	 jsr	updcrc		;update crc
	 lda	crc		;are low and
	 ora	crc+1		;  high crc bytes 0?
	 bne	rzdcrc		;no, bad crc
	 lda	specchar
	 clc
	 bcc	rzdret
;
rzdcan	 lda	#3
	 sta	cancount	;need 3 more can's for a zcan
rzdcan1	 jsr	readline
	 bcs	rzdcrc		;return with a crc error on a bad byte
	 cmp	#zdle
	 bne	rzdcrc		;if it isn't zdle, it's a crc error
	 dec	cancount	;got another can
	 bne	rzdcan1		;look for more
	 lda	#reczcan	;return with ZCAN error 
	 bne	rzderr		;always jump
;
rzdcrc   lda	#crcerr
rzderr   sec
rzdret   sta	temp		;save error or specchar
	 pla
	 tay			;restore y
	 lda	temp
	 rts
;
;------------------------------------------------------------------------------
;
;Decode ZFILE data packet
;
decodfp	 lda	zfilf0
	 cmp	#zcnl		;text file?
	 bne	decodf01
	 lda	#true
	 bne	decodfp0
decodf01 ;cmp	#zcbin		;binary file?
	 lda	#0
decodfp0 ora	zcnlcvt		;zcnlcvt overrides binary transfer
	 sta	asciifil
	 lda	bufloc		;point to data buffer
	 sta	buffer
	 lda	bufloc+1
	 sta	buffer+1
	 ldy	#0		;current position in buffer -- y=low
				;x = high -- declared later
	 cpy	numdata		;buffer empty?
	 bne	decodfp1	;no
	 jmp	decoderr	;yes
;
decodfp1 lda	(buffer),y	;get filename char
	 beq	decodsiz	;got null -- filename finished
	 and	#%01111111	;remove any parity bit
	 jsr	asc2pet		;convert to petscii
	 cmp	#':		;convert colon
	 beq	decodfp2
	 cmp	#'*		;convert asterisk
	 beq	decodfp2
	 cmp	#'?		;convert question
	 beq	decodfp2
	 cmp	#'$		;convert dollar
	 beq	decodfp2
	 cmp	#',		;convert comma
	 beq	decodfp2
	 cmp	#'#		;convert pound
	 bne	decodfp3
decodfp2 lda	#'.		;change to period
decodfp3 cmp	#'\		;convert British pound (backslash)
	 bne	decodfp4
	 lda	#'/		;change British pound to slash
decodfp4 sta	filename,y	;put char in filename buffer
	 iny
	 cpy	#16		;got 16 chars for filename?
	 bne	decodfp1	;no,keep going
;
decodsiz cpy	#0		;null file name?
	 bne	decods1		;no
	 jmp	decoderr	;yes
;
decods1  lda	#0
	 sta	filename,y	;put end of string char for print
	 tya
	 pha			;save current buffer position
	 ldx	#<rfmess	;receiving file message
	 ldy	#>rfmess
	 jsr	printst		;print it
	 ldx	bufloc		;actual file name received
	 ldy	bufloc+1
	 jsr	printst
	 lda	#cr
	 jsr	chrout
	 ldx	#<fnmess	;filename message
	 ldy	#>fnmess	;and filename
	 jsr	printst
	 pla
	 tay			;restore buffer position
;
;Put ,s,w or ,p,w in filename buffer ----
;
	 lda	#',
	 sta	filename,y
	 sta	filename+2,y
	 lda	#'W
	 sta	filename+3,y
	 lda	#'P
	 bit	asciifil	;if it's text, use seq
	 bpl	decods2		;prg
	 lda	#'S
decods2  sta	filename+1,y	;store file type
	 tya			;get buffer position
	 clc
	 adc	#4		;add ',S,W' to file length
	 sta	filenlen	;save it
	 tya
	 pha
	 ldx	#<lenmess	;display "length:"
	 ldy	#>lenmess
	 jsr	printst
	 pla
	 tay
;
;Position at beginning of file length
;
	 ldx	#0		;current position in buffer--high
decodl1  lda	(buffer),y
	 beq	decodl3		;we have end of filename char
	 iny
	 bne	decodl2
	 inx			;inc high character count
	 inc	buffer+1	;inc actual high address
decodl2  cpy	numdata		;at end of buffer?
	 bne	decodl1		;no,next addr
	 cpx	numdata+1
	 bne	decodl1
	 beq	decodnol	;end of buffer; no count specified
decodl3  iny
;
;Check if end of buffer
;
	 cpy	numdata
	 bne	decchkl		;no, check for the length
	 cpx	numdata+1
	 bne	decchkl		;no
;
decodnol txa
	 pha			;save x & y in case routine is
	 tya			;called from decchkl
	 pha
	 ldx	#<nolmess	;display no length specified
	 ldy	#>nolmess
	 jsr	printst
	 pla
	 tay
	 pla
	 tax
	 rts
;
decchkl  lda	(buffer),y	;is a length there?
	 bne	decchkl1	;go convert & print length
	 jsr	decodnol	;print "no length"
	 jmp	decchkdt
~ecchkl1:tya			;put buffer offset in acc
	 clc
	 adc	buffer		;add to buffer
	 tax			;put low addr in x
	 lda	#0
	 adc	buffer+1	;add in carry
	 tay			;put high addr in y
	 jsr	asc2bin		;convert string to num
	 bcs	decodnol	;exit on error
	 ldy	#3
decchkl2 lda	longword,y	;put length
	 sta	estflen,y	;where it belongs
	 dey			;all 4 bytes
	 bpl	decchkl2
	 ldx	#<estflen	;display file length
	 ldy	#>estflen
	 jsr	dispnum
	 lda	#cr
	 jsr	chrout		;move a line down
	 lda	#cr
	 jsr	chrout
;
decchkdt nop			;no date routine yet
;
	 clc
	 rts
;
decoderr lda	#nofilen	;error to do with filename
	 sec
	 rts
;
;------------------------------------------------------------------------------
;
;Get space left on disk
;
diskspac txa
	 pha
	 lda	#'$		;set up directory file name which will display
	 sta	filename	;  only disk name & blocks free lines
	 lda	drive
	 sta	filename+1
	 lda	#':
	 sta	filename+2
	 lda	#$ff		;hopefully no files called . (where . is $ff)
	 sta	filename+3
	 lda	#4		;file length
	 sta	filenlen
	 ldx	device		;get drive device number
	 bne	disksp0		;if 0, then
	 ldx	fa		;  use current device number
disksp0	 lda	#3		;file #
	 ldy	#0		;secondary address
	 jsr	openfile
	 bcs	dspacclo
	 ldx	#3		;input from dir
	 jsr	chkin
	 ldx	#6		;skip 6 bytes
disksp1  txa
	 pha
	 jsr	readbyt		;get byte
	 pla
	 tax			;don't care about acc
	 bcs	disksper	;go show error
	 dex
	 bne	disksp1
disksp2  jsr	readbyt		;skip dir header
	 bne	disksp2		;wait for a 0
	 jsr	readbyt		;skip line pointer
	 jsr	readbyt
	 jsr	readbyt		;get low blocks free
	 tax
	 jsr	readbyt		;get high blocks free
	 tay
	 jsr	mult254		;*254 to get bytes remaining
	 ldx	#<spacmess	;display disk space remaining
	 ldy	#>spacmess
	 jsr	printst
	 ldx	#<longword	;point to bytes remaining
	 ldy	#>longword
	 jsr	dispnum		;display number to test routine
diskspex clc			;no error
	 bcc	dspacclo	;always jump
disksper jsr	showderr
	 sec
dspacclo php			;save carry status
	 sta	temp
	 jsr	clrchn
	 lda	#3
	 jsr	close		;close directory file
	 pla
	 tax
	 lda	temp
	 plp			;restore carry status
	 rts
;
;------------------------------------------------------------------------------
;
;Write receive buffer to disk
;
;Converts from ASCII to PETSCII if decodfp routine said so
;
writedsk lda	buffer		;save the pointer to the last byte + 1
	 sta	lastwrit
	 lda	buffer+1
	 sta	lastwrit+1
	 lda	bufloc		;point to the first byte to write out
	 sta	buffer
	 lda	bufloc+1
	 sta	buffer+1
	 tya
	 pha
	 txa
	 pha
;
	 bit	quiet
	 bmi	writed0
	 ldx	#<wrtmess	;print writing message
	 ldy	#>wrtmess
	 jsr	printst
;debugging
	sec
	lda	lastwrit	;display number of bytes written
	sbc	buffer
	pha
	lda	lastwrit+1
	sbc	buffer+1
	jsr	sendhx
	pla
	jsr	sendhx
	lda	#cr
	jsr	chrout
	lda	#159		;cyan color
	jsr	chrout		;for debugging
;
writed0	 ldx	#2		;send output to already opened disk file
	 jsr	chkout
	 ldy	#0
writed1	 lda	buffer
	 cmp	lastwrit	;have we written everything?
	 bne	writed10
	 lda	buffer+1
	 cmp	lastwrit+1
	 beq	writedgd
writed10 lda	(buffer),y	;get byte to write
	 bit	asciifil	;convert to PETSCII?
	 bpl	writed20	;no
	 cmp	#cr
	 beq	writed21	;ignore carriage returns
	 cmp	#lf
	 bne	writed11
	 lda	#cr		;convert line feeds to PETSCII carriage returns
	 bne	writed20
writed11 cmp	#ht
	 bne	writed12
	 lda	#space		;convert tabs to spaces
	 bne	writed20
writed12 jsr	asc2pet		;convert other characters to PETSCII
;
writed20 jsr	chrout		;send to disk
	 jsr	readst		;check for error
	 bne	writedbd	;exit on error
writed21 inc	buffer		;point to next byte in buffer
	 bne	writed1
	 inc	buffer+1
	 bne	writed1		;always branch
;
writedbd jsr	clrchn		;send output back to screen
	 lda	#diskerr
	 sec
	 bcs	writeden
writedgd jsr	clrchn		;send output back to screen
;debugging
	lda	#5     ;write color
	jsr	chrout ;for debugging
;
	 clc
writeden pla
	 tax
	 pla
	 tay
	 rts
;
;------------------------------------------------------------------------------
;
;Send ZMODEM headers
;
;------------------------------------------------------------------------------
;
;ZRINIT
;
szrinit  lda	bufsiz
	 sta	szp0
	 lda	bufsiz+1
	 sta	szp1
	 lda	#0
	 sta	szf1
	 lda	#zrinit		;1
;	 lda	#canfdx		;1  (redundant LDA)
	 sta	szf0		;full duplex
	 sta	stype
	 jmp	zshhdr
;
;------------------------------------------------------------------------------
;
;ZACK
;
szack	 lda	#zack		;3
	 sta	stype
szack1	 txa			;entry point for szrpos
	 pha
	 ldx	#3
szack2   lda	filepos,x	;move all 32 bits of
	 sta	szp0,x		;filepos to zp0-zp3
	 dex
	 bpl	szack2
	 pla
	 tax
	 jmp	zsbhdr
;
;------------------------------------------------------------------------------
;
;ZSKIP
;
szskip   lda	#zskip		;5
	 jmp	zsbbhdr
;
;------------------------------------------------------------------------------
;
;ZNAK
;
sznak    lda	#znak		;6
	 jmp	zsbbhdr
;
;------------------------------------------------------------------------------
;
;ZABORT
;
szabort  lda	#zabort		;7
	 jmp	zsbbhdr
;
;------------------------------------------------------------------------------
;
;ZFIN
;
szfin    lda	#zfin		;8
	 jmp	zsbbhdr
;
;------------------------------------------------------------------------------
;
;ZRPOS
;
szrpos	 lda	#zrpos		;9
	 sta	stype
	 jmp	szack1		;send file position data
;
;------------------------------------------------------------------------------
;
;ZFERR
;
szferr   lda	#zferr		;12
	 jmp	zsbbhdr
;
;------------------------------------------------------------------------------
;
;ZCOMPL
;
szcompl  lda	#zcompl		;15
	 jmp	zsbbhdr
;
;------------------------------------------------------------------------------
;
;ZCAN
;
szcan    txa
	 pha
	 ldx	#1
	 jsr	chkout		;send to modem
	 lda	#zdle		;send zdle (can)
	 ldx	#8		;8 of them
szcan1   jsr	sendline
	 dex
	 bne	szcan1
	 lda	#8		;send backspace
	 tax			;8 of them
szcan2   jsr	sendline
	 dex
	 bne	szcan2
	 jsr	clrchn		;revert output back to screen
	 pla
	 tax
	 rts
;
;------------------------------------------------------------------------------
;
;Over & out
;
szoo     txa
	 pha
	 ldx	#1
	 jsr	chkout		;send to modem
	 lda	#'o		;Over and Out
	 jsr	sendline
	 jsr	sendline
	 jsr	clrchn		;revert output back to screen
	 pla
	 tax
	 rts
;
;------------------------------------------------------------------------------
;
;Send blank binary header
;
zsbbhdr  sta	stype
	 txa
	 pha
	 lda	#0
	 ldx	#4
zsbb1    sta	stype,x		;clear header
	 dex
	 bne	zsbb1		;don't clear type
	 pla
	 tax
;	 jmp	zsbhdr		;send it  (follow through to zsbhdr routine)
;
;------------------------------------------------------------------------------
;
;Send a binary header
;
zsbhdr   txa
	 pha
;
	 bit	debug
	 bpl	zsbhdr0
	 ldx	#<szbmes	;display "sending binary header" message
	 ldy	#>szbmes
	 jsr	printst
	 lda	stype
	 jsr	sendhx
	 lda	#cr
	 jsr	chrout
;
zsbhdr0	 ldx	#1
	 jsr	chkout
	 ldx	#0
	 stx	crc		;clear crc
	 stx	crc+1
	 lda	#zpad
	 jsr	sendline	;send '*'
	 lda	#zdle
	 jsr	sendline	;send ^X
	 lda	#zbin
	 jsr	sendline	;send 'A'
zsbhdr1  lda	stype,x
	 jsr	updcrc
	 jsr	sendzbyt	;send header type and data
	 inx
	 cpx	#5		;send all 5 bytes
	 bne	zsbhdr1
	 lda	#0
	 jsr	updcrc		;obtain correct crc
	 jsr	updcrc
	 lda	crc+1
	 jsr	sendzbyt	;send crc
	 lda	crc
	 jsr	sendzbyt
	 jsr	clrchn		;revert output back to screen
	 pla
	 tax
	 rts			;exit
;
;------------------------------------------------------------------------------
;
;Send a hex header
;
zshhdr   txa
	 pha
;
	 bit	debug
	 bpl	zshhdr0
	 ldx	#<szhmes	;display "sending hex header" message
	 ldy	#>szhmes
	 jsr	printst
	 lda	stype
	 jsr	sendhx
	 lda	#cr
	 jsr	chrout
;
zshhdr0	 ldx	#1
	 jsr	chkout		;open channel to modem
	 ldx	#0
	 stx	crc		;clear crc
	 stx	crc+1
	 lda	#zpad
	 jsr	sendline	;send '**'
	 jsr	sendline
	 lda	#zdle
	 jsr	sendline	;send ^X
	 lda	#zhex
	 jsr	sendline	;send 'B'
zshhdr1  lda	stype,x		;get next byte
	 jsr	updcrc		;update crc
	 jsr	sendhx		;output byte in hex
	 inx
	 cpx	#5		;send 5 bytes
	 bne	zshhdr1
	 lda	#0
	 jsr	updcrc		;finish off crc calc
	 jsr	updcrc
	 lda	crc+1
	 jsr	sendhx		;send high crc byte
	 lda	crc
	 jsr	sendhx		;send low crc byte
	 lda	#cr
	 jsr	sendline	;send return
	 lda	#lf
	 jsr	sendline	;send line feed
	 lda	stype
	 cmp	zfin		;don't send XON for a ZFIN or ZACK header
	 beq	zshhdr2
	 cmp	zack
	 beq	zshhdr2
	 lda	#xon
	 jsr	sendline	;send ^q (XON)
zshhdr2	 jsr	clrchn		;revert output back to screen
	 pla
	 tax
	 rts
;
;------------------------------------------------------------------------------
;
;Wait for RS-232 transmit buffers to clear
;
waitbufc lda	enabl
	 and	#1
	 bne	waitbufc
	 rts
;
;------------------------------------------------------------------------------
;
;Check dispmess flag & print string
;
printmess bit	quiet		;display message?
	 bmi	printmex	;no
	 jmp	printst
printmex rts
;
;----------------mm------------------------------------------------------------
;
;Print string
;
printst	 stx	temp
	 sty	temp+1
	 ldy	#0
printst1 lda	(temp),y
	 beq	printst2	;end of string?
	 jsr	chrout		;print it
	 iny			;point to next char
	 bne	printst1
	 inc	temp+1
	 bne	printst1	;always jump
printst2 rts
;
;------------------------------------------------------------------------------
;
;Show kernal error
;
showkerr sta	temp		;save error code
	 tya
	 pha
	 txa
	 pha
	 lda	temp		;get error:b{ck
	 asl	a		;multiply by 2
	 tax			;use it as index to string addr table
	 lda	errtab,x	;get low byte
	 inx
	 ldy	errtab,x	;get high byte
	 tax
	 jsr	printst		;print error message
	 pla
	 tax
	 pla
	 tay
	 sec
	 lda	temp		;return with same error code
	 rts
;
;------------------------------------------------------------------------------
;
;Show disk error
;
showderr txa
	 pha
	 tya
	 pha
	 ldx	#15		;get input from disk
	 jsr	chkin
	 lda	bufloc		;get buffer address
	 sta	buffer
	 lda	bufloc+1
	 sta	buffer+1
	 ldy	#0		;point to beginning of buffer
showd1   jsr	readbyt		;get byte from disk
	 sta	(buffer),y	;save byte
	 bcs	showd2		;return on error
	 cmp	#cr		;last char in err mess?
	 beq	showd2		;yes, exit
	 iny			;point to next bufferloc
	 bpl	showd1		;buff overflow; branch if ok
showd2   iny
	 lda	#0
	 sta	(buffer),y	;end of string
	 tay			;point to 1st char in buffer
	 lda	(buffer),y	;get 1st char in buffer
	 cmp	#'0		;is it 0?
	 beq	showd3		;yes, don't display error
	 ldx	bufloc		;load string address
	 ldy	bufloc+1
	 jsr	printst		;print error
showd3   jsr	clrchn		;revert input back to keybd
	 pla
	 tay
	 pla
	 tax
	 rts
;
;-------------------------------------------------------------------------m---m
;
;Show internal negative error
;
showierr bit	quiet		;print message or not?
	 bmi	showiret	;no
	 sta	temp2
	 tya
	 pha
	 txa
	 pha
	 lda	temp2
	 eor	#$ff		;produce a positive number
	 asl	a		;multiply by 2
	 tax			;use it as index to string addr table
	 lda	ierrtab,x	;get low byte
	 inx
	 ldy	ierrtab,x	;get high byte
	 tax
	 jsr	printst		;print error message
	 pla
	 tax
	 pla
	 tay
	 lda	temp2
showiret rts
;
;
;******************************************************************************
;Lower level zmodem routines
;******************************************************************************
;
;Get header byte (hex or ZDLE)
;
getheadb bit	hdrtyp		;what kind of header?
	 bpl	gethb1		;hex header
	 jmp	zdlread		;get zdle encoded byte
gethb1	 jmp	gethbyt		;get hex byte
;
;------------------------------------------------------------------------------
;
;Display 32 bit binary number
;
;Entry- x & y point to address of number
;
dispnum  stx	temp
	 sty	temp+1
	 ldy	#3		;this is to flip bytes
	 ldx	#0		;abcd => dcba
disp1    lda	(temp),y	;put 8 bits at a time into
	 sta	fps+1,x		;floating point mem (bypass mantissa)
	 inx
	 dey
	 bpl	disp1
	 ldy	#$20		;max num of rotations
disp2    bit	fps+1		;finished converting?
	 bmi	disppnt		;yes, print it
	 asl	fps+4		;low byte
	 rol	fps+3
	 rol	fps+2
	 rol	fps+1		;high byte
	 dey			;keep track of rotations
	 bne	disp2		;abort if we've done too many rot.
disppnt  tya
	 clc
	 adc	#$80		;create mantissa
	 sta	fps		;and save it
	 lda	#<fps		;point to fps
	 ldy	#>fps
	 jsr	memfac		;convert memory to fp accum
	 jsr	facasc		;cvt fac to ascii digits
	 tax
	 jmp	printst		;print it & exit
;
;------------------------------------------------------------------------------
;
;Open disk error channel
;
openerr  lda	#'i		;initialize command
	 sta	filename
	 lda	drive		;disk drive number
	 sta	filename+1
	 lda	#2		;its length
	 sta	filenlen
	 ldx	device		;get drive device number
	 bne	opener1		;if 0, then
	 ldx	fa		;  use current device number
opener1	 lda	#15		;logical file number
	 ldy	#15		;secondary address
;        jmp	openfile	;open it & display errors  (follow through)
;
;------------------------------------------------------------------------------
;
;Open a file
;
openfile jsr	setlfs
	 lda	filenlen
	 ldx	#<filename
	 ldy	#>filename
	 jsr	setnam
	 jsr	open
	 bcs	openferr
	 jmp	showderr
openferr jmp	showkerr
;
;
;******************************************************************************
;Lowest level routines
;******************************************************************************
;
;Send ZDLE encoded byte
;
sendzbyt pha
	 cmp	#zdle		;trying to send zdle?
	 bne	sendz1
	 jsr	sendline	;send zdle
	 lda	#zdlee
	 jsr	sendline	;send zdlee
	 bne	sendzexit
sendz1   cmp	#rub0		;sending $7f?
	 bne	sendz2
	 lda	#zdle
	 jsr	sendline
	 lda	#zrub0		;yes, send zdle zrub0
	 bne	sendznon
sendz2   cmp	#rub1		;sending $ff?
	 bne	sendz3
	 lda	#zdle
	 jsr	sendline
	 lda	#zrub1		;yes, send zdle zrub1
	 bne	sendznon
sendz3   bit	escctl		;zdle all control chars?
	 beq	sendznon	;no, send unencoded
	 pha
	 and	#%01111111	;get rid of high bit
	 cmp	#space		;is char < 32 ?
	 pla
	 bcs	sendznon	;char is not control
	 ora	#'a		;zdle encode char
	 pha			;save converted char
	 lda	#zdle
	 jsr	sendline	;send zdle
	 pla			;restore converted char
sendznon jsr	sendline	;send char
sendzexit pla
	 rts
;
;------------------------------------------------------------------------------
;
;Send a hex byte
;
sendhx   sta	temp2
	 lsr
	 lsr
	 lsr
	 lsr
	 and	#$0f		;mask lower bits
	 cmp	#10		;num > 9
	 bcc	sendhx1
	 adc	#$26		;conv to letter
sendhx1  adc	#'0		;add $30
	 jsr	sendline
	 lda	temp2
	 and	#$0f
	 cmp	#10
	 bcc	sendhx2
	 adc	#$26
sendhx2  adc	#'0
	 jsr	sendline
	 lda	temp2
	 rts
;
;------------------------------------------------------------------------------
;
;Send byte to the modem
;
sendline pha
	 jsr	chrout
	 pla
	 rts
;
;------------------------------------------------------------------------------
;
;Get ZDLE encoded byte
;
;Error codes returned with carry set
;
; same as readline
; speczdle - special ZDLE char returned in specchar
;
zdlread  jsr	readline	;get a byte
	 bcs	zdlret		;error receiving byte
	 cmp	#zdle		;is it ZDLE?
	 beq	gotzdle
	 clc			;no
zdlret	 rts
;
gotzdle  jsr	readline	;get encoded byte
	 bcs	zdlret		;error receiving byte
	 cmp	#zrub0		;check for zrub0
	 bne	gotz1
	 lda	#$7f		;convert if true
	 clc
	 rts
gotz1    cmp	#zrub1
	 bne	gotz2
	 lda	#$ff
	 clc
	 rts
gotz2    cmp	#zdlee
	 bne	gotz3
	 lda	#zdle
	 clc
	 rts
gotz3    pha
	 and	#%01100000	;is it a capital letter
	 cmp	#%01000000	;  with any kind of parity?
	 bne	gotspec		;no, it's a special char
	 pla			;yes, convert to control
	 and	#%10011111	;  char with same parity
	 clc
	 rts
gotspec  pla
	 sta	specchar	;no,save special zdle char
	 lda	#speczdle	;return with special zdle error code
	 sec
	 rts			;and return
;
;------------------------------------------------------------------------------
;
;Get hex encoded byte -----------
;
;Error codes returned with carry set
;
; same as readline
; baddata  - bad hex byte
;
gethbyt  jsr	readline	;get 1st digit
	 bcs	getret
	 jsr	cvthex		;convert to binary
	 bcs	getret
	 asl	a
	 asl	a
	 asl	a
	 asl	a
	 sta	hexbyt
	 jsr	readline	;get 2nd digit
	 bcs	getret
	 jsr	cvthex		;convert to binary
	 bcs	getret
	 ora	hexbyt
getret   rts
;
;------------------------------------------------------------------------------
;
;Get RS232 byte
;
;Error codes returned with carry set
;
; baddata - bad data
; timeout - timeout (appr 9 sec)
; usercan - user pressed run/stop
;
readline txa
	 pha
	 tya
	 pha
	 lda	#0
	 sta	wait
	 sta	wait+1
	 jsr	stop		;stop key pressed?
	 beq	getbcan		;yes
	 ldx	#1
	 jsr	chkin
readlin1 jsr	getin		;get rs232 byte
	 sta	temp		;save it
	 jsr	readst		;check for error
	 cmp	#0		;good byte
	 beq	goodbyt
	 and	#%00001000	;no byte available
	 bne	nobyt
	 jsr	clrchn
	 lda	#baddata	;bad data received
	 sec
	 bcs	readlinn	;exit
;
getbcan  jsr	clrchn
	 lda	#usercan	;user cancelled
	 sec
	 bcs	readlinn	;exit
;
goodbyt  jsr	clrchn
	 lda	temp		;restore data
	 clc
	 bcc	readlinn	;exit
;
nobyt    inc	wait		;increment wait
	 bne	readlin1	;is low byte=0?
	 inc	wait+1		;yes, inc high byte
	 lda	wait+1		;have we passed max delay time?
	 cmp	maxdly
	 bne	readlin1	;no, keep going
	 jsr	clrchn
	 lda	#timeout	;yes, exit with timeout error
	 sec
readlinn sta	temp		;save error code, if any
	 pla
	 tay
	 pla
	 tax
	 lda	temp
	 rts			;exit
;
;------------------------------------------------------------------------------
;
;Update CRC with acc
;
updcrc   sta	temp
	 pha
	 txa
	 pha
	 ldx	#$07
crc1     bit	crc+1
	 bpl	crc2
	 jsr	crccal
	 lda	crc+1
	 eor	#$10
	 sta	crc+1
	 lda	crc
	 eor	#$21
	 sta	crc
	 clc
	 bcc	crc3
crc2     jsr	crccal
crc3     dex
	 bpl	crc1
	 pla
	 tax
	 pla
	 rts			;exit routine
;
crccal   asl	crc
	 rol	crc+1
	 asl	temp
	 lda	crc
	 adc	#0
	 sta	crc
	 lda	crc+1
	 adc	#0
	 sta	crc+1
	 rts
;
;------------------------------------------------------------------------------
;
;Convert ASCII # to binary
;
asc2bin  stx	temp		;low byte address
	 sty	temp+1		;high byte address
	 ldy	#0
	 sty	longword	;holds final result
	 sty	longword+1
	 sty	longword+2
	 sty	longword+3
asc2bin1 lda	(temp),y	;get ascii digit
	 beq	asc2binf	;finished when char is null
	 cmp	#space		;or space
	 beq	asc2binf
	 and	#%11001111	;convert to binary
	 cmp	#10		;rudimentary ascii digit check
	 bcs	asc2berr	;not ascii number
	 pha			;save it for now
	 lda	longword	;multiply number so far
	 asl	a		;by 2
	 sta	fps+1		;and store temporarily in fps
	 lda	longword+1
	 rol	a
	 sta	fps+2
	 lda	longword+2
	 rol	a
	 sta	fps+3
	 lda	longword+3
	 rol	a
	 sta	fps+4
	 ldx	#3		;multiply rest by 2 three times
asc2bin2 asl	longword	; making it a
	 rol	longword+1	; multiply by 8
	 rol	longword+2
	 rol	longword+3
	 dex
	 bne	asc2bin2	;do it again
	 clc
	 lda	fps+1		;and add in the number
	 adc	longword	;multiplied by 2 previously
	 sta	longword	; resulting in the equation
	 lda	fps+2
	 adc	longword+1
	 sta	longword+1
	 lda	fps+3		; n = 2*n + 8*n = 10*n
	 adc	longword+2
	 sta	longword+2
	 lda	fps+4
	 adc	longword+3
	 sta	longword+3
	 pla			;get current binary digit
	 clc
	 adc	longword	;add it in
	 sta	longword
	 txa			;x is still 0 -- put in acc
	 adc	longword+1
	 sta	longword+1
	 txa			;zero acc
	 adc	longword+2
	 sta	longword+2
	 txa			;zero acc
	 adc	longword+3
	 sta	longword+3
	 iny			;point to next ascii char
	 jmp	asc2bin1	;always branch
asc2binf clc			;return with carry off
	 rts
asc2berr lda	#baddata	;bad data error
	 sec
	 rts
;
;------------------------------------------------------------------------------
;
;Convert ASCII to PETSCII
;
asc2pet  stx	temp
	 tax			;put ascii char into index
	 lda	rasctab,x	;get petscii char for ascii char
	 ldx	temp
	 rts
;
;------------------------------------------------------------------------------
;
;Convert an ASCII hexidecimal digit to binary
;
cvthex	 cmp	#'0		;char < 0 print
	 bcc	badhbyt
	 cmp	#'9 + 1		;char > 9 ?
	 bcs	chkhex
	 and	#%00001111	;convert ascii to number
	 clc
	 rts
chkhex   cmp	#'a		;char < a ?
	 bcc	badhbyt
	 cmp	#'f + 1		;char > f ?
	 bcs	badhbyt
	 sbc	#'a - 11	;convert ascii to number
	 clc
	 rts
badhbyt  sec
	 lda	#baddata
	 rts
;
;------------------------------------------------------------------------------
;
;Read byte from disk
;
readbyt  txa
	 pha
	 jsr	chrin		;get byte from input channel
	 sta	temp
	 bcs	readbret	;return if error
	 jsr	readst		;get status var
	 and	#%10111111	;ignore eoi line
	 clc
	 beq	readbret	;return if okay
	 sta	temp
	 sec			;else set carry & return
readbret pla
	 tax
	 lda	temp
	 rts
;
;------------------------------------------------------------------------------
;
;Multiply 16 bit number by 254
;
mult254  txa
	 asl	a		;multiply low byte by 2
	 sta	fps+1		;just using fps for temp storage
	 tya
	 rol	a		;mult high byte by 2
	 sta	fps+2
	 lda	#0		;clear addition area
	 sta	longword
	 sta	longword+1
	 sta	longword+2
	 sta	longword+3
	 ldx	#127		;successive addition 127 times
mult1    clc
	 lda	fps+1
	 adc	longword
	 sta	longword
	 lda	fps+2
	 adc	longword+1
	 sta	longword+1
	 lda	#0
	 adc	longword+2
	 sta	longword+2
	 dex
	 bne	mult1
	 rts
;
;
;******************************************************************************
;Variables & data storage
;******************************************************************************
;
filepos  .word	0		;32 bit current file position
	 .word	0
estflen  .word	0		;estimated file length
	 .word	0
numdata  .word	0		;number of data bytes in last packet
lastwrit .word	0		;address of last byte in buffer to write
				;  (bufloc is first byte)
wait     .word	0		;wait while receiving byte
hexbyt   .byte	0		;storage of hex byte
hdrtyp   .byte	0		;type of header we're receiving (hex or binary)
specchar .byte	0		;special zdle encoded char (0=none)
cancount .byte	0		;number of can's received so far
fps      .byte	0		;floating point storage mantissa
	 .word	0		;  higher 16 bits (ab..)
	 .word	0		;  lower  16 bits (..cd)
longword .word	0		;storage for 32 bit word
	 .word	0
temp2	 .word	0		;miscellaneous temporary storage
timeouts .byte	0		;how many timeouts have occurred so far
garbbyt	 .byte	0		;whether a garbage byte was received before
				;  a header
asciifil .byte	0		;-1 if the current file must be
				;  converted from ASCII to PETSCII
;
zfilf3   .byte	0		;zfile zf3 byte
zfilf2   .byte	0		;      zf2
zfilf1   .byte	0		;      zf1
zfilf0   .byte	0		;      zf0
;
type     .byte	0		;received header data area
zf3      =	*
zp0      .byte	0
zf2      =	*
zp1      .byte	0
zf1      =	*
zp2      .byte	0
zf0      =	*
zp3      .byte	0
;
stype	 .byte	0		;transmitted header data area
szf3     =	*
szp0     .byte	0
szf2     =	*
szp1     .byte	0
szf1     =	*
szp2     .byte	0
szf0     =	*
szp3     .byte	0
;
stypebak .byte	0		;temporary backup for header
szp0bak	 .byte	0
szp1bak`nbyte0
szp2bak	 .byte	0
szp3bak	 .byte	0
;
endmess  _ascii	"ZMODEM receive finished"
	 .byte	cr,0
tmomess  _ascii	"Aborting due to timeouts"
	 .byte	cr,0
zfermes  _ascii	"Error in ZFILE packet"
	 .byte	cr,0
rcmdmess _ascii	"Remote commands "
nosupmes _ascii "not supported"
	 .byte	cr
abortmes _ascii	"Aborting"
	 .byte	cr,0
gothdr   _ascii	"Received header: "
	 .byte 0
szbmes	 _ascii	"Sending binary header: "
	 .byte	0
szhmes	 _ascii	"Sending hex header: "
	 .byte	0
wrtmess	 .byte	28
	 _ascii	"Writing bytes: "
	 .byte	0
nolmess  _ascii	"no length specified"
	 .byte	0
;
rfmess   _ascii	"       Receiving: "
	 .byte	0
fnmess	 _ascii	"         To file: "
filename =    *			;storage for disk file name
*	 =	* + 23		;room for '@0:' name & ',p,w'
	 .byte	0		;end of filename (for printing)
filenlen .byte	0		;length of file name (for opening)
lenmess  .byte	cr
	 _ascii	"     File length: "
	 .byte	0
spacmess _ascii	" Disk space left: "
	 .byte	0
lastmess _ascii	"    Last message: "
	 .byte	0
;
;Received ASCII lookup table -- given ASCII char, returns PETSCII char
;
rasctab  =	*
	 .byte	0,19,2,3,4,5,6,7,20,9,10,11,147,13,14,15,16
	 .byte	17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
	 _ascii	" !"
	 .byte	34
	 _ascii	"#$%&'()*+,-./0123456789:;<=>?"
	 _ascii	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
	 _ascii	"`abcdefghijklmnopqrstuvwxyz{|}~"
	 .byte	128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143
	 .byte	144,145,146,147,148,149,150,151,152,153,154,155,156,57,58,159
	 .byte	160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175
	 .byte	176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191
	 .byte	192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207
	 .byte	208,209,10,211,212,13,214,215,216,217,218,219,220,221,222,223
	 .byte	224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239
	 .byte	240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
;
errtab   =	*		;kernal error addr table
	 .word	kerr0,kerr1,kerr2,kerr3,kerr4,kerr6,kerr6
	 .word	kerr7,kerr8,kerr9
;
kerr0    _ascii	"User cancelled"
	 .byte	cr,0
kerr1    _ascii	"er1"
	 .byte	cr,0
kerr2    _ascii	"er2"
	 .byte	cr,0
kerr3    _ascii	"er3"
	 .byte	cr,0
kerr4    _ascii	"File not found"
	 .byte	cr,0
kerr5    _ascii	"Device not present"
	 .byte	cr,0
kerr6    _ascii	"er6"
	 .byte	cr,0
kerr7    _ascii	"er7"
	 .byte	cr,0
kerr8    _ascii	"No file name"
	 .byte	cr,0
kerr9    _ascii	"Illegal device"
	 .byte	cr,0
;
ierrtab  =	*		;internal error addr table
	 .word	ierr1,ierr2,ierr3,ierr4,ierr5,ierr6,ierr7,ierr8,ierr9
;
ierr1    _ascii	"Fatal error"
	 .byte	cr,0
ierr2    _ascii	"Timeout"
	 .byte	cr,0
ierr3    _ascii	"Bad data"
	 .byte	cr,0
ierr4    _ascii	"Bad CRC"
	 .byte	cr,0
ierr5    _ascii	"Bad ZDLE encoded byte"
	 .byte	cr,0
ierr6    _ascii	"User cancelled"
	 .byte	cr,0
ierr7    _ascii	"No file name"
	 .byte	cr,0
ierr8    _ascii	"Received ZCAN"
	 .byte	cr,0
ierr9    _ascii	"Overflow"
	 .byte	cr,0
;
buffl	 =	$6000		;location of ZMODEM data receive buffer
;
	 .end
