;====================================================================
;                         SWAPCOPY v 2.3			    |
;								    |
;    A single drive file copy program Kaypro NEW 2 & 10 models	    |
;====================================================================
;
; Modified for the video of the Kaypro models only.
; Automatically sets logged drive for all copy operations.
;
; Added auto-input prompting and cursor addressing for console
; messages.
;
; This is the program Kaypro --> SHOULD HAVE GIVEN EVERY PURCHASER 
; of a NEW 2 model.
;
;     Steve Sanders  Tampa, FL  01/06/84  (813) 937-7249 voice
;     (813) 937-3608   (813) 937-6829  data  300/1200  24 hrs
;....................................................................
;
; Allows files to be transfered from one diskette to
; another in a system with only a single drive.
;
; Allows ambiguous file specifications.
; Allows transfer of files larger than available memory,
; and transfer of multiple files in a single pass.
;
; Allows use of dissimiliar media (i.e., to copy
; programs from a single density master diskette
; to a double density working diskette).
;
;	Disk to disk file transfer program.
;
;	At the command level, the command:
;
;	A>SWAPCOPY FILENAME.EXT <cr>
;
;	will copy the file FILENAME.EXT from diskette
;	to diskette, prompting as necessary to allow the
;	diskette in drive A to be swapped.
;
;  or...
;
;       A>SWAPCOPY *.* <cr>
;
;       will prompt for each filename found on disk
;       with (y/n) and copy as many as possible into
;       memory and so on ...
;
;      ===================================================
;      ===  PROGRAM ABORTS IF NO FILE SPEC IS ENTERED  ===
;      ===================================================
;
;///////////////////////////////
;
; equates
;
boot	equ	   0	;system reboot
bdos	equ	   5	;bdos entry point
memtop	equ	   6	;pointer to start of fbase
fcb1	equ	  5ch	;first file name & source fcb
fcb2	equ	  6ch	;second file name
tpa	equ	 100h	;beginning of tpa
;
false	equ	   0
true	equ	 255
;
esc	equ	  27
bell	equ	   7
clrscr	equ	  26	;clear screen/home cursor
eos	equ	  23	;clear to end of screen
eol	equ	  24	;clear to end of line
cntrlc	equ	   3
tab	equ	   9
lf	equ	  10
cr	equ	  13
secsiz	equ	 128	;bytes per sector
;
	org	tpa	;where else?
;
swapcopy:
	lxi	sp,stack;set up local stack space
;
	mvi	a,false
	sta	done	;we're not done yet
	sta	one2go	;nor do we have a file to go
;
	mvi	c,isdrv	;get the current drive
	call 	bdos
	sta	drive	;store it
;
	mvi	a,true
	sta	rflag	;report operations - yes
	sta	qflag	;query before copy - yes
;
	lxi	h,buf+eofo
	mvi	m,true	;set initial eof to true
;
	lhld	memtop
	lxi	d,-(secsiz+freeo+8)
	dad	d	;highest availiable dma buffer
	shld	buftop
;
	lxi	h,buf
	shld	fpbpnt	;free space starts at buf
;
	mvi	a,1
	sta	names	;initial next-names count
;
	lxi	d,banner
	call	print
;
	lda	fcb1+1	;check for filename or *.*
	cpi	' '	;if none entered
	jnz	sdisk	
	lxi	d,nospec
	jmp	abort	;abort with error msg
;
sdisk:	lxi	d,sourmx	;or...
	call	prompt	;ask for the source disk
	call	ffname	;get the first matching file name
	call	adjpnt
	call	filfpb	;set rdma & wdma pointers, fill in file name
	call	query	;is the first file to be copied?
	jnc	nxtnam	;if not, try the next file
	mvi	a,true
	sta	one2go	;if so, set 'one to go' to true, then copy it
;
rdloop:	lda	fileof
	ana	a
	jm	wr	;start writing when the last file has been read
	lhld	rdma
	call	fulbuf
	jc	wr	; or when the buffer space has been exhausted
;
	lhld	eof
	mov	a,m
	ana	a
	jz	samfil	;if not end-of-file, we're on the same file.
