;==============================================================
;Advanced PTDOS File Copy Program
;==============================================================
;REVISION HISTORY
; 1.03 23 April 2018  M. Eberhard
;   Make options work more like other PT programs
; 1.02 13 April 2018  M. Eberhard
;   Improve error checking and responses, fix a few bugs
; 1.01 13 April 2018  M. Eberhard
;   Eliminate V=0 option. Make ACOPY interuptable. Fix bug
;   that prevented copying a device driver. Fix bug checking
;   file number when it was larger than FFh.
; 1.00  9 March 2018  M.Eberhard
;   Created
;
;TO DO
;  Respond better when aborting using a device driver
;
;---------------
;Program version
;---------------
VERSION	equ	103h	;BCD-encoded version number

;==============================================================
;ACOPY {source}{/u},{dest}{/u}{,options separated by commas}
;
;Copies source to dest, keeping file-protect attributes and
;File type unchanged.
;
;if dest is not specified (and its unit number is not the same
;as source's unit number), then the the destination file will
;have the same name as the source file.
;
;See page 5-1 for valid file name characters. Wildcard
;characters < and >in source work like the files command.
;  string> copies anything that starts with string
;  <string copies anything that ends with string
;  <string> copies anything that contains string
;
;Allowed combinations:
;  Single-file copy:
;    ACOPY source{/u1},dest{/u2}{options}
;      copies source, a single file (optionally on unit u1), to
;      dest (optionally on unit u2). source must not be the
;      same as dest, unless they are on different units. If the
;      unit is not specified (for either u1 or u2) then PTDOS's
;      default unit is used.
;    ACOPY source/u1
;      copies file source from unit u1 to the defaut unit,
;      which must not be u1.
;    ACOPY srcdriver{/u1},dest{/u2}{options}
;      copies a file received from driver srcdriver to dest
;      dest (optionally on unit u2). If the unit is not
;      specified (for either u1 or u2) then PTDOS's default
;      unit is used.
;    ACOPY source{/u1},destdriver{/u2}{options}
;      copies source (optionally on unit 1) to device driver
;      destdriver. If the unit is not specified (for either u1
;      or u2) then PTDOS's default unit is used.
;
;  Multiple-file copies:
;   ACOPY [source with wildcards]{/u1}{,/u2}{options}
;      copies all files on unit u1 to the same-named files on
;      unit u2. If u2 is not specified then PTDOS's default
;      unit is used. u1 must not be the same as u2.
;   ACOPY <source{/u1}{,/u2}{options}
;      copies all files on u1 that end with "source" to the
;      same- named files on unit u2. If the unit is not
;      specified (for either u1 or u2) then PTDOS's default
;      unit is used. u1 must not be the same as u2.
;   ACOPY source>{/u1}{,/u2}{options}
;      copies all files on u1 that begin with "source" to the
;      same-named files on unit u2. If the unit is not
;      specified (for either source or dst) then PTDOS's
;      default unit is used. u1 must not be the same as u2.
;   ACOPY <source>{/u1}{,/u2}{options}
;      copies all files on u1 that contain with "source" to the
;      same-named files on unit u2. If the unit is not
;      specified (for either u1 or u2) then PTDOS's default
;      unit is used. u1 must not be the same as u2.
;   Note that device drivers are not allowed as the source or
;      destination for multiple-file copies.
;
;Options:
;  B=0 tells ACOPY not to optimize the block size. (The default
;      is B=1, which will optimize the block size using the
;      default max block size, unless the source or destination
;      is a device driver.)
;
;  B=hhh (hhh is a hex value between 100 and FFF) sets the max
;        block size for optimizing the block size. (The default
;        is DMIBLK. See page 5-4.)
;
;  T=nn If the source is not being used as a device driver,
;       then this means only copy files with file type nn.
;       (Only valid when a wildcard is present) (See RETYPE
;       command for valid types. See page 5-2). If the source
;       is being used as a device driver (and the destination
;       is not also a device driver), then this will set the
;       destination file type to nn.
;
;       Valid file types for nn:
;
;       1) 'D' becomes 0FFh (a device driver)
;       2) Any single digit except 'I' becomes that ASCII
;          character with its msb set
;       3) 'I' becomes ASCII I (2Ah) with msb cleared (This is
;          the same as 'I2A', the default Image File type.)
;       4) 'In' followed by becomes ASCII of n with msb cleared
;       5) 'I#h' where h is a 1- or 2-digit hex value < 80h
;          becomes hh (msb cleared)
;       6) '#h' where h is a 1- or 2-digit hex value < 80h
;          becomes hh with its msb set
;
;  S={D}{-I}{-P}{S}
;
;    S Options
;
;      D tells ACOPY to copy to a device driver, rather than 
;        use it as a driver for the destination. (Otherwise
;        ACOPY will treat a destination device driver as a 
;        device driver, sending the file to that device.)
;
;     -I tells ACOPY to copy files even if they are
;        information-protected. (This option is only valid
;        when a wildcard is present)
;
;     -P tells ACOPY to ignore any protections that would
;        otherwise prevent copying. (Otherwise, ACOPY will not
;        copy from a read-protected file, or replace a write-
;        protected file. P will also will allow copying a
;        device driver that has its attributes protected.)
;
;      S tells ACOPY to copy a device driver, rather than use
;        it as a driver for the source. (Otherwise ACOPY will
;        treat a source device driver as a device driver,
;        and will read teh file from that device.)
;
;==============================================================
;NOTES
;
;Verifying Writes
;
; PTDOS has a flag called RBC in its System Global Area, which
; can be set or cleared with the SET SW={+/-}V commmand.
; according to PTDOS documentation, this flag will enable or
; disable read-back checking of disk writes, which would be
; really nice here. However, the PTDOS source code does not
; appear to contain any code for actually performing a write
; verification, and does not contain any references to the
; SWRBC bit in SYSGLOBL except this bit's definition. For this
; reason, ACOPY performs write verification the hard way.
;
;ZAPping PTDOS: Protection Attributes
;
; Among PTDOS's file protection attributes is PATR, attribute
; change protection. This particular protection is usually set
; on many of  PTDOS's system files, along with PREA - read
; protection. a file with both of these attributes set cannot
; normally be copied by PTDOS, except via DISKCOPY.
;
; Eventually, Processor Technology provided a workaround that
; would allow such files to be copied. The workaround was a
; little program called ZAP, which would overwrite a particular
; byte within PTDOS (the PATR bit mask in the protection test
; routine), and override the PATR protection. (They also
; provided UNZAP, which put this bit mask back.)
;
; The exact location of this attribute test within PTDOS
; changed from PTDOS 1.4 to PTDOS 1.5, and so ZAP had to check
; which version was running, by inspecting the two possible
; locations to see which one was in fact PATR.
;
; ACOPY ZAPs the operating system the same way, before it
; copies the system files, and UNZAPs it when done. As with
; PT's ZAP program, this program will abort with an error if
; PATR is not found at either of the two possible addresses.
;
;Block Size Tuning
;
; PTDOS's 'firm sectoring' blocks are multiples of 256 byte
; 'sectors' plus 64 bytes of additional data in the inter-
; sector space within a block, so the larger the blocks the
; more available storage space. The largest allowed block uses
; 12 'sectors', and (because of the inter-sector space) has
; 4095 data bytes (the largest block is actually 1 byte short.)
;
; By default, ACOPY will tune the block size of every file it
; writes to use the fewest number of blocks, given an upper
; limit on block size that is either ACOPY's default or a limit
; set on the command line. Block size tuning is done both to
; save disk space and to speed up file access. Block size
; tuning may also be disabled by a command line option.
;
;Multiple-file copies
;
; One of the primary motivators for creating ACOPY was to allow
; wildcards in the source file name, so that multiple files can
; be copied qith a single command. Wildcards in ACOPY work the
; same as those in the DILES command. Furrther file selection
; filtering are possible with the I=1 option (which will
; include files that are information-protected) and the T=nn
; option (which will only copy files of the specified file
; type).
;
; File types for the T=nn option work the same as for the
; REATTR command. In addition, you can specify a non-image file
; type with T=Ihh, where hh is a 2-digit hex value that is
; less than 80h, and you can specify a non-image file type with
; T=hh, where hh is a 2-figit hex value less than 80h. These
; additional ways to specify a file type allow filtering for
; various system file types, which cannot be set with the
; REATTR command.
;
;Copying Device Drivers
;
; Another big motivator for creating ACOPY was to allow device
; drivers to be copied or replaced without a lot of gymnastics
; with the REATTR command. The S=0 option will cause a device
; driver that's specified as the source to be copied rather
; than invoked. The D=0 option will cause a device driver
; that's specified as the destination to be overwritten rather
; than invoked.
;==============================================================

FALSE	equ	0
TRUE	equ	not FALSE

;---------------
;Program equates
;---------------
STKSIZ	equ	32	;stack size (bytes)
FGLLOW	equ	6800h	;lowest PTDOS address while we work
			;leaving room for big buffers
DMIBLK	equ	9C0h	;default max "ideal" allowed block size
DBSIZE	equ	4C0h	;default block size if source is used
			;..as a driver. (See page 5-4.)
DDATTR	equ	0	;default protection attributes
DITYPE	equ	2Eh	;default image file type
			;(Page 5-2 is wrong)
DFLTYP	equ	'.'+80h	;default file type
TEMPFT	equ	'$'+80h	;temp file type used during operations
VBSIZE	equ	255	;verify buffer size (<256)

;-------------
;PTDOS Equates
;-------------
PMPARM	equ	20	;Max PTDOS parameter chr count
PDBSIZ	equ	256	;PTDOS directory block size
PDBCNT	equ	16	;PTDOS directory block count
PDESIZ	equ	21	;PTDOS directory entry size
PNMAX	equ	8	;max characters in a file name
PDTYPE	equ	0FFh	;PTDOS device driver file type
PCLOSA	equ	255	;Close all files for CAOP

SECSIZ	equ	100h	;size of one raw sector
ISGSIZ	equ	40h	;size of inter-sector gap

UCFROM	equ	82h	;UTIL ctrl word (see page 8-1)
			;"CALLED FROM", Returns when done
MAXPID	equ	14h	;max ID for core PTDOS file

;Directory entry offsets

PDEFT	equ	8	;to file type
PDEFPA	equ	11	;to protection attributes
PDEFID	equ	12	;to file ID

;-------------
;ZAP addresses
;-------------
PATR15	equ	0A890h	;Address of PATR mask in PTDOS 1.5
PATR14	equ	0A8B1h	;Address of PATR mask in PTDOS 1.4

;-----------------------------
;PTDOS definitions from PTDEFS
;-----------------------------
; 1) Main Entry PointS  (EP AREA)

SYS	equ	0BCBCh
RB	equ	SYS-3	;Read single byte
WB	equ	SYS-6	;Write single byte
UTIL	equ	SYS-9	;Utility handler
SRESET	equ	SYS-12	;Reset to system
ERRL0	equ	SYS-15	;Error level 0 return from driver
ERRL1	equ	SYS-18	;Error level 1 return from driver
ERRL2	equ	SYS-21	;Error level 2 return from driver
SYSGLO	equ	SYS-23	;Pointer to system global area
CONIN	equ	SYS-26	;Console input one character
CONOUT	equ	SYS-29	;Console output one character
			;(trashes b)
CONTST	equ	SYS-32	;Console input ready test
PSCAN	equ	SYS-35	;parameter scanner entry
INTDN	equ	SYS-38	;interrupt done entry point
INTDK	equ	SYS-41	;interrupt processing entry

; 2) SYS Call Operations  (Code for Operation byte)

