;               ARCHIVE - File Archive/Backup Utility
;               Version 1.0, 6-June-82 by Kelly Smith
; 
; 
;      ARCHIVE  is  a  user oriented disk maintenance  utility  for 
; archival  storage and file backup.   User facilities include  the 
; ability to selectively 'tag' files for archival (or non-archival) 
; attributes,  display  file  archive attributes,  or  backup  non-
; archive files to a selected disk.  Wild-card filenames (using '*' 
; and/or  '?')  may be used freely,  to select some or  all  (i.e., 
; '*.*') files for archiving.
; 
; 
;                      - Theory of Operation -
; 
;      Digital  Research's  MP/M-II and CP/M-86  Operating  Systems 
; utilize a file attribute for archival backup of any file addition 
; or update to the disk directory.   This attribute is 'reset' when 
; an  application  program is required to write a file to  a  disk.  
; The  actual 'mechanism' for this archive function is realized  by 
; controlling  the  most significant bit of the third character  of 
; the FCB (File Control Block) directory entry (i.e.,  position 11, 
; identified  as byte position T3,  with the Archive attribute  bit 
; (bit  7  of byte T3) identified as T3').   It is  then  a  simple 
; matter to identify a file as archived (e.g.,  backed-up) when T3' 
; is a 'one',  and non-archived (e.g.,  needs to be backed-up) when 
; T3'  is  a  'zero'.   The 'not-so-simple' part,  is  the  utility 
; required   to  keep  track  of  the  archive  attributes   on   a 
; disk...thats where ARCHIVE comes in, and works with CP/M-80 (with 
; the help of a little 'patch'),  as well as with MP/M-II and CP/M-
; 86 (when translated for 8086 CPU operation).    
; 
; 
;                       - Examples of Usage -
; 
;      At  the CP/M user command prompt,  (usually 'A>'),  the user 
; has the option for four single letter ARCHIVE command directives, 
; as follows:
; 
;                S - Set file archive attribute.
; 
;                R - Reset file archive attribute.
; 
;                D - Display file arcive attribute.
; 
;                B - Backup non-archived files.
; 
;      ARCHIVE  is  invoked by the user at the CP/M  command  level 
; therefore, as follows,
; 
;                    A>ARCHIVE FN.FT OPTION<cr>
; 
; ...where,  'FN.FT' is an amibiguous or un-ambiguous filename  and 
; filetype,  followed  by one of the single character options,  and 
; then a keyboard RETURN entry.  An actual example then might be:
; 
;                       A>archive b:*.* b<cr>
; 
; ...then,  we  are executing ARCHIVE from the 'A' disk drive,  for 
; all  filenames and filetypes on disk 'B' whose archive  attribute 
; has   been  'reset',   to  be  'backed-up'  to  some   (as   yet) 
; indeterminate destination disk.
; 
;      Options  'S',  'R' and 'D' (Set,  Reset,  and Display)  when 
; invoked,  will  cause each file found in the directory 'match' to 
; be  displayed by 'multiple extension'...that is,  each  directory 
; entry will be displayed,  with its 'extent' number,  followed  by 
; the archive attribute action/status as 'S' or 'R' (Set or Reset).
; 
;      Option  'B'  (Backup)  will  request the  user  to  enter  a 
; destination disk letter (A to P), then proceed to scan the source 
; disk  directory for 'reset' archive file  entries.   Any  'reset' 
; entries  will  then be 'set' (indicating that the file  has  been 
; archived)  and then these files will be copied to the destination 
; disk  (Note  however,  that  these  files  will  not  retain  any 
; previously  set  attributes of 'Read Only' or 'System'  had  they 
; been  set...all  files  copied to the destination  disk  will  be 
; 'tagged'  as archived only).   If a disk becomes full,  the  user 
; will  be requested to remove the disk from the destination  drive 
; and  insert another for completion of the  backup  process.   Any 
; file  that has not been completely copied to the destination disk 
; at the time that disk is detected as 'full', will be deleted from 
; that disk,  and the file copy will resume intact on the next disk 
; installed in the destination drive.
; 
;      Users of MP/M-II can use ARCHIVE immediately, with no system 
; modification  required  because it "knows" about file  archiving.  
; However,  for  CP/M-80 users,  they must patch there system  BDOS 
; (Basic Disk Operating System) to "teach" CP/M-80 file  archiving.  
; The following code must be edited and assembled,  then loaded (as 
; a  '.HEX'  file)  with DDT (Dynamic Debugging  Tool)  using  your 
; normal  system  installation  procedure for  merging  your  'Boot 
; Loader'  and  BIOS (Basic Input/Output System) into  your  system 
; "image"  for eventual SYSGEN'ing (this code assumes the  standard 
; SYSGEN position of address 980H for the start of the CCP (Console 
; Command   Processor,   with   a  CP/M-80  system  'size'  of   56 
; Kilobytes...change the value at label MSIZE to suit your needs).
; 
; 
;                           *+*+*+*+*+*+*
;                           *+         +*
;                           *+ WARNING +*
;                           *+         +*
;                           *+*+*+*+*+*+*
; 
;      You  must incorporate this 'archive patch' in an  unmodified 
; CP/M-80   (version   2.2)   BDOS   O-N-L-Y...any   other    prior 
; modifications  will (no doubt) be overlayed by this one,  causing 
; unpredictable results...'nuf said.
;                                              - KS -
;
;
;
; msize		equ	56		; size of this CP/M 2.2 system
; ;
; bdos$loc	equ	(msize-20)*1024+3c00h	; base address of BDOS
; wrsec		equ	bdos$loc+03b8h	; address of write sector routine
; pntdir	equ	bdos$loc+055eh	; address of directory pointer routine
; reset$archive	equ	bdos$loc+0df0h	; address of reset archive bit patch
; ;
; ccp$base	equ	0980h		; sysgen ccp base position
; bdos$entry	equ	ccp$base+0806h	; sysgen bdos entry position
; wrt$dir	equ	bdos$entry+05c8h; 'wrsec' call location, in DIR write routine
; scratch	equ	bdos$entry+0deah; scratch RAM inside bdos for patch
; 
; 	org	wrt$dir			; patch 'call wrsec'
; ;
; 	call	reset$archive		; call patch in scratch RAM
; ;
; 	org	scratch			; patch area for 'archive bit reset'
; ;
; 	call	pntdir			; point to directory entry in buffer
; 	lxi	d,11			; make offset to 't3' in FCB
; 	dad	d
; 	mov	a,m			; get 't3' character from FCB
; 	ani	07fh			; kill archive bit position
; 	mov	m,a			; return reset archive bit to FCB
; 	call	wrsec			; write directory sector
; 	ret				; return to 'wrt$dir' routine
; ;
; ;
; ;
; 	end
;
;
;
; *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
; *+                                                                     +*
; *+  ARCHIVE.ASM Utility Version 1.0,  as of 6-June-82, by Kelly Smith  +*
; *+                                                                     +*
; *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
;
;
; 
true	equ	-1		; define true
false	equ	not true	; define false
;
mpm	equ	not true	; conditional assembly for MP/M
;
bdos	equ	5		; CP/M entry point
exit	equ	0		; CP/M exit point
dfcb	equ	5ch		; CP/M default fcb
fcbext	equ	dfcb+12		; fcb extent byte
fcbrno	equ	dfcb+32		; fcb record number byte
dbuff	equ	80h		; default disk buffer
; 
coninp	equ	1		; console character input function
pchar	equ	2		; print character function
dircon	equ	6		; direct console i/o function
pmessg	equ	9		; print message function
constat	equ	11		; console status function
version	equ	12		; return version number function
rsetdsk	equ	13		; reset disk system function
seldsk	equ	14		; select drive function
open	equ	15		; open file function
close	equ	16		; close file function
srchfst	equ	17		; search for first file match function
srchnxt	equ	18		; search for next file match function
delete	equ	19		; delete file function
read	equ	20		; read record function
write	equ	21		; write record function
make	equ	22		; make file function
currdsk	equ	25		; return current logged disk function
stdma	equ	26		; set dma address function
attrib	equ	30		; set file attributes function
; 
tab	equ	09h		; tab character
lf	equ	0ah		; line feed character
cr	equ	0dh		; carriage return character
; 

	org	100h

;
; jump over authorship notice
;
	jmp	over
;
	db	cr,lf,'ARCHIVE Ver.1.0, 6-Jun-82:  Kelly Smith'
	db	cr,lf,'3055 Waco Avenue, Simi Valley, CA 93063'
	db	cr,lf,'Z'-40h	; force end-of-file for display
;
; make a new stack pointer at CP/M serial number
;
over:	lhld	bdos+1		; set up a stack
	sphl			; at top of tpa
;
; get curent logged disk, and save it
;
	mvi	c,currdsk	; return curent disk
	call	bdos
	sta	logdsk		; save as logged disk
;
; reset disk system in case someone swapped in new disk on the system
;
	mvi	c,rsetdsk	; reset disk system function
	call	bdos
;
; now return to original login disk
;
	lda	logdsk		; get logged in disk number
	mov	e,a		; do disk select
	mvi	c,seldsk
	call	bdos	
;
; announcing, the new and improved ARCHIVE...it beats as it sweeps, as it cleans
; 
	lxi	d,signon	; point to signon message
	mvi	c,pmessg	; print string fuction
	call	bdos		; print it
;
; check for proper environment, we only live for CP/M 2.2
;
	mvi	c,version	; check version number, must be 2.2
	call	bdos
	cpi	022h		; version number correct?
	lxi	d,bad$ver$num	; set-up bad version message, in case its not
	jnz	arcexit		; bail-out now, if wrong version number
;
	if	not mpm		; CP/M-80 does not know about archiving...
	lhld	bdos+1		; see if archiving installed in CP/M
	lxi	d,05c9h		; add offset to archive patch address
	dad	d
	shld	arch$addr	; save address for later
	mov	a,m		; get low byte content
	cpi	0b8h		; is this address for write sector routine?
	jnz	patch$bdos	; if not, patch it back into bdos
	lxi	d,noarch	; tell user that archive is not installed
	jmp	arcexit		; exit via CP/M warm boot
;
patch$bdos:
;
	lhld	bdos+1		; patch write sector address back to bdos
	lxi	d,03b2h		; add offset address of 'wrsec'
	dad	d
	xchg			; address patch info to [DE]
	lhld	arch$addr	; get address to patch in [HL]
	mov	m,e		; low byte address patched first
	inx	h		; bump pointer
	mov	m,d		; high byte address patched second
	endif			; endif MP/M
;
; check if filename specified, abort if not
;
	lda	dfcb+1		; this will be a space, if no filename
	cpi	' '
	jnz	gotname		; if not space, probably filename
	lxi	d,no$file$nam	; inform user of error of their way...
	mvi	c,pmessg
	call	bdos
	lxi	d,opts		; tell'em about backup options also
	jmp	arcexit		; bail-out now, no filename
;
; got a name, now check to see which (if any) disk specified
; 
gotname:lda	dfcb		; check for specific drive
	dcr	a
	mov	e,a		; set up for select disk call
	mvi	c,seldsk
	inr	a		; if no specified drive, skip call
	sta	src$dsk$num	; save drive specifier for later check
	cnz	bdos
	xra	a		; now zap out drive specifier
	sta	dfcb
	mvi	a,'?'		; force extent number wild
	sta	dfcb+12
	lda	dfcb+17		; get "B", "S", "R" or "D" option
	sta	option
	cpi	'B'		; backup operation?
	jz	okopt
	cpi	'S'		; set archive operation?
	jz	okopt
	cpi	'R'		; reset archive operation?
	jz	okopt
	cpi	'D'		; just display?
	jz	okopt
; 
badopt:	lxi	d,ilgopt	; let'em know it was an illegal option
arcexit:mvi	c,pmessg	; bitch, bitch, bitch... 
	call	bdos
	jmp	exit
; 
; findout now, if operation is for backup or general lobotomy
;
okopt:	lda	option		; get option specifier
	cpi	'B'
	jnz	arc$s$r		; if not backup, must be set/reset archive
dest:	lxi	d,req$dest	; request destination disk for backup files
	mvi	c,pmessg
	call	bdos
	mvi	c,coninp	; get console input character
	call	bdos
	ani	05fh		; force upper case
	sbi	'A'		; force binary digit
	cpi	16		; disk >P?
	jnc	dest		; must satisfy ourselves with 16 disks maximum
	lxi	h,logdsk	; is this jerk backing up to the same disk?
	ani	0fh		; strip high nibble for match to logged disk
	cmp	m
	push	psw		; save flags
	inr	a		; save re-adjusted disk number
	sta	dest$dsk$num
	pop	psw		; get flags
	jz	dsksame		; if same, tell'em so now
	inx	h		; bump pointer to destination disk
	mov	a,m		; get disk number
	inx	h		; bump pointer to source disk number
	cmp	m		; any chance he specified destination as source
	jnz	backup		; if not, we can (finally) proceed with backup
dsksame:lxi	d,same$dsk	; sob...(read this anyway you want)
	mvi	c,pmessg
	call	bdos
	jmp	dest		; computers never lose their patience...
;
; ready for directory write operations now...do archive set/reset
;
arc$s$r:call	crlf		; tidy up display
	xra	a		; zero out file count
	sta	filcnt
	lxi	d,dfcb		; find the first file and get its block map
	mvi	c,srchfst
	call	bdos
	inr	a		; search successful?
	jnz	gotfile		; yes, go process rest
	lxi	d,no$file$fnd	; oops, no file found
	jmp	arcexit		; exit via CP/M warm boot
; 
gotfile:dcr	a		; compensate for 'inr' above
	rrc			; file offset to bits 5 and 6
	rrc
	rrc
	ani	60h
	lxi	h,dbuff		; point to base of buffer
	mov	c,a
	mvi	b,0
	dad	b		; index by file offset
	push	h		; save directory entry, for the moment
	lxi	b,filetable	; point to base of file table
	call	filepoint	; get table pointer to [HL]
	xchg			; [DE] now points to place in table
	lda	filcnt		; keep track of number of files in table
	inr	a
	sta	filcnt		; bump file count
	pop	h		; [HL] points to directory entry
	mvi	b,32
	call	blkmov		; copy entry into table
getnext:mvi	c,srchnxt	; search for another entry
	lxi	d,dfcb
	call	bdos
	inr	a		; returns 0ffh at end of search
	jnz	gotfile		; got another one, go save it
; 
;  end of directory encountered, now process them
; 
tagfile:call	abort		; check for user abort of process
	lxi	b,filetable-32	; allow for file count one greater than desired
	call	filepoint
	push	h
	lxi	d,dfcb		; copy next name to default fcb
	mvi	b,32
	call	blkmov		; someday I will grow up to be a Z80...
	xra	a
	sta	dfcb		; clear drive number
	lxi	d,-20		; point back to extent field
	dad	d
	mvi	m,'$'		; tag end of print here
	pop	d		; get back pointer to start of entry
	inx	d		; bump forward to name
	mvi	c,pmessg
	call	bdos		; say what we're working on
	mvi	e,' '		; make space between filename.typ and extent
	mvi	c,pchar
	call	bdos
	lda	dfcb+12		; get extent number
	push	psw		; save it
	adi	'0'		; convert to ascii
	mov	e,a
	mvi	c,pchar		; display extent number
	pop	psw
	call	bdos
	lda	option		; get B, S, R, or D
	cpi	'D'		; display only?
	jz	nextfile
	rrc			; bit 7 = 0 for B, R, and D, 1 for S
	ani	80h
	mov	b,a		; save mask
	lxi	d,dfcb+11	; point to t3
	ldax	d		; get it
	ani	7fh		; strip t3'
	ora	b		; set bit if option was S
	stax	d		; put it back
	lxi	d,dfcb		; point to start of fcb
	xra	a		; zap out drive field
	stax	d
	mvi	c,attrib	; do set attributes function
	call	bdos
; 
nextfile:
; 
	lda	dfcb+11		; get t3
	rlc			; isolate t3'
	ani	1
	adi	'R'		; make an R or S
	sta	donmsg+1
	lxi	d,donmsg
	mvi	c,pmessg	; print completion message for this file
	call	bdos
	call	crlf		; tidy up display
	lxi	h,filcnt	; point to file counter
	dcr	m		; count it down
	jz	exit		; exit if done
	jmp	tagfile		; tag next file
;
; backup routine - does multiple file search and copy
;
backup:	call	crlf		; tidy up display
noback:	call	abort		; check for user abort of process
	call	mfname		; set-up multi-file search (seek and ye shall find?)
	jnc	movname		; file found, if no carry
	lda	mfflg1		; check if anything ever found...
	ora	a
	lxi	d,bakup$done	; set-up backup done message
	jnz	nofile		; if not...indicate, no file found
	lda	got$arc		; check archive found flag
	ora	a
	jnz	arcexit		; if not zero, must have found one (or more)
	lxi	d,narcs		; indicate no archive files found
	jmp	arcexit
nofile:	lxi	d,no$file$fnd	; oops, no file found
	jmp	arcexit		; exit with perturbed message
movname:lxi	h,dfcb+11	; point to t3 in filetype
	mov	a,m		; get t3 byte
	rlc			; archive bit set?
	jc	noback		; if so, no backup required
	rrc			; adjust back to normal
	sta	got$arc		; set archive file found flag with non-zero
	ori	080h		; set 'archived' status back
	mov	m,a		; put it back
	push	h		; save pointer
	lxi	d,dfcb		; point to start of fcb
	mvi	c,attrib	; do set attributes function
	call	bdos
	pop	h		; restore pointer
	dcx	h		; point to t2 in filetype
	mov	a,m		; get t2 byte
	ani	07fh		; strip-off t2'
	mov	m,a		; put it back
	dcx	h		; point to t1 in filetype
	mov	a,m		; get t1 byte
	ani	07fh		; strip-off t1'
	mov	m,a		; put it back
	lxi	h,dfcb+1	; point to filename for move and display
	lxi	d,fname		; display destination for filename
	mvi	b,8		; set-up to move 8 characters in filename
	call	blkmov		; too bad that 8080 is defacto standard
	lxi	h,dfcb+9	; point to filetype for move and display
	lxi	d,fname+9	; display destination for filetype
	mvi	b,3		; set-up to move 3 characters in filetype
	call	blkmov		; LDIR is so nifty...
dspname:lxi	d,fname		; display filename and filetype
	mvi	c,pmessg
	call	bdos
; 
; save first fcb for use later as destination file name
; 
	mvi	b,11	   	; number of characters to move
	lxi	h,dfcb+1	; from default fcb
	lxi	d,destfcb+1 	; to destfcb
	call	blkmov		; round'em up and head'em out
; 
; open the source file
; 
	lda	logdsk		; select disk, in case of disk full status
	mov	e,a
	mvi	c,seldsk	; select disk function
	call	bdos	
	lxi	d,dfcb		; point to source fcb
	mvi	c,open		; attempt open
	call	bdos
	cpi	0ffh		; file not found?
	jnz	openok
	lxi	d,src$open$err	; oops, source file open error
	jmp	arcexit		; exit via CP/M warm boot
; 
; open the destination file
; 
openok:	lxi	d,destfcb	; point to destination fcb
	lda	dest$dsk$num	; get destination disk number
	stax	d		; set-up destination disk fcb disk number
	mvi	c,delete	; erase any old file
	call	bdos
	lxi	d,destfcb
	mvi	c,make		; make the new one
	call	bdos
	cpi	0ffh		; succesfully created?
	jz	full		; oops, disk full...time for a new one
; 
; read source file to buffer, write to destination as copy
;
copy:	lxi	h,(table$end-filetable)/128	; save buffer size
	shld	bufmax
	xra	a		; clear eof flag
	sta	eof$flg
copy1:	call	abort		; check for user abort on ctrl-c
	lxi	h,0		; set current buffer counter to zero
	shld	bufcnt
	lxi	h,filetable	; set buffer start pointer to begin
	shld	bufpnt
; 
; file source reading loop to read all of buffer full or stop on eof
; 
copy2:	lhld	bufpnt		; set dma address to buffer pointer
	xchg
	mvi	c,stdma
	call	bdos
	lxi	d,dfcb		; point at default fcb for reading
	mvi	c,read		; function set for record read
	call	bdos
	ora	a		; check if read was o.k., or eof
	jnz	copy3		; end of file so set eof flag
	lhld	bufpnt		; set buffer pointer up one sector
	lxi	d,128
	dad	d
	shld	bufpnt
	lhld	bufcnt		; increase buffer sector count
	inx	h
	shld	bufcnt
	xchg			; check to see if memory is full
	lhld	bufmax		; maximum sector count
	call	cdehl		; compare
	jnz	copy2		; if not full go get next sector
	jmp	copy4		; go handle write operation
; 
; here if read operation indicates that the file is at its end on read
; 
copy3:	mvi	a,0ffh		; set eof flag
	sta	eof$flg
; 
; write output file processing loop to send memory buffer to destination disk file
; 
copy4:	lxi	h,filetable	; set buffer pointer to start
	shld	bufpnt
copy5:	call	abort		; check for user abort on ctrl-c
	lhld	bufcnt		; see if buffer is empty yet
	mov	a,h
	ora	l
	jz	copy6		; buffer empty so check eof flag
	dcx	h		; dec buffer sector count for each write
	shld	bufcnt
	lhld	bufpnt		; set up dma address
	push	h		; save for size bump
	xchg
	mvi	c,stdma
	call	bdos
	pop	h
	lxi	d,128		; increase address for sector size
	dad	d
	shld	bufpnt
	lxi	d,destfcb	; point to output file fcb
	mvi	c,write		; write record function code
	call	bdos		; go write
	ora	a   		; check if any error
	jz	copy5		; o.k., so do next record
	jmp	full		; oops...disk full, time for a new one
;
copy6:	lda	eof$flg		; buffer all written so go check eof
	ora	a
	jz	copy1		; go to read next buffer full
	lxi	d,destfcb	; point at fcb for file close
	mvi	c,close		; close file function code
	call	bdos
	cpi	0ffh		; check if close error
	jnz	backup		; if not, backup more files
	lxi	d,src$close$err	; oops, close error on destination disk
	jmp	arcexit		; exit with error message
;
; subroutine to compare [DE] to [HL], [Z] set if equal
;
cdehl:	mov	a,d		; high bytes equal?
	cmp	h
	rnz
	mov	a,e		; yes, how'bout low bytes?
	cmp	l
	ret			; set zero, if equal
;
; subroutine to allow disk change, to continue backup process
;
full:	lxi	d,destfcb	; delete partial file on destination disk
	mvi	c,delete
	call	bdos
	lxi	d,dsk$full	; indicate that disk (or dir) is full
	mvi	c,pmessg
	call	bdos
	lda	dest$dsk$num	; get destination disk number
	adi	040h		; ASCII'ize it
	mov	e,a		; display it
	mvi	c,pchar
	call	bdos
	lxi	d,now$full	; display remaining portion of full message
	mvi	c,pmessg
	call	bdos
req$cnt:lxi	d,enter$ret	; tell'em to remove/insert disk, hit return
	mvi	c,pmessg
	call	bdos
	mvi	c,coninp	; wait for user response
	call	bdos
	cpi	cr		; carriage return?
	jnz	req$cnt		; if not, leave reminder on what to do
	call	crlf		; tidy up screen
	mvi	c,rsetdsk	; reset disk system for newcomer
	call	bdos
	call	rset$fcb	; reset fcb for retry of previous file
	jmp	dspname		; continue on new disk, with last file
;
; Multi-file  access  subroutine.   Allows processing  of  multiple 
; files (i.e.  *.*) from disk.  This routine builds the proper name 
; in the fcb each time it is called.  Carry is set if no more names
; can be found. 
;
mfname:	mvi	c,stdma		; set dma address
	lxi	d,dbuff
	call	bdos
	xra 	a		; clear fcb extension and  record number
	sta	fcbext
	sta	fcbrno
	lda	mfflg1		; get multi-file flag
	ora	a
	jz	mfile1		; if zero, not 1st time flag
	lxi	h,dfcb		; save filename as requested name
	lxi	d,mfreq
	mvi	b,12
	call	blkmov
	lda	dfcb
	sta	mfcur		; save disk in current fcb
	lxi	h,mfreq		; set-up for filename search
	lxi	d,dfcb
	mvi	b,12
	call	blkmov
	mvi	c,srchfst	; do search first
	lxi	d,dfcb
	call	bdos
	jmp	mfile2		; check if file found
;
mfile1:	lxi	h,mfcur		; do search first on current filename
	lxi	d,dfcb
	mvi	b,12
	call	blkmov
	mvi	c,srchfst
	lxi	d,dfcb
	call	bdos
	lxi	h,mfreq		; do search next on requested filename
	lxi	d,dfcb
	mvi	b,12
	call	blkmov
	mvi	c,srchnxt
	lxi	d,dfcb
	call	bdos
mfile2:	inr	a		; return carry set, if file not found
	stc
	rz
;
; move name found to current filename
;
	dcr	a		; adjust location found
	ani	3
	add	a
	add	a
	add	a
	add	a
	add	a
	adi	81h
	mov	l,a		; make filename pointer
	mvi	h,0
	push	h		; save filename pointer
	lxi	d,mfcur+1
	mvi	b,11
	call	blkmov		; move filename to current filename
;
; move filename found to fcb
;
	pop	h		; get filename pointer
	lxi	d,dfcb+1
	mvi	b,11
	call	blkmov		; move filename to fcb
;
; setup fcb for subsequent file write operation
;
rset$fcb:
;
	lxi	d,dfcb		; point to source fcb
	lda	src$dsk$num	; force disk number, in case not logged disk
	stax	d
	xra	a		; clean-up for new file backup
	sta	fcbext
	sta	fcbrno
	sta	destfcb
	sta	destfcb+12
	sta	destfcb+32
	sta	mfflg1		; turn off 1st time flag
	ret
;
; 
; 
;  subroutine to do block moves
;
blkmov:	mov	a,m		; copy byte from [HL] to [DE]
	stax	d
	inx	h		; bump pointers
	inx	d
	dcr	b		; loop for count in [B]
	jnz	blkmov
	ret
; 
;  subroutine to index [BC] by file counter
;
filepoint:
;
	lhld	filcnt		; get file counter
	mvi	h,0		; force hi ord to 0
	dad	h		; multiply by 32
	dad	h
	dad	h
	dad	h
	dad	h
	dad	b		; use as index to file table
	ret
;
; subroutine to check for user abort (any key pressed)
;
abort:	push	h		; save all registers
	push	d
	push	b
	push	psw
	mvi	c,dircon	; check direct console i/o (status)
	mvi	e,0ffh		; input only
	call	bdos
	ora	a		; set flags
	jz	abortx		; return, if zero result
	cpi	'C'-40h		; control-c for abort?
	jnz	abortx		; just return, if not
	lxi	d,abort$process	; indicate we are aborting process
	mvi	c,pmessg
	call	bdos
	jmp	exit		; exit via CP/M warm boot
abortx:	pop	psw		; restore all registers
	pop	b
	pop	d
	pop	h
	ret
;
; subroutine to do carriage return/line feed
;
crlf:	lxi	d,crlf$msg
	mvi	c,pmessg
	call	bdos
	ret
; 
; 
; 
signon:		db	cr,lf,'ARCHIVE (CTRL-C to Abort) - Ver.1.0'
		db	cr,lf,cr,lf,'$'
; 
ilgopt:		db	'Invalid or Unspecified Option - Must Be Specified As:'
		db	cr,lf,cr,lf
opts:		db	tab,'B - Backup File Archive or,'
		db	cr,lf
		db	tab,'S - Set File Archive or,'
		db	cr,lf
		db	tab,'R - Reset File Archive or,'
		db	cr,lf
		db	tab,'D - Display File Archive'
		db	cr,lf,'$'
; 
no$file$fnd:	db	cr,lf,'File Not Found, Aborting$'
; 
donmsg:		db	'  ','$'
; 
crlf$msg:	db	cr,lf,'$'
; 
bad$ver$num:	db	'Must be CP/M Version 2.2 to Archive, Aborting$'
;
	if	not mpm		; archive must be 'installed' in CP/M-80
noarch:		db	'Archiving Not Installed, Aborting$'
	endif			; endif MP/M	
;
req$dest:	db	'Destination Disk for BACKUP Files (A to P)? $'
;
same$dsk:	db	cr,lf,'Can''t BACKUP to Source Disk You TWIT!',cr,lf,'$'
;
abort$process:	db	cr,lf,'User Abort of ARCHIVE Process$'
;
narcs:		db	cr,lf,'No ARCHIVE Files Found to BACKUP$' 
;
no$file$nam:	db	'No Filename or Option Specified - '
		db	'ARCHIVE must be invoked as:'
		db	cr,lf,cr,lf
		db	tab,'ARCHIVE FN.FT OPTION<cr>'
		db	cr,lf,cr,lf
		db	'Where;',tab,'FN.FT is Filename and Filetype '
		db	'(? and * allowed)'
		db	cr,lf,cr,lf
		db	'And;',tab,'OPTION is specified as:'
		db	cr,lf,cr,lf,'$'
; 
src$open$err:	db	cr,lf,'Oops, Can''t Open File on Source Disk$'
; 
dsk$full:	db	cr,lf,'Destination BACKUP Disk $'
;
now$full:	db	': is now FULL - Remove, insert NEW Disk$'
;
enter$ret:	db	cr,lf,'Enter RETURN when ready to continue BACKUP process: $'
; 
src$read$err:	db	cr,lf,'Oops, Read Error on Source Disk$'
; 
src$close$err:	db	cr,lf,'Oops, Bad Close on Destination Disk$'
;
bakup$done:	db	cr,lf,'BACKUP Complete',cr,lf,'$'
;
fname:		db	'            $'	; 11 spaces for filename.typ
;
got$arc:	db	0	; archive file found flag (1 = found)
;
mfflg1:		db	1	; 1st time flag for multi-file access
;
mfreq:		ds	12	; multi-file requested filename
;
mfcur:		ds	12	; multi-file current filename
;
arch$addr:	ds	2	; archive patch address
;
logdsk:		ds	1	; current logged-in disk number
; 
dest$dsk$num:	ds	1	; destination disk number
;
src$dsk$num:	ds	1	; source disk number
;
filcnt:		ds	1	; count of files in filetable
;
option:		ds	1	; storage for B, S, R or D option letter
;
destfcb:	ds	33	; destination disk file control block
;
bufmax:		ds	2	; buffer maximum size
;
bufcnt:		ds	2	; buffer sector count
;
bufpnt:		ds	2	; buffer pointer
;
eof$flg:	ds	1	; end-of-file flag
;
filetable	equ	$		; file table/buffer starts here
		ds	4*4096		; 16k buffer
table$end	equ	$		; file table/buffer ends here
; 
;
;
	end