;
	call	adjpnt	;Else, this is the start of a new file...
	call	filfpb	;adjust pointers & fill new fpb
	xra	a
	lhld	sfext
	mov	m,a	;source file extent := 0
	lhld	sfcr
	mov	m,a	;source file current record := 0
	lhld	dfext
	mov	m,a	;dest file extent := 0
	lhld	dfcr
	mov	m,a	;dest file current record := 0
	lhld	eof
	mov	m,a	;eof := false
	lhld	made
	mov	m,a	;made := false
	lhld	opened
	mov	m,a	;opened := false
	lhld	last
	mov	m,a	;last := false
	mvi	a,true
	sta	fpbusd	;mark current fpb as used
	lhld	sfcb
	call	open
;
samfil:	call	read
	lhld	eof
	mov	a,m
	ana	a
	jz	samnam	;read sectors until eof or buffer full
;
nxtnam:	call	ffname	;on eof, find the next name to be considered
	lxi	h,names
	mov	a,m
	sta	namcnt
nnloop:	call	fnname	;find next name...
	lxi	h,namcnt
	dcr	m
	jnz	nnloop	;as many times as necessary.
	lxi	h,names
	inr	m	;then bump the count for the next pass
;
	lda	fileof
	ana	a	;if all matching file names have been copied
	jm	wr	;(or passed over), start writing.
	call	query	;is the first file to be copied?
	jnc	nxtnam	;if not, try the next file
	mvi	a,true
	sta	one2go	;if so, set 'one to go' to true, then copy it
	lda	fpbusd
	ana	a	;if the current fpb is unused, use it
	jz	samnam
	lhld	rdma	;else set up the next fpb, and use it
	mov	a,m
	inx	h
	mov	h,m
	mov	l,a
	shld	fpbpnt	;next block starts just past rdma
	jmp	rdloop
;
samnam:	lhld	rdma
	call	bump	;rdma := rdma + secsiz
	jmp	rdloop
;
wr:	lda	one2go	;anything for the destination disk?
	ana	a
	jz	fini	;if not, we're done.
	lxi	h,buf
	shld	fpbpnt	;fpbpnt := buf
	call	adjpnt
	lxi	d,destmx
	call	prompt	;prompt(destmx)
;
wrnext:	lhld	made
	mov	a,m
	ana	a
	jnz	a$m	;if already made, don't make it again
;
	mvi	m,true	;if not made, set made := true
	lhld	opened
	mvi	m,true	;then set opened := true,
	call	make	;then make the file.
;
a$m:	lhld	opened
	mov	a,m
	ana	a
	jnz	a$op	;if already opened, don't open it again
	mvi	m,true
	lhld	dfcb
	call	open	;if not open, open it & set opened := true
;
a$op:	lhld	rdma
	mov	e,m
	inx	h
	mov	d,m	;de gets rdma
	lhld	wdma
	mov	a,e
	cmp	m	;compare least signifigant bytes
	jnz	nequal
	inx	h
	mov	a,d
	cmp	m
	jz	equal	;is wdma equal to rdma?
;
nequal:	call	write	;if not, write a sector
	lhld	wdma
	call	bump	;wdma := wdma + secsiz
	jmp	a$op
;
equal:	call	close	;when wdma = rdma, close the file
	lhld	eof
	mov	a,m
	ana	a	;done with file?
	jz	skprpt	;if not, don't report the transfer yet.
	lda	rflag
	ana	a	;is the 'Report' option set?
	jz	skprpt	;if not, don't report the file transfer.
;
	lxi	d,cpydmx
	call	print	;print 'Copied '
	lhld	dfcb
	call	pfname	;print the file name
	call	crlf	;start a new line
;
skprpt:	lhld	wdma
	call	fulbuf	;if the whole buffer has been written,
	jc	wrexit	;try to read another some more in
;
	lhld	rdma	;else, set up to write the next file
	mov	e,m
	inx	h
	mov	d,m
	xchg		;hl points to next fpb
	shld	fpbpnt	;set up the file param. block pointer
	lhld	last
	mov	a,m
	sta	done
	ana	a
	jnz	fini	;if the last file has been written, we're done
	call	adjpnt
	jmp	wrnext	;else adjust pointers, then write the next file
;
wrexit:	lda	done
	ana	a	;shall we exit or read more files?
	jnz	fini	;we're done, so exit
;
	lxi	b,freeo
	lxi	d,buf
	lhld	fpbpnt
	call	ldir	;move the last fpb to the start of the buffer
;
	lxi	h,buf
	shld	fpbpnt
	call	adjpnt
	call	filfpb	;set up the pointers at the start of the buffer