CREOP	equ	0	;Create file (de -> parms)
OPEOP	equ	1	;Open file (de -> name, hl = buff opt)
KILOP	equ	2	;Kill file (de -> name)
RBLOP	equ	3	;Read block of data
WBLOP	equ	4	;Write block of data
SPAOP	equ	5	;Move file cursor
EOFOP	equ	6	;Endfile a file
CLOOP	equ	7	;Close a file
CHTOP	equ	8	;Change type of file
CHAOP	equ	9	;Change attributes of a file
CHNOP	equ	10	;Change name of a file
INFOP	equ	11	;Get info about a file
SUNOP	equ	12	;Set defualt unit
RETOP	equ	13	;Return to CI
RESOP	equ	14	;Reset
ABTOP	equ	15	;Abort, then return to CI
SEKOP	equ	16	;Set file cursor to this point (Seek)
RNDOP	equ	17	;Make file randomly accessible
CAOP 	equ	18	;Close all files (or all on a unit)
CTLOP	equ	19	;Control/Status of driver
SREOP	equ	20	;Short reset to CI
DRDOP	equ	21	;Delimited read
DWROP	equ	22	;Delimited write
DSPOP	equ	23	;Delimited space
RTROP	equ	24	;Return and set trap

; 3) file Protection AttributeS

PKIL	equ	1	;Delete protect
PWRI	equ	2	;Write protection
PREA	equ	4	;Read protection
PFINF	equ	8	;Information protect (Files won't list)
PATR	equ	10h	;attribute change protection
PNAT	equ	20h	;Name and type change protection
PALO	equ	40h	;Disk allocation prohibition
PUSR	equ	80h	;User attribute

; 4) System Global Area

SGA	equ	0	;System Global Area base
GLCIF	equ	SGA	;CI input file#
GLCOF	equ	SGA+1	;CI output file#
GLUTF	equ	SGA+2	;Utility file#
GLECF	equ	SGA+3	;CI echo file#
GLUNI	equ	SGA+4	;defualt unit #
GLPRM	equ	SGA+5	;# of permanently open files
GLMXU	equ	SGA+6	;Max unit number (= # drives+1)
GLSWI	equ	SGA+7	;System switches
GLERS	equ	SGA+9	;Soft (level 2) error word
GLERM	equ	SGA+11	;Moderate (level 1) error word
GLERH	equ	SGA+13	;Hard (level 0) error word
GLTRP	equ	SGA+15	;CI return trap word
GLPRO	equ	SGA+17	;User memory protect word
GLFLG	equ	SGA+19	;character waiting flag
GLBYT	equ	SGA+20	;The character
GLRCH	equ	SGA+21	;File #0 read chr routine pointer
GLWCH	equ	SGA+23	;File #1 write chr routine pointer
GLTCH	equ	SGA+25	;Console test chr ready routine ptr
GLNCT	equ	SGA+27	;Null count following CR
GLVER	equ	SGA+28	;System version
GLDAT	equ	SGA+29	;Date (MM,DD,YY)
GLNAM	equ	SGA+32	;Disk name (8 ASCII characterS)
GLPAS	equ	SGA+40	;Password
GLBD1	equ	SGA+48	;Wait for interrupt routine address
GLBD2	equ	SGA+50	;Reserved for future use
GLIF1	equ	SGA+52	;interrupt enable flag
GLIF2	equ	SGA+53	;Disk int processing complete flag
GLLOW	equ	SGA+54	;Low address of buffer area
GLECH	equ	SGA+56	;Echo disable switch
GLLOK	equ	SGA+57	;Disk write lock switch
GLUPS	equ	SGA+58	;Upshift switch
GLRBC	equ	SGA+59	;Read-back check switch
GLBIO	equ	SGA+60	;Binary console I/O switch
GLLOG	equ	SGA+61	;Log file switch
GLVRB	equ	SGA+62	;Verbose switch
GLFTR	equ	SGA+63	;switch reserved for future use

; 6) CXBUF and OLBUF

CXBUF	equ	0BCC0H	;Command execution buffer
OLBUF	equ	0BDC0H 	;Overlay buffer

; 7) Status Bits in User Device Word

SBPRO	equ	1	;Settable prompt message
SBINT	equ	2	;interrupt driven
SBATB	equ	4	;does absolute tabs
SBFF	equ	8	;does form-feeds
SBSTB	equ	10h	;Settable tabs
SBTAB	equ	20h	;does tab's (Control-I)
SBIAT	equ	40h	;Interactive
SBDUP	equ	80h	;Duplicative

; 8)  Error Numbers

ERNEX	equ	1	;Nonexistant file
ERAEX	equ	2	;File already exists
ERUFN	equ	3	;Unassigned filenumber
ERPRO	equ	4	;Protection violation
ERNIF	equ	5	;File not type image
ERMOP	equ	6	;Illegal op on multiply open file
ERRAC	equ	7	;Random access to non-random file
ERRDV	equ	8	;Random access to device file
ERZBC	equ	9	;ZERO block count in device image file

ERIOP	equ	10	;Illegal PTDOS operation
ERIBF	equ	11	;Illegal buffer address
ERIBS	equ	12	;Illegal block size
ERINM	equ	13	;Illegal character in name
ERICU	equ	14	;Illegal character in unit
ERIUN	equ	15	;Illegal unit
ERNTL	equ	16	;Name too long
ERMEM	equ	17	;User memory protect violation
ERSMP	equ	18	;System memory protect violation
ERUCN	equ	19	;Unit conflict
ERIUP	equ	20	;Illegal utility operation
ERDRI	equ	21	;driver error
ERIDA	equ	22	;Illegal driver access
ERNCT	equ	23	;Driver not participating in this ctrl

EREOF	equ	24	;End of file encountered (OR BOF)
ERSEK	equ	25	;Seek address out of range

ERTOP	equ	26	;Too many files open
ERMOV	equ	27	;memory overflow
ERNID	equ	28	;No file ID'S left
ERDFL	equ	29	;Disk full
ERDIR	equ	30	;Directory full

ERFSB	equ	32	;File structure bad
ERFIC	equ	33	;File ID conflict
ERBSC	equ	34	;Block Size conflict
ERSCC	equ	35	;Sector conflict
ERCFS	equ	36	;Can't find sector
ERCRC	equ	37	;CRC error OR read/write borts
ERCFT	equ	38	;Can't find track
ERRBC	equ	39	;Read-back-check failed
ERLOK	equ	40	;Disk is write locked
ERXXX	equ	41	;Catestrophic error
ERSYN	equ	42	;Command syntax error
ERNAX	equ	43	;Name expected
ERNUX	equ	44	;File number expected
ERBCX	equ	45	;byte count expected
ERADX	equ	46	;address expected
ERVAX	equ	47	;Value expected
EROPX	equ	48	;Optional parameter expected
ERINA	equ	49	;Illegal file name
ERIFI	equ	50	;Illegal file number
ERIAD	equ	51	;Illegal address
ERIBC	equ	52	;Illegal byte count
ERIVA	equ	53	;Illegal value
ERIOS	equ	54	;Illegal option specifier
ERITY	equ	55	;Illegal type
ERIAT	equ	56	;Illegal attributeS
ERRED	equ	57	;Drive not ready

; 9) Parameter Scanner Operations

PSCO	equ	0	;Create if none exist and open
PSOP	equ	1	;Open file
PSFCO	equ	2	;Force create and open file
PSC	equ	4	;Create fileIF none exist
PSN	equ	5	;Read name to reg de
PSFC	equ	6	;Force create file
PSOPT	equ	8 	;Read all chrs until , ; CR 0
PSV	equ	85H	;Convert value into reg de
PSONE	equ	0A5H	;Read single byte

;Note: above values + 40h = User input buffer in reg hl

PSCN	equ	0D5H	;Convert my value to de, base in reg b

; 10) Utilities

UXOP	equ	0	;Explain error
UXOP1	equ	1	;Data for UXOP
UXOP2	equ	2	;Ditto, part 2
UCAT	equ	3	;Catalog routine

;----------------
;ASCII Characters
;----------------
BS	equ	08h
LF	equ	0Ah
VT	equ	0Bh		;Solos clear screen chr
CR	equ	0Dh
ESC	equ	1Bh
QUOTE	equ	27h		;single quote
SOLDEL	equ	5Fh		;Solos destructive delete
DEL	equ	7Fh		;delete
MODE	equ	80h		;MODE select

;*************
;Start of Code
;*************
	org	0

	lxi	sp,FGLLOW	;stack at top of user memory
	call	INIT		;parse command line, etc. with
				;..code that gets overwritten