;
	lxi	d,sourmx
	call	prompt	;ask for the source disk
	jmp	rdloop
;
fini:	lxi	d,systmx
	call	prompt	;prompt for system disk
	lxi	d,normal
abort:	call	print
	rst	boot
;
namsiz	equ	12	;size of a disk file name (1+8+3)
fcbsiz	equ	36	;size of a file control block
sfcbo	equ	0	;offset to source fcb
sfexto	equ	12	;offset to source file extent
scro	equ	32	;offset to source file current record
dfcbo	equ	36	;offset to destination file control block
dfexto	equ	48	;offset to destination file extent
dcro	equ	68	;offset to destination file current record
rdmao	equ	72	;offset to read zone pointer
wdmao	equ	74	;offset to write zone pointer
madeo	equ	76	;offset to file-made flag
openo	equ	77	;offset to file-open flag
eofo	equ	78	;offset to end-of-file flag
lasto	equ	79	;offset to last-file flag
freeo	equ	80	;offset to start of freespace
;
adjpnt:	lhld	fpbpnt	;compute and save the...
	lxi	d,sfexto
	dad	d
	shld	sfext	;	pointer to source file extent
	lxi	d,scro-sfexto
	dad	d
	shld	sfcr	;	pointer to source file current record,
	lxi	d,dfcbo-scro
	dad	d
	shld	dfcb	;	pointer to dest fcb,
	lxi	d,dfexto-dfcbo
	dad	d
	shld	dfext	;	pointer to dest file extent,
	lxi	d,dcro-dfexto
	dad	d
	shld	dfcr	;	pointer to dest file current record,
	lxi	d,rdmao-dcro
	dad	d
	shld	rdma	;	pointer to read dma zone,
	inx	h
	inx	h
	shld	wdma	;	pointer to write dma zone,
	inx	h
	inx	h
	shld	made	;	pointer to file made flag,
	inx	h
	shld	opened	;	pointer to file-open flag
	inx	h
	shld	eof	;	pointer to end-of-file flag,
	inx	h
	shld	last	;	pointer to the 'last-file' flag,
	inx	h
	shld	fspace	;and the pointer to the start of free space.
	ret
;
filfpb:	lhld	fpbpnt
	xchg
	lhld	fileof	;get offset into directory bufffer
	mvi	h,0
	dad	h
	dad	h
	dad	h
	dad	h
	dad	h	;multiply by 32
	lxi	b,dirbuf
	dad	b	;hl points to file name
	lxi	b,namsiz
	push	h
	push	b
	call	ldir	;move file name to source fcb
	pop	b
	lhld	dfcb
	xchg
	pop	h
	call	ldir	;move file name to destination fcb
	lhld	fspace
	xchg
	lhld	rdma
	mov	m,e
	inx	h
	mov	m,d	;point rdma to start of free space
	inx	h
	mov	m,e
	inx	h
	mov	m,d	;point wdma to start of free space
	ret
;
bump:	mvi	a,secsiz
	add	m
	mov	m,a	;add sector size to l.s. byte of address
	rnc
	inx	h
	inr	m	;if carry, increment m.s. byte of address
	ret
;
fulbuf:	mov	e,m
	inx	h
	mov	d,m	;de gets address to be compared
	lhld	buftop
	mov	a,l
	sub	e
	mov	a,h
	sbb	d
	ret		;return with carry set if @(de) > buftop
;
query:	lda	qflag
	ana	a
	stc
	rz		;copy all files if not querying
	lxi	d,copymx
	call	print	;print 'Copy '
	lda	fileof
	ani	3
	add	a
	add	a
	add	a
	add	a
	add	a	;get 32 * file offset
	lxi	h,dirbuf
	add	l
	mov	l,a
	adc	h
	sub	l
	mov	h,a	;point hl to filename
	call	pfname	;print the filename
	lxi	d,qmark
	call	print	;print '?'

getit:	mvi	c,cstsf		;check console for chr
	call	bdos
	ora	a
	jz	getit		;if none - loop until chr waiting
	mvi	c,cif
	call	bdos		;get the chr
	cpi	' '		;if a blank
	jz	getit		;loop and wait for next chr
	ani	(not 20h) and 255
	sui	'Y'		;if y or Y
	stc
	rz			;copy the file
	cmc
	ret