;-------------------------------------------------------
;The source and destination file names are validated,
;and the option flags have been set according to the
;user input. 
;
;If no wildcards are in the source file name:
;   SRCFIL = complete source file name with unit
;   DSTFIL = complete destination file with unit
;If the source file name has wildcards:
;   SRCFIL = wildcarded source file name with unit
;   WSFIL = SRCFIL string without the unit
;   DSTFIL = bogus (don't care)
;   The directory is open
;   DIRFN=directory file number
;SRCFIL, WSFIL, and DSTFIL all terminated with nulls
;	
;BFLAG=1 to optimize block size, 0 to use the source
;   file's block size
;PFLAG=FF to ignore protections, 0 to respect them
;TFLAG=FF to copy only a specific file type, 0 otherwise
;CTYPE=specific file type to copy of TFLAG=1
;WFLAG=FF if wildcards specified in the source file
;SFLAG = FF if source is a driver that will be used
;DFLAG = FF if dest file is a driver that will be used
;DEFUNT=default unit, ASCII
;SRCUNT=source unit, ASCII
;DSTUNT=dest unit, ASCII
;--------------------------------------------------------

;If any wildcards are in the source file then we will
;loop to find every match in the source disk's directory

	lda	WFLAG
	ora	a		;single-file copy?
	cz	FCOPY		;y: just copy it
				;returns with Z set always
	jz	REXIT		;done with single-file copy

;***************
;Multi-file copy
;***************

;-----------------------------------------------
;Outer loop to read PDBCNT PDBSIZ-byte directory
;blocks into DIRBUF so we can search them.
;-----------------------------------------------
CMANY:	mvi	b,PDBCNT	;Number of blocks in the directory

DBLOCK:	push	b		;b=directory block count

	lxi	b,PDBSIZ	;read one entire block

	lda	DIRFN		;directory file number
	lxi	d,DIRBUF
	push	d

	call	SYS
	db	RBLOP		;read
	call	FIXERR		;Error-return

	pop	h		;DIRBUF
	pop	b		;b=dir block count

;-----------------------------------------------------------
;Inner loop to search each entry in this directory block for
;a match. Each block may have from 0 to 12 directory entries
;(given by the 1st byte in the block) that must be checked.
;-----------------------------------------------------------
	mov	c,m		;number of entries in this block
	inx	h		;point to 1st directory entry
	inx	h		;skipping over pointer

DFILE:	dcr	c		;more entries in this block?
	jm	NXDBLK		;n: go get next block

;------------------------------------------------------
;hl points to the next file name in the directory
;Clear Z (to avoid copying) if either:
;  IFLAG is not set and this file is info-protected
; or
;  TFLAG is set and this file is not the specified type
;
;If Z is set (meaning we will copy this file if it
;matches), then also tack a null on the end of the file
;name, in case the name is 8 characters long.
;------------------------------------------------------
	push	h

	lxi	d,PDEFPA	;look at protection attributes
	dad	d		;..in the directory

	lda	IFLAG		;FF if we should copy info-
	cma			;..protected files
	ana	m		;protection bits
	ani	PFINF		;info protected?
	jnz	ITOK		;y: don't copy

	dcx	h		;1st byte after after name
	dcx	h		;..is also the file type
	dcx	h

	lda	TFLAG
	ora	a		;Z set means no T= option

	mov	a,m		;a=this file's type
	mvi	m,0		;string-terminating null

	jz	ITOK		;T= option?

	lxi	h,CTYPE		;specified file type
	cmp	m		;matches this file's type?

ITOK:	pop	h		;beginning of name string

;-----------------------------------------------
;Z set if we should even look at this file.
;See if we can match the wildcarded name to this
;directory entry's name. If a match is found,
;then copy the file to the destination unit.
;-----------------------------------------------
	push	b
	push	h		;filename in directory

	cz	WMATCH		;match our wildcard?
				;returns Z if match
	cz	FCOPY		;y: copy the file

	pop	h

	call	CHKABT		;user trying to abort?
	pop	b

;----------------------------------
;Next entry in this directory block
;----------------------------------
	lxi	d,PDESIZ	;bytes/directory entry
	dad	d
	jmp	DFILE		;next entry in this block

;--------------------
;Next directory block
;--------------------
NXDBLK:	dcr	b		;more blocks?
	jnz	DBLOCK

;---------------------------------------
;Done. Report the number of files copied
;---------------------------------------

	call	CILPRT
	db	'Copied',' '+80h

	lda	CFCNT
	mov	h,a		;temp save

;Print a in decimal

PADEC:	lxi	b,100*256	;b=divisor,c=no leading zeros
	call	PDDIG		;print high digit

	mov	a,e		;remainder becomes dividend
	mvi	b,10		;middle digit divisor
	call	PDDIG

	mov	a,e		;remainder is lsb
	call	PASCII		;print lsb always

;Finish report message

	call	ILPRNT
	db	' fil','e'+80h

	dcr	h		;exactly 1?
	mvi	a,'s'		;be gramatically correct
	cnz	CONOUT		;n: it's plural

	jmp	REXIT		;done

;---Local subroutine----------------
;Compute and print one decimal digit
;On Entry:
; a=divisor
; b=dividend
; c=0 means don't print leading 0
;On Exit:
; e=remainder
;-----------------------------------
PDDIG:	mvi	d,0FFh		;accumulate dividend

PDDLUP:	inr	d		;this loop goes once too many
	sub	b
	jnc	PDDLUP

	add	b		;compute a=remainder
	mov	e,a		;remainder for return

	mov	a,d		;dividend
	ora	c		;leading 0?
	mov	c,a		;remember for next digit
	rz			;y: done

	mov	a,d		;dividend	

;Fall into PASCII to print digit & return (trashes b)

;***Subroutine***
;Print a as ASCII
;Trashes psw,b
;****************
PASCII:	adi	'0'		;make it ASCII
	jmp	CONOUT		;trashes b

;***Subroutine**********************************
;Match wildcarded file name with a complete name
;from the directory
;On Entry:
;  WSFIL points to null-terminated wildcard name
;    (without a unit number)
;  hl points to a directory name
;  SRCUNT = source unit number
;  DSTUNT = destination unit number
;On Exit, the directory entry matches WSFIL
;  COmplete file name is in SRCFIL and DSTFIL,
;    with units appended
;  Z set
;On Exit, no match
;  Z cleared
;Trashes a,b,de
;***********************************************
WMATCH:	push	h		;name from directory
	lxi	d,WSFIL		;wildcarded file name

;-----------------------------------------------------
;If the first character of the wildcarded filename is
;'<', then set b to FF and skip over this character in
;WSFIL. Otherwise, set b to 0.
;-----------------------------------------------------
	mvi	b,0

	ldax	d		;1st chr wildcard?
	cpi	'<'

	jnz	WM1
	inx	d		;skip over leading <
	dcr	b		;b=FF
WM1:

;----------------------------------------------------
;Alignment loop: keep trying to find a match of the
;wildcarded name, starting with each character of the
;directory name, if b=FF (meaning the ist character
;of the wildcarded name is '<'). Otherwise, just
;check for alignment starting with the 1st character
;of the directory name.
;----------------------------------------------------
WLOOP1:	push	h		;dir starting position
	push	d		;wildcarded name start

;-----------------------------------------------------
;Match Loop: does wildcarded name match this directory
;name if we start with the directory character at hl?
;b=FF if 1st chr is '<', 0 otherwise
;-----------------------------------------------------
WLOOP2:	ldax	d		;target name chr
	cpi	'>'		;wildcard from here on?
	jz	MATCH

	cmp	m		;test another character
	jz	WM2		;..matches?

;----------------------------------------------------
;No match for this alignment. Try another alignment,
;but only if the 1st chr is '<' (b=FF) and we haven't
;tried all possible alignments.
;b=FF if 1st chr is '<', 0 otherwise
;----------------------------------------------------
	pop	d		;wildcarded name start
	pop	h		;directory name start

	inx	h		;next alignment
	mov	a,m		;terminating null?
	ana	b		;or no leading '<'?
	jnz	WLOOP1

;----------------------------------------------------------
;No match found. Clean up the stack and exit with Z cleared
;----------------------------------------------------------
	pop	h		;fix stack (dir file name)
	dcr	a		;Clear Z, a=FF
	ret	

;--------------------------------------------
;Match so far. Check the next character,
;unless we've reached both terminating nulls.
;--------------------------------------------
WM2:	inx	h
	inx	d

	ora	a		;end of both strings?
	jnz	WLOOP2		;..meaning all chrs match?

;-----------------------------------------------------
;This directory name matches this wildcarded name.
;Copy the file name from the directory into SRCFIL and
;DSTFIL, tack on unit numbers, and exit with Z set.
;-----------------------------------------------------
MATCH:	pop	d		;don't need these anymore
	pop	h		;..so fix the stack

	pop	d		;directory file name

	lxi	h,SRCFIL
	lda	SRCUNT
	call	COPYFN

	lxi	h,DSTFIL
	lda	DSTUNT	

;Fall into COPYFN

;***Subroutine*****************************************
;Copy a file name, and tack on the unit if needed
;On Entry
;  a = unit number to tack on
;  de = source of copy, which may include a unit number
;  hl = destination for copy
;On Exit:
;  a=0, Z set
;Trashes b,hl
;******************************************************
COPYFN:	push	d
	mov	b,a

CFNLUP:	ldax	d		;copy from here

	mov	m,a		;..to here

	inx	h
	inx	d

	cpi	'/'		;already have a file number?
	jz	CFN1

	ora	a
	jnz	CFNLUP

;tack on the unit number

	dcx	h		;back up 1
	mvi	m,'/'		;install / before unit
	inx	h

CFN1:	mov	m,b		;install unit number

	inx	h		;terminate string here
	xra	a		;a=0 for return too
	mov	m,a		;terminating null

	pop	d
	ret

;***Subroutine**********************************************
;Copy source file to destination file
;If the source file is a core PTODIS file (its file number
;is MAXPID or less), then ask the user before copying, as
;copying these files will probably cause trouble.
;If the source file is read-protected and protections are
;not overridden (with P=1 option), then notify the user and
;skip this file. If the destination already exists, then
;ask the user if we should overwrite it and skip this write
;if the user says no.
;On Entry:
;  SRCFIL has source file name with unit number
;  DSTFIL has destination file name with unit number
;  SFLAG set if this source is to be used as a device driver
;  DFLAG set if destination is to be used as a driver
;On Exit:
;  Z set
;  Files are closed
;Trashes a,bc,de,hl
;***********************************************************
FCOPY:

;If the source is to be used as a driver, then we already
;have its info, and its type & attributes are ok

	lda	SFLAG		;source to be used as a driver?
	ora	a
	jnz	SRCDVR		;driver: ready to go

;Ask PTDOS to get the source file info and put it in INFOBF

	call	GSINFO

;If the destination file is a core PTDOS file (such as BOOT,
;DIRECTRY, the NEXTID file, etc) that must be in a specific
;place on disk and/or have a specific file number, then don't
;copy it. It's Ok to copy such a file to a file with a
;different name though. (This may be useful for debugging a
;disk, perhaps.)

	lhld	IFILID		;source file ID
	mov	a,l		;16-bit compare
	sui	MAXPID		;max allowed id
	mov	a,h
	sbi	0
	cc	CHKSYS		;print message, don't copy
				;dest name is explicit

;Remember the source file's file type because we may change
;it while we copy the file

	lda	IFTYPE		;File type from INFOBF
	sta	SFTYPE
	mov	c,a		;remember IFTYPE

;Remember the source file's block size because it is
;the default dest block size if we don't tune it.

	lhld	IBSIZE		;source file's block size
	shld	BLKSIZ		;..becomes dest block size

;Remember the source file's protection atributes
;because we may change them while we copy the file
;c=IFTYPE

	lxi	h,IPATTR
	mov	a,m		;prot. atributes from INFOBF
	sta	SPATTR

;If the source file is read-protected, and we are supposed
;to override protections (S=P), then unprotect it in the INFOBF
;structure, and remember to change the source file protection
;attributes later by setting the flag to 1. If we can't change
;the source's protection (because the user didn't set S=P)
;then exit with an error message and Z set.
;c=IFTYPE
;hl=IPATTR

	mvi	a,PREA		;read protection bit
	call	CHKFPA		;hl=IPATTR
	jnz	RDPROT		;error if still read-protected

;Device drivers cannot be copied because PTDOS will invoke the
;driver to get data, rather than copying the driver. If this is
;a device driver (that we are supposed to copy) then we convert
;it temporarily to an Image file of type TEMPFT, and remember
;that we did, so that we can put it back when we are done.
;c=IFTYPE
;hl=IPATTR

	inr	c		;test for 0FFh: device driver?
	jnz	TYPOK		;n: file is already copiable

;If changing the file type is blocked, then change the
;protection attributes first in INFOBF, and remember
;to change the source file protection attributes later.
;hl=IPATTR

	mvi	a,PNAT		;File type change protection
	call	CHKFPA		;hl=IPATTR

;Now change the file type in the INFOBF structure. Remember
;that we need to change source file's file type, by setting
;the FTFLAG to 1.
;hl=IPATTR

	mvi	a,TEMPFT	;temporary file type
	sta	IFTYPE		;File type in INFOBF

	mvi	a,1		;flag file type for change	
	sta	FTFLAG
TYPOK:

;Make the dest file writeable etc. in INFOBF, and remember
;the modified attributes from CHKFPA above, in h for CHNGPA
;hl=IPATTR

	mov	b,m		;b=modified attributes
	mvi	m,0		;no protection yet

;Change the source file attributes, if flagged for change
;by CHKFPA above. de points to the file name still.
;b=modified attributes
;h=modified attributes

	lxi	h,PAFLAG
	mov	a,m
	inr	m		;2: attrib's will have changed
	mov	h,b		;attributes 

	ora	a		;was PAFLAG<>0?
	cnz	CHNGPA		;y: h=new attributes

;Convert the source file type, if flagged for change above.

	lxi	h,FTFLAG
	mov	a,m		;need to change file type?
	inr	m		;2 means file type has changed
	ora	a		;need to change file type?
	cnz	CHGTFT		;y: temp change file type

;Open the source file and announce the file we will copy to

SRCDVR:	call	SRCOPN

	call	CILPRT
	db	' '+80h		;new line (always)

	call	PDSTFL

;If the source has wildcards, then set
;the dest file type the same as the source

	lda	WFLAG		;multi-file copy?
	ora	a
	jz	SFC

	lda	SPATTR
	sta	DPATTR
SFC:

;------------------------------------------------------------
;Loop to copy the source file to the destination file, one
;buffer-full at a time. On entry here, the destination file
;is not yet open. It will be opened after the first buffer-
;full of source file data has been read into the buffer. This
;allows us to optimize the block size for the destination
;file for any file that fits in the (rather large) buffer.
;------------------------------------------------------------
	xra	a
	mov	l,a		;dest file not yet open
	sta	BIGFLG		;not a big file yet

;Read up to bc bytes from the source, and save them at de

COPLUP:	push	h		;l is dest-open flag
	call	SREAD		;a=FF if EOF
				;hl=byte count

	shld	SRCSIZ		;remember byte count for VERIFY
	mov	b,h
	mov	c,l		;bc=byte count

	lxi	h,BIGFLG	;another buffer-full
	inr	m

	pop	h		;l=1 if dest is open

	mov	h,a		;h=FF if EOF
				;l=1 if dest open

;Create and open the destination file if it's not already
;open. Compute ideal block size if the entire file fit in
;the buffer. Otherwise use the source file's block size.
;bc = number of bytes read
;h = FF if EOF encountered
;l = 1 if destination file is open, 0 if not

	dcr	l		;dest file open?
	cnz	DOPEN		;n: go open it
	jnz	SKIPWR		;can't write to this file

;Write from the buffer to the destination disk
;bc = number of bytes to write
;h = FF if EOF encountered, 0 if not

	lxi	d,CPYBUF	;beginning of source buffer

	lda	DSTFN		;destination file number
	call	SYS
	db	WBLOP		;write
	call	FIXERR		;Error-return

	mvi	l,1		;dest file is open for sure
	inr	h		;Z means EOF, NZ means not yet
	jnz	COPLUP		;no EOF: read more

;--------------------------------------------------
;This file copy is complete. Close the source file.
;--------------------------------------------------
	lda	SRCFN
	call	FCLOSE

;----------------------------------------------------------
;If the source file has an index block and the destination
;is not used as a device driver, then create an index block
;for the destination file. Note that if the source is a
;device driver then it won't have an index block anyway. 
;----------------------------------------------------------
	lda	DFLAG		;is the destination a driver?
	cma			;a=0 if driver
	mov	b,a		;temp save		

	lhld	IINDBS		;Source file index pointer
	mov	a,h
	ora	l		;was there an index block
	ana	b		;.and it wasn't a driver?
	jz	NOINDX

	lda	DSTFN		;destination file number
	call	SYS
	db	RNDOP		;make file random access
	call	FIXERR		;Error-return
NOINDX:

;-----------------------------------------
;Close the destination file
;b=inverted DFLAG (0 if driver, FF if not)
;-----------------------------------------
	call	DCLOSE

;If the destination was used as a device driver,
;then do not verify the copy or modify its file 
;type or protection attributes.

	inr	b		;b=inverted DFLAG
	jnz	DDM2		;..was FF?

;Verify the write (if possible)

	call	VERIFY

;If the source file was used as a driver then install the
;default (or set by T=) file type onto the destination.
;otherwise, set the destination file type to be the same
;as the source file type.

	lhld	SFTYPE		;l=source file type
				;h=CTYPE (user-specified)

	lda	SFLAG		;nz means source driver
	ora	a
	jnz	DDM1
	mov	h,l		;h=SFTYPE
DDM1:

	lxi	d,DSTFIL
	call	CHNGFT		;h=file type

;Install protection attributes onto the destination file

	lxi	d,DSTFIL
	lhld	DPATTR-1	;h=dest prot. attributes
	call	CHNGPA		;apply to destination
DDM2:

;Bump the copied-file counter

	lxi	h,CFCNT
	inr	m
SKIPWR:

;Fall into TARSTR to restore source file type and attributes

;***Subroutine*************************************
;Restore source file type and protection attributes
;On Entry:
;    SRCFIL is 0-terminated file name string
;       which may include unit number
;    PAFLAG = 2 if file protection
;             attributes must be restored
;    SPATTR = original protection attributes
;    FTFLAG = 2 if file type must be restored
;    SFTYPE = original source file type
;On Exit:
;   Z set
;   PAFLAG = 0
;   FTFLAG = 0
;Rude abort if PTDOS error
;trashes a,de,hl
;**************************************************
TARSTR:	lxi	d,SRCFIL	;for CHGFT and CHGPA

	lhld	FTFLAG		;l=flag, h=original value		
	dcr	l
	dcr	l		;was the file type changed?
	cz	CHNGFT		;y: put it back

	xra	a
	sta	FTFLAG

	lhld	PAFLAG		;l=flag, h=original value
	dcr	l
	dcr	l		;were attributes changed?
	cz	CHNGPA		;y: put them back

	xra	a		;clear Z too
	sta	PAFLAG

	ret

;---Subroutine end--------------------
;Tell user the file is read-protected, 
;and that we can't copy this file
;On Entry:
;  SRCFIL=source filename
;On Exit:
;  Z set
;-------------------------------------
RDPROT:	call	CILPRT
	db	' '+80h

	lxi	h,SRCFIL
	call	PRINTF

	call	PIPROT		;print ' is protected'

	xra	a		;Set Z for ret
	ret			;done with this file

;---Local Subroutine-------------------------
;See if we can write a system file
;On Entry:
;  DWFLAG = 0 if the dest filename is explicit
;Chucks return address and returns from FCOPY
;if DWFLAG is not 0
;---------------------------------------------
CHKSYS:	lda	DWFLAG		;unspecified dest will use
	ora	a		;..the source file's name
	rz			;dest file is specified

	pop	h		;chuck return address

;Tell user we can't write this file

	call	ILPRNT
	db	' can',QUOTE,'t cop','y'+80h

	xra	a		;Set Z fore ret
	ret			;done with this file

;***Subroutine********
;Print ' is protected'
;On Exit:
;  a<>0
;Trashes b
;*********************
PIPROT:	call	ILPRNT
	db	' i','s'+80h

;Fall into PPROT

;***Subroutine********
;Print 'protected'
;On Exit:
;  a<>0
;Trashes b
;*********************
PPROT:	call	ILPRNT
	db	' protecte','d'+80h
	ret

;***Subroutine***************
;Print destination file name
;Trashes psw,b,hl
;****************************
PDSTFL:	lxi	h,DSTFIL

;Fall into PRINTF

;***Subroutine***************
;Print null-terminated string
;On Entry:
;  hl points to string
;Trashes psw,b
;****************************
PRINTF:	push	h

PFLOOP:	mov	a,m
	ora	a
	cnz	CONOUT		;trashes b
				;a unchanged

	inx	h
	ora	a
	jnz	PFLOOP

	pop	h
	ret

;***Subroutine****************************
;Verify file write
;On Entry:
;  BIGFLG = 1 if the entire file fit
;    in CPYBUF (and is still there)
;  Source and destination files are closed
;  SRCSIZ = number of bytes in CPYBUF
;  Source file alread has its attributes
;    modified to allow reading
;On Exit:
;  Source and destination again closed
;  success: return
;  Fail, user chooses to ignore: return
;  fail: user chooses not to ignore: abort
;Trashes all registers
;*****************************************
VERIFY:	call	CHKABT		;user trying to abort?

	lda	BIGFLG		;did the file fit in the buffer?
	mov	b,a		;1 means one buffer-full
	dcr	b		;0 means source fit in the buffer

;If the source is a driver and the entire file did not fit in
;the buffer, then we can't verify it.

	lda	SFLAG		;is source a driver?
				;(FF means yes)
	ana	b		;b=0 if exactly 1 buffer-full
	rnz			;NZ means driver, and file did
				;..not fit in the buffer

;If the entire file fit in CPYBUF (b=0) then we don't
;need to re-read it. In that case SRCSIZ bytes are in
;CPYBUF. Otherwise, re-open the source file
;a=0 here

	cmp	b		;source fits in buffer?
	cnz	SRCOPN		;n: re-open the source file
				;sets b to not 0

;Open the destination file so we can read it

	lxi	d,DSTFIL	;re-open the dest file
	call	FOPEN
	sta	DSTFN		;remember dest file number

;set up to compare source and destination
;We will use the register as follows:
; b = 0 if entire file fit in the buffer
; c = VFYBUF byte count
; de = VFYBUF pointer
; hl = CPYBUF pointer
; on stack: CPYBUF byte count

	lhld	SRCSIZ		;source byte count in CPYBUF
	push	h
	lxi	h,CPYBUF	;data pointer
	lxi	d,VFYBUF	;data pointer
	mvi	c,0		;number of bytes in VFYBUF

;Loop to compare one byte at a time,
;reloading buffers as they becomes empty

VERLUP:	xthl			;pointer onto stack
	mov	a,h		;hl=source byte count
	ora	l		;need to refill source?
	jnz	VLP1

	cmp	b		;is whole file in the buffer?
	cnz	VSREAD		;n: get some more
	jz	VREND		;Z means EOF, no data
				;..c should be 0 here too

VLP1:	dcx	h		;about to compare a byte
	xthl			;hl has pointer again

	mov	a,c
	ora	a		;was c 0?	
	cz	VDREAD

	jz	MSIMCH		;Z means EOF, too soon

	dcr	c		;bump dest byte count

	ldax	d		;get a dest byte
	cmp	m		;match?

	inx	h
	inx	d
	jz	VERLUP

;File mismatch

MSIMCH:	mvi	c,1		;fail indicator

;Source eof found. C=0 if also end of dest.

VREND:	pop	h		;clean up stack
	
;Close the files we opened for verification

	lda	BIGFLG		;need to close?
	dcr	a
	lda	SRCFN
	cnz	FCLOSE

	call	DCLOSE

;Verify done.
;C=0 for success, <>0 for fail

	call	ILPRNT		;on the line with the file name
	db	' veri','f'+80h

	mov	a,c
	ora	a		;verify sucess?
	jnz	VFAIL

;--------------
;Verify success
;--------------
	call	ILPRNT
	db	'ie','d'+80h

	ret			;done with verify

;------------------------------------
;Verify failed. Announce failure.
;If multi-file copy then ask the user
;what to do on fail (ignore/abort)
;On Entry:
;  'Verif' has already been printed
;  WFLAG=0 if only 1 file verified
;  WFLAG<>0 if multi-file copy
;------------------------------------
VFAIL:	call	ILPRNT
	db	'y fai','l'+80h

	lda	WFLAG		;multi-file?
	ora	a
	rz

	call	ILPRNT
	db	'. Qui','t'+80h
	call	ASKYN

	rz			;ignore

	jmp	ABEXIT		;abort: close all and exit

;***Subroutine****************************************
;Read up to VBSIZE bytes from the dest into the VFYBUF
;On Exit:
;  c=number of bytes read (<256)
;  de=VFYBUF
;  Z set if no bytes read (EOF)
;Trashes a
;*****************************************************
VDREAD:	push	b		;can't trash b

	lxi	b,VBSIZE	;max bytes (b=0)
	lxi	d,VFYBUF	;DMA address
	lda	DSTFN		;dest file number
	call	SYS
	db	RBLOP		;read
	call	RBERR		;Error-return: either EOF or
				;..real error


;Compute c=number of bytes read
;c=VBSIZE-c

	mvi	a,VBSIZE
	sub	c		;Z set if 0

	pop	b
	mov	c,a

	lxi	d,VFYBUF
	ret

;***Subroutine******************************************
;Read up to CBSIZE bytes from the source into the CPYBUF
;On Exit:
;  Z set if EOF (no bytes read)
;  hl=number of bytes read
;  a = FF if EOF encountered
;  a<FF otherwise
;  data pointer on the stack = CPYBUF
;*******************************************************
VSREAD:	pop	h		;return address
	xthl			;data pointer
	lxi	h,CPYBUF	;buffer beginning
	xthl			;on the stack
	push	h

;Fall into SREAD

;***Subroutine******************************************
;Read up to CBSIZE bytes from the source into the CPYBUF
;On Exit:
;  Z set if zero bytes read
;  a = FF if EOF encountered
;  a<FF otherwise
;  hl=number of bytes read
;*******************************************************
SREAD:	push	d
	push	b

	lxi	b,CBSIZE	;max bytes
	lxi	d,CPYBUF	;DMA address
	lda	SRCFN		;source file number
	call	SYS
	db	RBLOP		;read
	call	RBERR		;Error-return:
				;b=FF if EOF
				;(Aborted if real error)

;Compute hl=the number of bytes actually read
; hl:=de-CPYBUF
; Z set if none transfered

	lxi	h,-CPYBUF	;16-bit subtract
	dad	d

	mov	a,h		;any bytes transfered?
	ora	l
	mov	a,b		;a=eof flag from RBERR

	pop	b
	pop	d

	ret