;
pfname:	mvi	a,8
	call	pathl	;print 8 characters in the name,
	mvi	e,'.'
	call	co	;print a period,
	mvi	a,3	;print the 3 characters in the extension.
;
pathl:	inx	h
	mov	e,m
	call	co	;print the character @+(hl)
	dcr	a
	jnz	pathl	;repeat (a) times
	ret
;
;	system interface routines
;
cif	equ	   1	;console input function #
cof	equ	   2
printf	equ	   9	;print buffer function #
linef	equ	  10	;line input function #
cstsf	equ	  11	;get console status function #
resetf	equ	  13	;select & write enable drive A
seldsk	equ       14	;select disk drive
openf	equ	  15	;open file function #
closef	equ	  16	;close file function #
sfff	equ	  17	;search for first function #
sfnf	equ	  18	;search for next function #
deletef	equ	  19	;delete file function #
readf	equ	  20	;sequential file read
writef	equ	  21	;sequential file write
makef	equ	  22	;create & open a new file
isdrv	equ	  25	;interogate selected drive
dmaf	equ	  26	;set dma address function #
;
open:	mvi	c,openf
	xchg
	call	bdos
	inr	a
	jz	openng
	ret
openng:	lxi	d,cantop
	jmp	abort
;
close:	mvi	c,closef
	lhld	dfcb
	xchg
	call	bdos
	lhld	opened
	mvi	m,false
	ret
;
read:	lhld	rdma
	mov	e,m
	inx	h
	mov	d,m
	call	setdma
	mvi	c,readf
	lhld	fpbpnt
	xchg
	call	bdos
	lhld	eof
	mov	m,a
	ana	a
	rnz
	ret
;
write:	lhld	wdma
	mov	e,m
	inx	h
	mov	d,m
	call	setdma
	mvi	c,writef
	lhld	dfcb
	xchg
	call	bdos
	ana	a
	jnz	cantwr
	ret
cantwr:	lxi	d,space
	jmp	abort
;
make:	mvi	c,deletef
	lhld	dfcb
	xchg
	call	bdos
	mvi	c,makef
	lhld	dfcb
	xchg
	call	bdos
	inr	a
	jz	makeng
	ret
makeng:	lxi	d,wrprot
	call	print
	lxi	d,nodir
	jmp	abort
;
setdma:	mvi	c,dmaf
	jmp	bdos
;
reset:	mvi	c,resetf
	call	bdos
;	april 1982 - select desired disk drive
	mvi	c,seldsk
	lda	drive
	mov	e,a
	jmp	bdos
;
;
crlf:	mvi	e,cr
	call	co
	mvi	e,lf
co:	push	h
	push	psw
	mvi	c,cof
	call	bdos
	pop	psw
	pop	h
	ret
;
print:	mvi	c,printf
	jmp	bdos
;
ffname:	lxi	d,dirbuf
	call	setdma
	mvi	c,sfff
	lxi	d,fcb1
	call	bdos
	sta	fileof
	inr	a
	jnz	fixf
	lxi	d,nofile
	jmp	abort
;
fnname:	lxi	d,dirbuf
	call	setdma
	mvi	c,sfnf
	lxi	d,fcb1
	call	bdos
	sta	fileof
	add	a
	sbb	a	;a:=255 if fileof=255, else 0
	lhld	last
	mov	m,a
;
;	april 1982 fix for cpm1.4 where numbers >3 were detected
;	but not desired...hence and with 3
;
fixf:	lda	fileof
	cpi	255
	rz
	ani	3
	sta	fileof
	ret
;
prompt:	
	call	print
clear:	mvi	c,cstsf
	call	bdos
	ana	a
	jz	wait
	mvi	c,cif
	call	bdos	;if there's a char waiting, get it & ignore it.
	jmp	clear
wait:	mvi	c,cif
	call	bdos	;get the next char
	cpi	cntrlc
	jz	boot
	cpi	cr
	jnz	badch	;explain the procedure
	call	reset
	ret
;
badch:	lxi	d,explan
	jmp	prompt
;
ldir:	mov	a,m
	stax	d
	inx	h
	inx	d
	dcx	b
	mov	a,c
	ora	b
	jnz	ldir
	ret
;
;	console messages
;
; note: all these messages contain cursor addressing and video
;       attribute calls useable only on a Kaypro.
;                             [steve]
;
banner:	db	clrscr,esc,'B0'
	db	'                                                               '
	db	cr,lf,'  ',esc,'B1'
	db	' KAYPRO SWAPCOPY v2.3   SINGLE FLOPPY FILE COPYING PROGRAM '
	db	esc,'C1','  ',cr,lf,'  ',esc,'B1'
	db	'                by Steve Sanders 06-Jan-85                 '
	db	esc,'C1','  ',cr,lf
	db	'                                                               '
	db	cr,lf,lf,esc,'C0','$'
sourmx:	db	esc,61,38,32,eos,esc,'B0',esc,'B1'
	db	' Insert SOURCE disk, press <RETURN>: '
	db	esc,'C1',esc,'C0',bell,'$'
destmx:	db	esc,61,38,32,eos,esc,'B0',esc,'B1'
	db	' Insert DESTINATION disk, press <RETURN>: '
	db	esc,'C1',esc,'C0',bell, '$'
systmx:	db	esc,61,38,32,eos,esc,'B0'
	db	' Insert SYSTEM disk, press <RETURN> to finish: '
	db	esc,'C0',bell, '$'
copymx:	db	esc,61,39,32,eos,esc,'B0',' Copy --> $'
cpydmx:	db	esc,61,39,32,eos,bell,esc,'B0',' Copied --> $'
qmark:	db	'  (y/n)? ',esc,'C0',bell,'$'
explan:	db	esc,'B6',esc,61,45,32,esc,'B0'
	db	'                                       '
	db	cr,lf,'  ',esc,'B1',esc,'B2'
	db	' To continue, hit the <RETURN> key '
	db	esc,'C1','  ',cr,lf,'  ',esc,'B1'
	db	'      To abort, hit CTRL-C         '
	db	esc,'C2',esc,'C1','  ',cr,lf
	db	'                                       '
	db	esc,'C1',esc,'C0',esc,'C6','$'
normal:	db	esc,61,37,49,eos,lf,lf
	db	'== SwapCopy is finished ==',cr,lf,bell,'$'
nospec:	db	bell,esc,61,37,32,eos
	db	' No file specification was entered - ABORTING.'
	db	cr,lf,lf,' Use *.* for all or specify filename desired.'
	db	cr,lf,lf,lf,esc,'B3'
	db	'Note: Currently logged drive will be used for copying.'
	db	esc,'C3',cr,lf,bell,'$'
nofile:	db	esc,61,45,64, 'No source files',bell, cr, lf, '$'
nodir:	db	esc,61,45,59,'Directory space exhausted',bell, cr, lf, '$'
cantop:	db	esc,61,45,63, 'Can''t reopen file.',bell, cr, lf, '$'
space:	db	esc,61,45,62, 'Disk space exhausted.',bell, cr, lf, '$'
wrprot:	db	esc,61,45,60, 'Diskette Write protected?', bell,cr, lf, '$'
;
;	data areas
;
sfcb:	ds	0
fpbpnt:	ds	2	;pointer to start of the file param. block
sfext:	ds	2	;pointer to source file extent byte
sfcr:	ds	2	;pointer to source file current record byte
dfcb:	ds	2	;pointer to destination file control block
dfext:	ds	2	;pointer to dest file extent byte
dfcr:	ds	2	;pointer to dest file current record byte
buftop:	ds	2	;pointer to top of free memory
rdma:	ds	2	;pointer to next read dma zone
wdma:	ds	2	;pointer to next write dma zone
made:	ds	2	;pointer to file made flag
opened:	ds	2	;pointer to file open flag
eof:	ds	2	;pointer to end of file flag
last:	ds	2	;pointer to last file flag
fspace:	ds	2	;pointer to start of free buffer space
fileof:	ds	1	;file offset. index into dirbuf
done:	ds	1	;all files copied flag
names:	ds	1	;number of the next file to be copied
namcnt:	ds	1	;copy of 'names'. used as a counter
drive	ds	1	;drive number 0-3 for a-d and so on
qflag:	ds	1	;'Query' option flag
rflag:	ds	1	;'Report' option flag
fpbusd:	ds	1	;current fpb has been used flag
one2go:	ds	1	;there is a file to be transfered flag
;
	ds	 64	;32 level stack
stack:	ds	  0
;
dirbuf:	ds	secsiz	;buffer for searching the directory
;
buf:	ds	  0	;buffer starts here and extends to fbase
;
	end	swapcopy