;***Subroutine************************************
;Open a file
;On Entry:
;  de points to a null-terminated file name string
;     that includes a unit number
;On Exit:
;  a = file number
;  b=file type
;  hl=0
;*************************************************
FOPEN:	lxi	h,0		;static buffering
	call	SYS
	db	OPEOP		;File open
	call	FIXERR		;Error return point

	ret

;***Subroutine************************************
;Close the destination file
;*************************************************
DCLOSE:	lda	DSTFN		;destination file number

;Fall into FCLOSE

;***Subroutine************************************
;Close a file
;On Entry:
;  a = file number
;*************************************************
FCLOSE:	call	SYS
	db	CLOOP		;Close
	call	FIXERR		;Error-return

	ret

;***Subroutine***************
;Open the source file
;On Exit:
;  a=source file number
;  b<>0
;  de=SRCFIL
;  SRCFN = source file number
;  SRCSIZ=0
;Trashes psw,hl
;****************************
SRCOPN:	lxi	d,SRCFIL
	call	FOPEN
	sta	SRCFN		;remember source file number
	shld	SRCSIZ		;hl=0 from FOPEN
	mov	b,d		;(not 0)
	ret

;***Subroutine*************************************************
;Create and open the destination file. Set the destination file
;block size as follows:
; If BFLAG=FF (meaning the block size should be optoimized) AND
;    the entire file fits in the buffer AND the destination is
;    not to be used as a driver, then calculate the 'ideal'
;    block size for the destination file.
; Otherwise, if the source file isn't to be used as a
;    driver, then use the source file's block size.
; Otherwise, use the default block size.
;On Entry:
;  bc = number of bytes read from the source
;  h = 0 if source EOF encountered, 1 if not
;  BLKSIZ=default file block size
;  DFLAG = FF if dest is to be used as a device driver
;  SFLAG = FF if source is to be used as a device driver
;On success Exit (meaning the file is open and ready to
;receive data:
;  DSTFN = file number
;  Z set
;  IBSIZE=block size, per above algorithm
;  The file is created unless it's a device driver
;  The file is open
;On fail exit (Fail the file already exists anbd the user says
;not to overwrite it):
;  Z clear, indicating this file should be skipped
;Trashes de
;*************************************************************
DOPEN:	push	h		;h=source EOF flag
	push	b		;number of bytes read


	dcr	h		;h=0 if source EOF encountered
	lda	BFLAG		;0 if no blocksize optimize
	ana	h		;either will stop optimization

	lhld	BLKSIZ		;default block size (either
				;..ACOPY's defualt or the
				;..source's block size)

	jz	DCREAT		;n: use source file block size

;-----------------------------------------
;Calculate "ideal" block size based on the 
;size of the complete file in the buffer.
;-----------------------------------------
	lxi	d,-1		;negative divisor
	push	d

;Calculate the minimum block size (that's less than MAXBLK)
;to fit the file into -de blocks. If the file fits, we are
;done. (Block sizes are each an optimal size from page 5-4)

;bc = filesize/(trial number of blocks)
;de = trial block size (smallest block is only 100h)

DBSL1:	lxi	d,SECSIZ	;1st block is smaller

DBSL2:	mov	a,e		;16-bit compare hl-bc
	sub	c		;de=trial block size
	mov	a,d		;bc=file size/(blocks/file)
	sbb	b		;carry set if bc>de

	jnc	DBSL5		;does it fit?

;next block size

	lxi	h,SECSIZ+ISGSIZ	;sector plus inter-sector gap
	dad	d		;hl=next block size
	xchg			;de=trial block size

	lhld	MXIBLK		;max allowed block size

	mov	a,l		;16-bit compare
	sub	e		;hl=max allowed block size
	mov	a,h		;de=trial block size
	sbb	d		;carry set if de>hl

	jnc	DBSL2		;try again if not too big

;The file is too big to do with this many blocks.
;Try again with one more block.

DBSL3:	pop	d		;de= -(last block size try)
	pop	h		;hl=file size
	push	h
	dcx	d		;next -divisor
	push	d

;hl = file size
;de = -(number of blocks to try)
;Compute bc = hl/(-de) rounded up

	lxi	b,0		;result will be in bc
	dcx	h		;so exact fit doesn't
				;..get rounded up

DBSL4:	inx	b
	dad	d		;loop to divide hl by de
	jc	DBSL4

	jmp	DBSL1		;try again with more blocks

;de = computed block size. Clean up stack and carry on

DBSL5:	xchg			;result to hl
	pop	d		;junk divisor on stack

;---------------------------------------------
;hl=Block size. Create the file (unless we are
;using it as a driver), and open it for use.
;---------------------------------------------
DCREAT:	shld	IBSIZE		;set the block size

;Don't create destination if it's a driver we are using

	lda	DFLAG		;use dest as driver?
	inr	a
	jz	DDRIVR		;y: ready to go

;check to see if the destination file already exists

	call	GDINFO		;Z clear if file exists
				;returns de=DSTFIL
	jz	NOKILL

;The destination file already exists.
;Skip copying if the file is already open.

	lda	DOFLAG
	ora	a
	jnz	OPNERR

;Skip copying if the file is write- or kill-protected and
;the user didn't say to override protections (with S=P)

	lda	PFLAG
	cma			;FF means don't override
	lxi	h,DIPTTR
	ana	m
	ani	PWRI or PKIL	;write- or kill-protected?
	cnz	PIPROT		;print ' is protected'
	jnz	SKPCRE

;Ask the user if we should replace the existing
;destination file, and skip this file if the answer is no
;de=DSTFIL
;The file name's already been printed

	call	ILPRNT		;trashes b
	db	' exists. Replac','e'+80h

	call	ASKYN		;a<>0 always
	jz	SKPCRE		;n: skip

;Kill the old destination file. (We kill the file instead of
;just overwriting it so that we can set its block size (either
;the source file's block size or from the above block size
;optimization, depending on the user's 'B=' option choice.)
;de=DSTFIL

	mvi	h,0		;new protections
	call	CHNGPA		;remove protections

	call	SYS
	db	KILOP		;kill the old file
	call	FIXERR		;error-return point
NOKILL:

;Specify DSTFIL as destination by pasting the dest
;file name into INFOBF, and right-filling with 0's
;de=DSTFIL

	lxi	h,IFNAME
	mvi	b,10		;8 chr name plus '/n'

DCR0:	ldax	d
	mov	m,a
	inx	h
	ora	a		;right-fill with 0's
	jz	DCR1
	inx	d

DCR1:	dcr	b
	jnz	DCR0

;Set the destination file type to a temp value
;while we copy (in case it's a device driver)

	mvi	a,TEMPFT	;temp file type
	lxi	d,IFTYPE
	stax	d

;Create the destination file using the File Info.
;de points to INFOBF:
;  IFTYPE = file type
;  IBSIZE = block size
;  IAPTTR = protection attributes
;  IFNAME = file name followed by /n for unit n
;  IZERO = null termination

	mvi	b,0FFh		;don't skip-file flag
	call	SYS
	db	CREOP		;File create
	call	FIXERR		;Error-return point

;Open existing driver or newly-created destination file

DDRIVR:	lxi	d,DSTFIL
	call	FOPEN
	sta	DSTFN		;remember file number

	xra	a		;set Z to cause copy

;Exit with the Z flag set to indicate that this
;file should be copied, or cleared if it should not

SKPCRE:	ora	a
	pop	b		;number of bytes read
	pop	h		;h=EOF flag
	ret

;---------------------------
;Error: file is already open
;---------------------------
OPNERR:	call	ILPRNT		;trashes b
				;a<>0
	db	' is in us','e'+80h
	jmp	SKPCRE

;***Subroutine*********************************************
;Get Y/N answer from user, echoing, and allowing characters
;to be deleted until CR is typed. (See page 9-8) Note that
;CONIN strips the parity bit, but the MODE key is 80h.
;On Exit:
;  a='N' and Z set if 'N'
;  a='Y' and Z clear if 'Y'
;Trashes bc,hl
;**********************************************************
ASKYN:	call	ILPRNT		;trashes b
	db	' (Y/N)?',' '+80h

ASKYN0:	lxi	h,0FFh		;h=1st chr, l=chr count-1

ASKYN1:	inr	l		;character count

	mov	c,h		;previous chr
	call	GETCON		;get chr, check for MODE
	ani	('a'-'A')xor 0FFh ;uppercase
				  ;also converts DEL to SOLDEL

	mov	h,a		;this chr
	cpi	SOLDEL
	jnz	ASKYN2

	dcr	l		;backup
	jm	ASKYN0		;unless we can't

	dcr	l		;account for inr l above
	
ASKYN2:	call	CONOUT		;echo, trashes b

	cpi	CR
	jnz	ASKYN1

	dcr	l		;1 chr +CR?
	jnz	ASKYN

	mov	a,c
	cpi	'N'
	rz

	cpi	'Y'
	jnz	ASKYN		;huh?

	ora	a		;clear Z for Yes
	ret

;***Subroutine*********************
;Check for abort from user
;On Exit:
;  Z set if no chr waiting
;  Z clear and a=chr if chr waiting
;  Direct abort if MODE typed
;Trashes b
;**********************************
CHKABT:	call	CONTST		;anything waiting?
	rz

;Fall into GETCON

;***Subroutine**********************************
;Get a console chr and check for abort from user
;On Exit:
;  Z clear and a=chr
;  Direct abort if MODE typed
;Trashes b
;***********************************************
GETCON:	call	CONIN
	ora	a	;MODE=80h. PTDOS strips parity
	rnz

;Fall into ABEXIT

;***Error Exit********************************************
;Abort exit 
;Close all files, restore source file attributes and file
;type, repair PTDOS's SYSGLO, inform user, return to PTDOS
;*********************************************************
ABEXIT:	call	TARSTR		;repair source file if needed

;Done. Fall into REXIT

;***Exit******************************************
;Restore PTDOS to the way we found it, and return
;to PTDOS, reseting the command interpreter
;*************************************************
REXIT:	mvi	a,PCLOSA	;Close all files
	call	SYS		;..on all units
	db	CAOP
	nop			;CAOP generates no errors
	nop
	nop

;restore PTDOS lower address limit

	lhld	GLOSAV
	xchg			;de has original value

	lhld	GLOADR		;hl=PTDOS GLLOW address
	mov	m,e
	inx	h
	mov	m,d

;UnZAP PTDOS, if it's ever been ZAPped

	lhld	UNZAP		;unZAP address
	mov	a,h
	ora	l		;0 means never ZAPped
	jz	NOUNZP
	mvi	m,PATR
NOUNZP:

;------------------------------------------------------
;Reset-return to PTDOS. Note that this restores PTDOS's
;error traps in SYSGLO. (See pages 6-7 and 7-16.)
;------------------------------------------------------
	call	SRESET		;Reset-Return to PTDOS

;***Error Subroutine****************************************
;Error return from RBLOP call to SYS. If the error is EOF,
;then return to the calling routine with b=FF (indicating
;EOF). If not, abort with a real error.
;On entry:
;  a = error code from PTDOS
;  top of stack = address of routine that generated the error
;On EOF exit:
;  b=FF if file EOF encountered
;On error exit to FIXERR:
;  a = error code
;***********************************************************
RBERR:	mvi	b,0FFh		;EOF indication for rz
	cpi	EREOF		;end of file?
	rz			;y: carry on

;Fall into FIXERR
	
;***Error Exit**********************************************
;PTDOS disk error: abort operation. Put source disk back
;together, explain errors, exit to PTDOS
;On Entry:
;  a = error code from PTDOS
;  top of stack = address of routine that generated the error
;  PAFLAG = 2 means the source file protection attributes
;             have been changed
;  FTFLAG = 2 means the source file type has been changed
;  SPATTR = original protection attributes
;  SFTYPE = original source file type
;  UNZAP = the ZAP address for this version of PTDOS, or 0
;***********************************************************
FIXERR:	push	psw		;save error code

	lxi	h,TARFLG	;been here before?
	dcr	m
	cnz	TARSTR		;restore file type & attributes

	lda	FTFLAG		;did we successfully restore
	lxi	h,PAFLAG	;..the attributes and file
	ora	m		;..type on the source disk?
	jz	FERR1

	call	CILPRT		;n: say so (trashes b)
	db	'Disk',' '+80h
	lda	SRCUNT		;already ASCII
	call	CONOUT
	call	ILPRNT		;trashes b
	db	' may be bad','!'+80h
FERR1:

;Recover error code from entry, and let PTDOS explain it

	pop	psw		;recover error code

;Fall into PTDER

;***Error Exit***********************************************
;Get PTDOS to explain a SYS error, close any open files,
;and return (with reset) to PTDOS
;(Pages 6-8 and 8-1)
;On Entry:
;  a = error code
;  top of stack = address of routine that generated the error
;  4 bytes before this is the system opcode
;************************************************************
PTDER:	sta	ERMCD		;Store error code for UTIL

	pop	h		;Get calling address
	dcx	h		;Back up and get SYS opcode
	dcx	h
	dcx	h
	dcx	h
	mov	a,m		;a=SYS opcode
	sta	COMCD		;Give SYS opcode to UTIL

	lxi	h,CPYMSG	;string for 'Called from' msg
	mvi	a,UCFROM	;UTIL ctrl word (see page 8-1)
				;Returns when done
	call	UTIL
	db	UXOP		;Explain error
	jmp	UERROR		;UTIL error return
COMCD:	db	0FFh		;(gets replaced)
ERMCD:	db	0FFh		;(gets replaced)
				;UTIL normally returns here
;---------------
;Return to PTDOS
;---------------
UERROR:	jmp	REXIT

;----------------------------------------
;This file's name for UTIL error messages
;----------------------------------------
CPYMSG:	db	'ACOPY',0	;error message for UTIL

;***Subroutine*********************************
;Change file type on a file to TEMPFT
;On Entry:
;    de points to 0-terminated file name string
;    h = file type
;trashes psw
;**********************************************
CHGTFT:	mvi	h,TEMPFT	;Temp file type

;Fall into CHGFT

;***Subroutine*********************************
;Change file type on a file
;On Entry:
;    de points to 0-terminated file name string
;    h = file type
;trashes psw
;**********************************************
CHNGFT:	call	SYS
	db	CHTOP		;Change file type
	call	FIXERR		;error-return point

	ret			;successful exit

;***Subroutine**************************************
;Check file protection attribute
;and clear it if set.
;On Entry:
;   a = protection attribute bit mask
;  hl = points to portection attributes
;  PFLAG=FF if protection override is allowed
;On Exit
;  Z clear if override is not allowed and bit is set
;On Exit if protection attribute bit was 1
;and override is allowed:
;  Protection bit is 0
;  PAFLAG = 1 
;trashes psw,b
;***************************************************   
CHKFPA:	ana	m
	rz			;no changes needed

	xra	m		;turn bit(s) off
	mov	b,a		;temp save

	lda	PFLAG
	inr	a		;override allowed?
	rnz

	mov	m,b
	mvi	a,1		;set PAFLAG to 1 so that
	sta	PAFLAG		;..prot. attributes will change

	ret			;Z set

;***Subroutine*********************************
;Change protection attributes on a file
;On Entry:
;    de points to 0-terminated file name string
;    h = protection attributes
;trashes psw
;**********************************************
CHNGPA:	call	SYS
	db	CHAOP		;Change prot. attributes
	call	FIXERR		;error-return point

	ret			;successful exit

;***Subroutine******************
;Get source file info
;On Exit:
;  source file info is in INFOBF
;  de = SRCFIL
;  hl = INFOBF
;Trashes psw
;*******************************
GSINFO:	lxi	d,SRCFIL
	lxi	h,INFOBF	;place to put file info

	call	SYS
	db	INFOP		;get info
	call	FIXERR		;source error return point

	ret

;***Subroutine****************
;Get destination file info
;On Exit:
;  de = DSTFIL
;  hl = DINFBF
;On Exit, file exists:
;  b=FF
;  Z cleared
;  dest file info is in DINFBF
;On Exit, file does not exist:
;  b = 0
;  Z set
;Trashes hl
;*****************************
GDINFO:	lxi	h,DINFBF	;place to put file info
	lxi	d,DSTFIL

	mvi	b,0		;so far, we think it exists
	call	SYS
	db	INFOP		;get info
	call	DINFER		;source error return point

	dcr	b		;b=FF or 0
	ret

;---Local Subroutine---------------------------
;Error getting destination file info.
;If the file does not exist, it's ok
;On Entry:
;  a = error code from SYS
;On Exit of the error is nonexistant file:
;  b=b+1
;Other errors abort
;Trashes psw
;----------------------------------------------
DINFER:	inr	b
	cpi	ERNEX		;Nonexistant file error?
	rz			;y: c=0 now
	jmp	FIXERR

;***Subroutine*************************************
;Print CR, LF, then inline string
;The call to ILPRNT is followed by an ASCII string.
;The last string byte has its msb set.
;Trashes psw,b
;**************************************************
CILPRT:	call	ILPRNT
	db	CR+80h

;Fall into ILPRNT

;***Subroutine*************************************
;Print inline string
;The call to ILPRNT is followed by an ASCII string.
;The last string byte has its msb set.
;Note that CONOUT follows a CR with an LF.
;On Exit:
;  a<>0
;Trashes psw,b
;**************************************************
ILPRNT:	xthl			;Save hl, get msg addr

IPLOOP:	mov	a,m
	ani	7Fh		;strip end marker
	call	CONOUT		;print byte (trashes b)
	mov	a,m		;end?

	inx	h		;Next byte
	ora	a		;msb set?
	jp	IPLOOP		;Do all bytes of msg

	xthl			;Restore hl,
				;..get return address
	ret

;=========================
;= Initialized Variables =
;=========================
UNZAP:	dw	0	;PTDOS ZAP address (0 means not ZAPped)
TARFLG:	db	2	;prevent infinite loops restoring source
CFCNT:	db	0	;count of copied files

PAFLAG:	db	0	;1: source attrib's need to be changed
			;2: source attributes have been changed
SPATTR:	db	0	;Source file original prot. atr's
			;(must follow PAFLAG)

FTFLAG:	db	0	;1: source type needs to be changed
			;2: source file type has been changed
SFTYPE:	db	DFLTYP	;Source file original type
			;(must follow FTFLAG)
CTYPE:	db	DFLTYP	;specific file type to copy
			;(must follow SFTYPE)
BLKSIZ:	dw	DBSIZE	;destination file block size
DPATTR:	db	DDATTR	;dest file protection attributes

WFLAG:	db	0	;FF=wildcards specified, 0 otherwise
DWFLAG:	db	0	;FF means dest name not explicit
MXIBLK:	dw	DMIBLK	;max block size

;Option flags (need to be in order:T,B,D,S,S,P)

OFLAGS:
TFLAG:	db	0	;FF to copy only a specific file type
BFLAG:	db	0FFh	;FF to optimize block size, 0 not to
DFLAG:	db	0FFh	;0 to copy to a driver, rather than use it
			;later, FF means use dest as a device driver
SFLAG:	db	0FFh	;0 to copy a driver, rather than use it
			;later, FF means use source as a driver
IFLAG:	db	0	;FF copies information-protected files
PFLAG:	db	0	;FF to ignore protections
;==========================================================
;The following code (and TMPBUF) get overwritten by buffers
;==========================================================

ICODE	equ	$
;==================================================
;Initialization Code. This code gets overwritten by
;data once the command line parsing is complete.
;==================================================

;***Init Subroutine************************
;Initialization
;On Exit:
; The command line has been scanned and
;    all variables set accordingly
; The source disk is the PTDOS default disk
; SYSGLO parameters that we will change
;    have been saved and then changed
;******************************************
INIT:
;------------------------------
;Remember some PTDOS parameters
;------------------------------

;Lookup and remember default unit as ASCII

	lhld	SYSGLO		;PTDOS's System Global area
	lxi	d,GLUNI		;look up default unit
	dad	d
	mov	a,m
	adi	'0'		;make it ASCII
	sta	DEFUNT

;Look up and remember the number of supported drives

	lxi	d,GLMXU-GLUNI	;max number of units
	dad	d
	mov	a,m
	adi	'0'-1		;Max allowed unit in ASCII
	sta	MAXUNT

;------------------------------------------------------
;Scan command line, extract and validate the file names
;------------------------------------------------------
;Get mandatory source file name

	lxi	d,SRCFIL	;file name goes here
	push	d
	mvi	b,0		;getting the source filename
	call	GETNAM		;Z set if no name & no unit
	pop	h		;SRCFIL (for FNERR too)
	jc	FNERR		;bogus?

	sta	SRCUNT		;remember ASCII source unit
	sta	DIRUNT		;remember for directory too
	jnz	INIT1		;Any source specified?

;---Init Exit-------------------------------------------
;No input parameters, so just print help screen and quit
;-------------------------------------------------------
HLPEXT:	call	CILPRT

;    123456789012345678901234567890123456789012345678901234567890123
 db CR
 db 'Advanced Copy version '
 db ((VERSION and 0F00h)/256)+'0','.'
 db ((VERSION and 0F0h)/16)+'0',(VERSION and 0Fh) +'0'
 db ' by M. Eberhard'
 db CR
 db 'ACOPY src{/u1}{,{dst}{/u2}}{,B=h}{,T=typ}){,S={D}{-I}{-P}{S}}'
 db CR,CR
 db '< and > are src wildcards. No wildcards with device drivers.'
 db CR
 db 'B=0 stops block size tuning, B=hhh sets max block size'
 db CR
 db 'T=typ copies typ files, sets dst type if src driver'
 db CR
 db 'S='
 db CR
 db '  D writes dst driver instead of using it'
 db CR
 db ' -I copies info-protected files'
 db CR
 db ' -P ignores protections'
 db CR
 db '  S copies src driver instead of using i','t'+80h

	call	SRESET		;Reset-Return to PTDOS

;--------------------------------------------------------------
INIT1:

;copy source file name (without unit) to wildcard
;source name buffer,in case source filename has a wildcard
;hl=SRCFIL

	lxi	d,WSFIL-1	;Wildcard source file buffer

WCLOOP:	inx	d
	mov	a,m
	stax	d
	inx	h
	xri	'/'		;end of name?
	jnz	WCLOOP

	stax	d		;0-termination where the / was

;Get the destination file name and/or unit

	lxi	d,DSTFIL	;file name goes here
	push	d
	mvi	b,0FFh		;Getting a destination
	call	GETNAM		;carry if bogus
	pop	h		;DSTFIL (or FNERR too)
	jc	FNERR		;bogus?

	sta	DSTUNT		;remember ASCII dest unit

;-------------------
;Scan options: T,B,S
;-------------------
OPTLUP:	lxi	d,TMPBUF	;put option here
	call	GETPAR		;get then next item
	jc	CMDERR		;bogus parameter?
	jz	OPTDON		;done with options?

	xchg			;hl=TMPBUF
	mov	b,m		;b=option

	inx	h		;must be '='
	mov	a,m
	cpi	'='
	jnz	CMDERR

	inx	h		;m=1st option chr

	lxi	d,OPTLUP	;Create return address
	push	d

;b = option
;c = byte count
;m = 1st option chr

	dcr	c
	dcr	c		;account for 'n='
	jz	CMDERR		;must be something after '='

	lxi	d,OFLAGS	;option flags pointer

	mov	a,b
	cpi	'T'		;Specify the file type
	jz	TOPT	

	inx	d		;BFLAG
	cpi	'B'		;Block size optimization
	jz	BOPT

	cpi	'S'		;S options

	jnz	CMDERR

;Loop to decode S options in this order: -D-SIP
;Note: there could be several here.
;c = byte count past the '='
;de points to BFLAG
;m = 1st option chr

SOPLUP:	dcr	c		;any more S options?
	rm			;n: done

	mvi	b,0		;assume true value

	mov	a,m		;look at option chr
	inx	h

	cpi	'+'		;doesn't really do anything
	jz	SOPLP0

	cpi	'-'		;preceding '-'?
	jnz	SOPLP1

	dcr	b		;b=0: active-low

SOPLP0:	dcr	c		;skip over - or +
	mov	a,m		;look at option chr
	inx	h

SOPLP1:	push	d

	push	h
	lxi	h,DSIP

SOPLP2:	inr	m		;test string chr
	dcr	m		;end of options string?
	jz	CMDERR

	inx	d		;next flag address

	cmp	m		;S option match?
	inx	h
	jnz	SOPLP2

	pop	h

	mov	a,b		;new flag value
	stax	d		;save as requested
	
	pop	d
	jmp	SOPLUP

;String for the above search

DSIP:	db	'DSIP',0


;---Local Subroutine----------------------------------
; B option = set max block size
;  B=0 means don't optimize block size
;  B=1 through FF = bogus
;  B=100h through FFFh = set max block size
;  b>FFF = bogus
;On Entry:
;  c = chr count past '='
;  de points to BFLAG
;  hl points to to option string past '='
;On Exit of value<100h:
;  BFLAG=0 if 'B=0'
;  CMDERR if value between 2 and FF
;On Exit if value >FFh:
;  BFLAG=FF
;  MXIBLK=given value
;-----------------------------------------------------
BOPT:	push	d
	mvi	b,3		;up to 3 digits allowed
	call	GETHEX		;get value
	pop	d

;Value must be 0 or >256
	xra	a
	cmp	h		;<256?
	jnz	BOPT1

	cmp	l
	jnz	CMDERR

	stax	d		;clear BFLAG	
	ret

;it's a block size max >255

BOPT1:	shld	MXIBLK		;max block size allowed

	dcr	a		;a=FF: optimize flag
	stax	d		;BFLAG

	ret

;---Local Subroutine---------------------------
;T option = copy only specified file type
;
;Valid file types:
;
;1) 'D' becomes PDTYPE for a device driver
;2) Any other single chr except 'I' becomes
;   that ASCII chr with the msb set
;3) 'I' becomes ASCII '.' (2Eh) with msb cleared
;   (same as 'I2E' or 'I.')
;4) 'Ia' becomes ASCII of a with msb cleared
;5) 'I#h' where h is a 1- or 2-digit hex value < 80h
;   becomes hh (msb cleared)
;6) '#h' where hh is a 1- or 2-digit hex value < 80h
;   becomes hh with msb set
;On Entry:
;  c = chr count after = sign
;  de points to TFLAG
;  hl points to to option string past '='
;Trashes psw,bc,hl
;----------------------------------------------
TOPT:	mvi	a,0FFh		;File type specified
	stax	d

	mov	a,m
	cpi	'D'		;Device driver?
	jnz	TOPT1

;'D' for device driver specified

	dcr	c		;only 1 chr allowed
	jnz	CMDERR

	mvi	a,PDTYPE	;device driver
	jmp	TDONE

;If the 1st chr is 'I' then the file type is an image
;File, which will have its MSB cleared. Otherwise.
;the msb will be set.

TOPT1:	mvi	b,80h		;non-image msb flag
	cpi	'I'		;image type
	jnz	NOTIMG		;1,2, or 6

;'I' for Image file specified. It can be either I (alone),
;Ia-type or I#h-type, whjere a is an ASCII chr and h is a
;1- or 2-digit hex number. The MSB is 0 for image files (only).

	dcr	c		;account for 'I'
	mvi	a,DITYPE	;default image file type
	jz	TDONE		;I (alone)-type? a='I'

	mvi	b,0		;msb flag cleared
	inx	h		;point past 'I'
	mov	a,m

;'A', '#h', 'IA', or 'I#h': The file  type must be either
;a 1-digit ASCII character or a 1- or 2-digit hex number.
;b has its MSB clear for image files, and set otherwise.

NOTIMG:	cpi	' '+1		;1st chr space or
				;..non-printable chr?
	jm	CMDERR		;not allowed

	cpi	'#'		;hex entry?
	jnz	ASCTYP		;n: ASCII type

	dcr	c		;no value?
	jz	ASCHSH		;it's really an ASCII chr


;'#h'or 'I#h': Decode 1- or 2-digit hex. Must be < 80h.

	inx	h		;skip #

	push	b
	mvi	b,2		;max digits
	call	GETHEX		;get 8-bit hex number
	pop	b

	mov	a,l		;look at low byte
	ora	a
	jm	CMDERR		;must be < 80h

	mvi	c,1		;pass next test

;a=file type in binary
;b=0 for image files, 80h otherwise

ASCTYP:	dcr	c		;only 1 chr allowed
	jnz	CMDERR

ASCHSH:	ora	b		;set MSB appropriately

TDONE:	sta	CTYPE
	ret	

;***INIT Routine********************************
;Option scanning is done. Finish verifying input
;***********************************************
OPTDON:
;***************************
;Source file name validation
;***************************
;------------------------------
;Is the source file wildcarded?
;------------------------------
	lda	WFLAG
	inr	a
	jz	SNDRV		;wildcard, so not driver

;--------------------------------------------------
;The source file is not wildcarded, so get its info
;--------------------------------------------------
	call	GSINFO

;----------------------------------------------------
;Adjust SFLAG to mean that the source is a driver AND
;we will use it as a driver. If so, then we will use
;the default block size and protection attributes.
;-----------------------------------------------------
	lda	IFTYPE		;source file's file type
	lxi	h,SFLAG		;FF means use a driver as
				;..a driver
	ana	m		;driver as driver?
	inr	a		;=FFh?
	lda	IPATTR		;source file's attr's
	sta	SPATTR		;remember prot. attributes
	jz	SRCOK		;y: leave dest prot. attr's alone

	sta	DPATTR		;dest gets this file's attr's

SNDRV:	xra	a		;n: treat a driver like a file	
	sta	SFLAG

;a=protection attributes if SFLAG is FF

SRCOK:	ani	PREA		;read-protected device driver?
	jnz	RPDERR		;can't do that

;********************************
;Destination file name validation
;********************************
	lxi	d,SRCFIL
	lxi	h,DSTFIL

;--------------------------------------
;Is the destination file name explicit?
;--------------------------------------
	mov	a,m		;1st chr of dest file name
	sui	'/'		;actually just unit?
	jnz	DISF

	dcr	a		;FF: dest name not explicit
	sta	DWFLAG

;---------------------------------------
;The destination is just a unit, so
;the source mustn't be used as a driver.
;---------------------------------------
	lda	SFLAG		;source a driver?
	ora	a
	jnz	OVWERR

;----------------------------------------------------
;If the source is wildcarded then we're done checking
;----------------------------------------------------
	lda	WFLAG
	ora	a
	jnz	DNDRV		;can't possibly be a driver

;---------------------------------------
;Explicit source, undefined destination.
;Copy the source filename into the
;destination filename and tack on DSTUNT
;de=SRCFIL
;hl=DSTFIL
;---------------------------------------
	lda	DSTUNT
	call	COPYFN		;(de)==>(hl)

	jmp	FNCMP		;check for overwrite error

;----------------------------------
;The destination file is explicit.
;Error if the source is wildcarded.
;----------------------------------
DISF:	lda	WFLAG		;source wildcard?
	ora	a
	jnz	OVWERR		;y: error

;-------------------------------------
;The source and destination (including
;the units) must not be the same.
;de=SRCFIL
;hl=DSTFIL
;-------------------------------------
FNCMP:	ldax	d

	ora	a		;end of strings?
	jz	OVWERR		;y: they match

	cmp	m
	inx	h
	inx	d
	jz	FNCMP

;---------------------------------------
;Get the destination info (if it exists)
;---------------------------------------
	call	GDINFO
	jz	DNDRV		;dest file does not exist yet.

;----------------------------------
;Adjust DFLAG to 0 if destination
;file is not a device driver, and
;check for a write-protected driver
;----------------------------------
	lda	DFTYPE		;FF if dest is a driver
	inr	a
	lda	DIPTTR		;destination protection attrs
	jz	DSTOK

DNDRV:	xra	a
	sta	DFLAG

;a=protection attributes if DTYPE is FF

DSTOK:	ani	PWRI		;Write-protected device driver?
	lxi	h,DFLAG
	ana	m		;..being used as a driver?
	jnz	WPDERR		;can't do that

;***********
;PTDOS Setup
;***********
;------------------------------------------------
;Test to see if PTDOS is locked, and abort if so.
;------------------------------------------------
	lhld	SYSGLO		;PTDOS's System Global area	
	lxi	d,GLLOK 	;Point to lock
	dad	d
	mov	a,m
	ora	a		;nz means locked

	mvi	a,ERLOK		;system write-locked error
	mvi	h,0FFh		;put 0FFh in place for PTDER
	cnz	PTDER		;let PTDOS explain error

;----------------------------------------------
;Remember SYSGLO parameters that we may change,
;so we can restore them when done, and change
;the ones we need to change.
;----------------------------------------------
;PTDOS's memory limit (GLLOW)
;Give PTDOS lots of RAM so that it can buffer 2 files that
;each has a 4K (FFFh) block size
	lhld	SYSGLO		;PTDOS's System Global area

	lxi	d,GLLOW
	dad	d
	shld	GLOADR		;remember address of GLLOW

	mov	e,m
	mvi	m,FGLLOW and 0FFh
	inx	h
	mov	d,m
	mvi	m,FGLLOW/256	;GLLOW = FGLLOW
	xchg
	shld	GLOSAV

	xchg

;Tell PTDOS to send level 1 and level 2 errors back to us,
;and to send level 0 errors to PTDOS. Do this before any
;SYS calls.

	lxi	d,GLERS-(GLLOW+1)
	dad	d		;hl=GLERS address

	lxi	b,4*256+0FFh	;4 bytes for 2 16-bit -1's

SERLUP:	mov	m,c		;(page 6-12)
	inx	h
	dcr	b
	jnz	SERLUP

	mov	m,b		;level 0 errors go to PTDOS
	inx	h
	mov	m,b

;--------------------------------------------
;Open the source disk's directory
;if wildcards in source filename
;(The source unit has already been installed)
;--------------------------------------------
	lda	WFLAG
	ora	a

	lxi	d,DIRNAM
	cnz	FOPEN
	sta	DIRFN		;remember directory file number

;-------------------------------
;See if PTDOS needs to be ZAPped
;-------------------------------
	lda	PFLAG		;override protections?
	ora	a
	rz			;n: done with INIT

;------------------------------------------------
;ZAP PTDOS so we can change protection attributes
;------------------------------------------------
	lxi	h,PATR15	;Attribute test for PTDOS 1.5
	mov	a,m
	sui	PATR		;This is the "A" attribute bit.
	jz	P15		;maybe it's 1.4 DOS

	lxi	h,PATR14	;Atrtribute test for PTDOS 1.4
	mov	a,m
	sui	PATR		;This is the "A" attribute bit.
	jnz	VRSERR		;Unrecognized PTDOS: error exit

P15:	shld	UNZAP		;remember where we zapped
	mov	m,a		;ZAP now

	ret			;done with INIT

;***Init Subroutine******************************************
;Get and validate a file name, add default unit
;if no unit provided
;Valid characters for source & dest:
;  21h - 7Fh excluding {bang,/;<=>} (page 5-2)
;On Entry:
;  de=points 20-byte buffer (for PSCAN)
;  b=0 if getting the source filename
;  b=FF if getting the destination file name
;On Exit:
;  a=unit in ASCII
;  filename with unit is in provided buffer
;On Exit if b=0 (source):
;  carry set if illegal character or too many chrs
;  Z set if no charcters
;  a=unit, unless Z
;  WFLAG=FF if any wildcards or / was 1st chr
;On Exit if b=FF (dest)
;  carry set if illegal character, too many chrs, or wildcard
;Trashes bc,e,hl
;************************************************************
GETNAM:	call	GETPAR		;get name and put it at de
				;returns a=c=chr count
	rc			;too many chrs?

	cmp	b		;report no chrs? (b=0 or FF)
	rz			;no chrs?

	xchg			;hl points to string
	mvi	e,1		;in case no chrs at all

	ora	a		;no characters?
	jz	ADUNIT		;it's the default unit

	dcr	e		;e counts characters

;scan file name to validate its length, filename, and wildcards.
;(PTDOS will catch bogus filename characters like = and bang.)

FNSCAN:	mov	a,m
	xri	'<'		;illegal except as 1st chr
				;(clears carry)
	cz	IWILDC		;returns carry if not 1st chr
	rc			;error?

	mov	a,m
	xri	'>'		;(clears carry)
	cz	WILDC
	rc			;error?

	mov	a,m
	inx	h		;point to next chr
	cpi	'/'		;unit specifier?
	jz	GUNIT

	inr	e		;bump chr count

	mvi	a,PNMAX+1	;max 8 chrs for file name
	cmp	e
	rc			;error if too many chrs

	dcr	c		;end of string?
	jnz	FNSCAN		;n: next chr

;End found without a unit number. Install default unit number
;and return with Z clear. (A filename was definitely provided) 
;c=0 here.

ADUNIT:	mvi	m,'/'
	inx	h
	lda	DEFUNT		;ASCII default unit
	mov	m,a		;install unit number

	jmp	GNDONE

;'/' found. Get and validate the unit, install terminating null
;de points to next chr past '/'

GUNIT:	lda	MAXUNT		;highest allowed unit (ASCII)
	cmp	m
	rc			;error: bogus unit

	dcr	c		;no more chrs allowed
	dcr	c		;/ and unit
	stc
	rnz			;error: too many chrs

;if B=0 (source) and / was the 1st chr (no filename) then
;set WFLAG to indicate wildcarded filename as source

	dcr	e		;0 means / was 1st chr
	ora	b		;..and getting source filename?
	cz	WILDC3		;y: then set WFLAG

;Get unit number for return

	mov	a,m		;get ASCII unit for ret

;Install terminating null return with Z cleared
;a=unit
;c=0
;hl points to unit at end of string

GNDONE:	inx	h		;install null termination
	mov	m,c		;c=0

	inr	c		;clear Z
	ret

;---Local Subroutine--------------------------
;Found a '<' wildcard in the file name
;On Entry:
;  a=0
;  b=0 if wildcards allowed, FF if not
;  e=character number-1
; hl points to the next chr
;On Exit, wildcards not allowed
;  Carry set
;On Exit, this is not the 1st character:
;  Carry set
;On Exit, this is the 1st chr of the filename
;and wildcards are allowed:
;  Carry clear
;  a=FF
;  WFLAG=FF
;---------------------------------------------  
IWILDC:	cmp	e		;'<' may only be 1st chr
	rc

	jmp	WILDC2		;recycle some code

;---Local Subroutine------------------
;Found a wildcard in the file name
;On Entry:
; hl points to the next chr
; c=count of remaining characters
; b=0 if wildcards allowed, FF if not
;On Exit:
;  carry set if the isn't the last chr
;  carry set if wildcards not allowed
;  a=FF
;  WFLAG=FF
;-------------------------------------  
WILDC:	inx	h
	mov	a,m		;next chr better be the end
	cpi	'/'		;unit specifier?
	jz	WILDC1

	mov	a,c		;must be the last chr
	dcr	a
	stc
	rnz			;carry set if not	

WILDC1:	dcx	h

WILDC2:	cmp	b		;b=0 if wildcards allowed
	rc			;b=FF if not

WILDC3:	mvi	a,0FFh		;remember we saw a wildcard
	sta	WFLAG
	ret	
	
;***Init Subroutine****************************
;Get next command line parameter
;On Entry:
;  de=address of parameter buffer
;  DELIM = previous delimiter
;On Exit:
;  If no more input:
;    Z set
;    a=c=0
;  If good input
;    c=character count
;    Z set
;    String is at de
;    DELIM = this option's delimiter
;  If bogus input
;    carry set
;Trashes a
;**********************************************
GETPAR:	lda	DELIM		;more options?
	cpi	','
	jz	GETP1		;n: done

	xra	a		;set Z
	mov	c,a
	ret

GETP1:	push	b
	push	d
	mvi	a,PSOPT		;Scan one item
	call	PSCAN		;(page 6-10)
	pop	d
				;c=chr count
				;Z set if c=0
				;a=delimiter
				;Carry set if error

	sta	DELIM		;remember delimiter
 	mov	a,c		;chr count
	pop	b
	mov	c,a
	ret			;carry & Z appropriate	

;===INIT Subroutine=========
;Convert hex digit to binary
;On Entry:
;  a=ASCII digit
;On Exit:
;  a = value
;  carry clear if bogus
;===========================
HX2BIN:	sui	'0'		;un-ASCII
	cpi	10
	rc			;0-9?

	sui	9+('A'-'9')	;should now be 0-5
	cpi	6		;gap chr or too big?
	rnc

	sui	100h-0Ah	;sets carry for success
	ret

;***Init Subroutine************************************
;Get hex a 16-bit value from command line
;On Entry:
;   b=max digits
;   c=digit count
;  hl=address of hex in buffer
;On Exit:
;  hl=result, 0 to 0FFFh
;Abort to CMDERR of bad hex, no hex, or too many digits
;Trashes psw,c,de
;******************************************************
GETHEX:	xchg			;pointer to de

	mov	a,c		;too many digits?
	dcr	a		;can't be 0 either
	cmp	b
	jnc	CMDERR

	xra	a
	mov	h,a		;initial value
	mov	l,a

GHLP:	add	l		;combine new digit
	mov	l,a

	dcr	c		;last chr?
	rm			;y: result in hl

	ldax	d		;get a digit
	inx	d

	dad	h		;make room for new digit
	dad	h
	dad	h
	dad	h

	call	HX2BIN		;convert from ASCII
	jc	GHLP		;nc means bogus hex

;Fall into CMDERR	

;***Error Exit********************
;Bogus parameter after filenames
;On Entry:
;  TMPBUF has offending string
;*********************************
CMDERR:	lxi	h,TMPBUF

;Fall into FNERR

;***Error Exit********************
;Bogus file name
;On Entry:
;  hl points to file name string
;*********************************
FNERR:	call	CILPRT
	db	'Huh?',' '+80h
	call	PRINTF

	call	SRESET

;***Error Exit********************
;Destination will overwrite source
;*********************************
OVWERR:	call	CILPRT
	db	'File conflic','t'+80h

	call	SRESET

;***Error Exit****************
;Write-protected device driver
;*****************************
WPDERR:	call	CILPRT
	db	'Writ','e'+80h
	jmp	PDERR

;***Error Exit***************
;Read-protected device driver
;****************************
RPDERR:	call	CILPRT
	db	'Rea','d'+80h

;Fall into PDERR

;***Error Exit*************************
;Read- or write-protected device driver
;**************************************
PDERR:	call	PPROT	;print ' protected'
	call	ILPRNT
	db	' drive','r'+80h

	call	SRESET

;***Error Exit*************
;Unrecognized PTDOS version
;**************************
VRSERR:	call	CILPRT
	db 'ZAP fai','l'+80h

	call	SRESET

;--------------------------
;Directory file name string
;--------------------------
DIRNAM:	db	'DIRECTRY/'
DIRUNT:	db	'X'		;gets replaced
	db	0		;null termination

;===============================
;Initialized variables used only
;during INIT, and get overlayed
;===============================
DELIM:	db	','	;command line delimiter

;===============================================
;End of file. Efficient if this is <=9BAh
;(Not 9C0 because converting it to an executable
;"image" file will add another 6 bytes.)
;===============================================
CODEND	equ	$

;=========================================
;Uninitialized variables used only in INIT
;=========================================
MAXUNT:	ds	1	;Max unit in ASCII, acording to PTDOS

;======================================
;Uninitialized buffer used only in INIT
;======================================
TMPBUF:	ds	PMPARM	;temp for option decoding

;=========================================================
;The following ununitialized variables and data structures
;overwrite the initialization code. Some of these are
;written to during INIT, wiping out the beginning of the
;INIT code (which is OK because that code is done).
;=========================================================
	org	ICODE

;=======================================
;= Uninitialized Variables and Buffers =
;= NOTE: The order of these matters,   =
;= because as INIT writes to them,     =
;= INIT code will get overwritten      =
;= (particularly DEFUNT and SRCFIL).   =
;=======================================
DEFUNT:	ds	1	;default unit, ASCII

SRCFIL:	ds	PMPARM	;source file name buffer
DSTFIL:	ds	PMPARM	;destination file name buffer
WSFIL:	ds	PNMAX+1	;wildcard source name buffer

SRCUNT:	ds	1	;source unit, ASCII
DSTUNT:	ds	1	;dest unit, ASCII (must follow SRCUNT)
GLOADR:	ds	2	;address of GLLOW
GLOSAV:	ds	2	;original GLLOW value
SRCFN:	ds	1	;Source file number
DSTFN:	ds	1	;Destination file number
DIRFN:	ds	1	;Directory file number
BIGFLG:	ds	1	;count of buffers-full
SRCSIZ:	ds	2	;Source file size (valid if <CBSIZE)

;===Uninitialized Data Structure===
;Buffer for reading File Info
;==================================
INFOBF:
	
IFILID:	ds	2	;File ID
IINDBS:	ds	1	;Index block sector
IINDBT:	ds	1	;Index block track
IFBS:	ds	1	;First block sector
IFBT:	ds	1	;First block track
IFBCNT:	ds	2	;File block count
IRESV1:	ds	1	;reserved
IOFLAG:	ds	1	;Open flag
IRESV2:	ds	1	;reserved
IFTYPE:	ds	1	;File type
IBSIZE:	ds	2	;Block size
IPATTR:	ds	1	;Protection attributes
IFNAME:	ds	8	;File name
IZERO:	ds	1	;always zero

	ds	2	;leave room to tack on "/n"

;***Buffer******************************************
;Buffer for searching the directory
;***************************************************
DIRBUF:	ds	PDBSIZ	;one directory block

;***Buffer******************************************
;Big buffer for copying file data
;***************************************************
CPYBUF:	equ	$
CBSIZE	equ	FGLLOW-STKSIZ-VBSIZE-CPYBUF

;***Buffer******************************************
;Small buffer for verifying file data
;***************************************************
VFYBUF:	equ	CPYBUF+CBSIZE

;***Buffer******************************************
;Temp buffer for getting dest file info
;(overlays VFYBUF)
;***************************************************
DINFBF:	equ	VFYBUF
DFILID:	equ	DINFBF		;File ID
DINDBS:	equ	DFILID+2	;Index block sector
DINDBT:	equ	DINDBS+1	;Index block track
DFBS:	equ	DINDBT+1	;First block sector
DFBT:	equ	DFBS+1		;First block track
DFBCNT:	equ	DFBT+1		;File block count
DRESV1:	equ	DFBCNT+2	;reserved
DOFLAG:	equ	DRESV1+1	;Open flag
DRESV2:	equ	DOFLAG+1	;reserved
DFTYPE:	equ	DRESV2+1	;File type
DIBSIZ:	equ	DFTYPE+1	;Block size
DIPTTR:	equ	DIBSIZ+2	;Protection attributes
DFNAME:	equ	DIPTTR+1	;File name

	end
