.title	'Copy hard disk partition to partition'
	.ident	cpyprt
	.sbttl	'Program discription'
version	==	2
revision==	0	;last change 12/01/83 DRB
patcè	=½	§ '
	.pabs
	.phex
	.sall
	.loc	100h
;----------
; This program copies one hard disk partition to
; another hard disk partition.  COPYPART accepts input
; from the command line or optionally prompts
; the user for the source and destination drives.
; After entry of valid drives, COPYPART verifies 
; that both drives are assigned to hard disk or 
; network partitions, and that both partitions are 
; the same size.  Copypart verifies that the same 
; CP/M drive is not specified as the sending
; and receiving drive.  If all is ok, the copy
; operation begins.  One CP/M track (128 sectors = 16K)
; is read from the source drive and written to the
; destination drive.  A 'c' is printed on the screen
; after each track is copied.  After all tracks have
; been copied, a message is printed and the program
; either terminates or does the optional verify.
; This part of the program verifies that the two 
; partition's are identical and prints a 'v' for
; each 16K verified.
;
; Table of contents
;
; Program description				1
; Update history				2
; Equates					4
; Macros					5
; Get user requests				6
; Disk read/write loop				7
; Verify loop					8
; Compare 2 CPM tracks				9
; Subroutines					10
; 	Process user request			10
;	Convert CPM drive to number		11
;	Get disk size				12
;	Set up read/write			14
;	Disk read/write routines		15
;	Not same, print location and bytes	16
;	BIOS interface				18
; Define storage				19
; CRT messages					20
.page
.sbttl	'Update History'
;
; 2.0 12/01/83	Update version for release.
;
; 'G' 11/21/83	Update Table of Contents for page and
;		subtitles.  DRB
;
; 'F' 11/15/83	Copypart now prints out a 'c' for each
;		16K copied and a 'v' for eack 16K
;		verified.  DRB
;
; 'C' 11/14/83  Some of the previous changes not yet
;		implemented.  User can type in file
;		and be prompted but it takes off after
;		that.  Command line processing instal-
;		led at this time.  DRB
;  Version 3 
.page
;  08/31/83	Combine copypart and testpart.  The
;		user interface has been radically
;		modified so the user has a chance to
;		save the bacon if they reverse the
;		drives.  Parition names are sent to
;		the screen so user can see what they
;		are about to do.  The user has option
;		of verifying the partition copy.  DRB
;
; 'E' 08/25/83  Add .sall to JCL to suppress printing
;		of macros.  Modify Nextsec: so it
;		sets up the DMA and sets the current
;		sector.  Should speed things up some.
;		Also, setup HL inline and call BIOS
;		BIOS directly for the same reason.
;
; 'D' 07/15/83	Add a real stack at end of .ascii
;		messages and initialize it to
;		76h so we can see usage.  DRB
;
; 'C' 07/13/83	Add message to login telling user
;		what this program does.  DRB
;
; 'B' 07/12/83	Remove test which checked if all
;		chars in command line parsed out.
;		There is a test for valid drive chars
;		and colons are disregarded and
;		I see no need for the test.  DRB
;
; 'A'		Add code so it will copy DMS-15
;		partitions.  Modify command line
;		parsing to disregard a colon after
;		a drive designation.
;
; revision 2:	utilize new cpmmap for multi
;		hard disk bios
; 
; 'A' 06/30/83	Add equate for DMS-15 Hard disk,
;		.sbttl the listing and make
;		a table of contents.  DRB
;
; revision 1: 	Convert the program to work on both
; 	     	CP/M 1 and 2.  Les Wilson and
; 	      	Doug Brentlinger 12/22/81
.page
.sbttl	'Equates'
;
; Device types as returned by cpmmap
;
sd	==	0<5	;single density 8 inch floppy
dd	==	1<5	;double density 8 inch floppy
hard	==	2<5	;hard disk map
net	==	3<5	;network map
mini1	==	4<5	;1 sided mini-floppy
mini2	==	5<5	;2 sided mini-floppy
hard15	==	6<5	;DMS-15 hard disk
;
; High memory equates
;
iobuff	==	4000h	; 16K I/O buffer
;
; ASCII equates
;
ctrlc	==	03h	; warm boot
cr	==	0Dh	; return
lf	==	0Ah	; line feed
;
; Low memory equates
;
wboot	==	0	; start over in CPM
cmdlin  ==	80h	; user command line
BDOS	==      5h
;
; CP/M function equates
;
conslin	==	1h	; CP/M 1,  Console Input
conout	==	2h	; CP/M 2,  Console Out
prtstrg ==	9h	; CP/M 9,  Print String
setdsk  ==	0Ch	; CP/M 13, Reset Disk System
;
; BIOS jump equates
;
rdjmp	==	24h
wrtjmp	==	27h
.page
.sbttl	'MACRO DEFINITIONS'
;---------------
;	jump if de not equal to register hl
;	(note: register a destroyed by compares
;
	.define	jumpdenothl[location]=[
	mov	a,e
	cmp	l
	jnz	location
	mov	a,d
	cmp	h
	jnz	location	]
;---------------
;	floptest, see if disk is a floppy and set up
;	for determining size
;
	.define floptest[%ok]=[
	pop	PSW	;get the accum back
	mov	C,A
	call	SELDSK
	call	CPMMAP
	dcx	h	;point to media type
	mov	a,m
        lxi	D,flop$
	cpi	hard	;is a hard disk partition
	jrz	%ok
	cpi	hard15	;is a DMS-15 hard disk
	jrz	%ok
	cpi	net	;is a net partition
	jnz	end	;if none, user blew it
%ok:
	inx	h	;make h as returned 
			;from cpm map
		]
.page
.sbttl 'Get user requests'
;
	lxi	SP,stack
	lxi	D,log$    ; Greet the user
	call	prtmsg
	lxi	H,cmdlin
	mov	A,m	  ; command line count
	cpi	0	  ; zero or one in
	cz	askusr
	jrz	cpytrks
	cpi	1	  ; command line count is ok
	cz	askusò	  » asë useò foò drivå stuff
	jrz	cpytrks	  ; copy what they want
;			  ; ELSE, see command line
	inr	A	  ; eatblnk works when we
	sta	comcntr	  ; look for end of com line
	call	eatblnk	  ; first drive in accum or
	jz	stxerr	  ; done before end, syntax err
	call	chkdsk	  ; ELSE, check dest drive
	jnc	driverr	  ; if bad, have to start over
	sta	dstdsk	  ; looks good, save dest disk
	call	eatblnk	  ; if char is ':' skip to next
	jz	stxerr	  ; done before end, syntax err
	cpi	'='	  ; must be equal sign
	jnz	stxerr	  ; or systax error
	call	eatblnk	  ; second drive in accum
	jz	stxerr	  ; done before end, syntax err
	call	chkdsk	  ; ELSE, check source drive
	jnc	driverr	  ; if bad, have to start over
	sta	srcdsk	  ; looks good, save src disk
	lxi	H,dstdsk  ; compare source and
	cmp	m	  ; destination drive
	jz     srcdsterr  ; source and dest same
;
	lda	dstdsk    ; see if partition sizes
	call	GETSIZE	  ; are the same
	sded	numtrk
	lda	srcdsk
	call	GETSIZE	  ; source size
	lhld	numtrk	  ; HL = size of dest disk
	jumpdenothl sizerr 

	call	eatblnk
	jrz	cpytrks	  ; end of buffer if zero
	cpi	'['	  ; next char must be
	jnz	stxerr	  ; left bracket
	call	eatblnk
	jz	stxerr	  ; then must be char 'V'
	cpi	'V'	  ; for verify
	jnz	stxerr	  ; if not, you are thru
	sta	verflag	  ; it is, store it for future
.page
.sbttl	'Disk read/write loop'
;
cpytrks:		 ;Copy from trk 0 to numtrk-1
	lxi	D,cpyst$ ;copy is started
	call	prtmsg
	lxi	D,crlf
	call	prtmsg
	sub	A	 ;track starts at 0
	sta	curtrk
;---------------
; Read 16K from source disk
readloop:
	lda	srcdsk	; read from source
	sta	curdsk	; stored as current disk
	lxi	H,iobuff ;buffer in RAM
	shld	curdma	; where DMA can put data
	mvi	A,1	; start a first sector
	sta	cursec
	call	SETUP
	call	read	; when comes back, read done
;---------------
; Write 16K to destination disk
;
	lda	dstdsk
	sta	curdsk	; now dest disk worked on
	lxi	H,iobuff
	shld	curdma
	mvi	A,1
	sta	cursec
        call	SETUP
	call	write
;			; Advance to next track
	mvi	E,'c'	; print a 'C' after 16K copied
	mvi	C,conout
	call	BDOS	
	lhld	curtrk
	inx	h	; increment track number
	shld	curtrk	
	lded	numtrk
	jumpdenothl readloop
;
	lda	verflag	  ; if user wants verify,
	cpi	'V'
	jrz	verify	  ; do it,
	lxi	D,done$   ; ELSE, we are done
	jmp	end	  ; tell user
.page
.sbttl	'Verify loop'
verify:
;---------------
; Copy from track 0 to numtrk - 1
	lxi	D,verst$
	call	prtmsg
	lxi	D,crlf
	call	prtmsg
	sub	A
	sta	curtrk
;---------------
; Read 16K from source disk
verloop:
	lda	srcdsk
	sta	curdsk
	lxi	H,buffsource
	shld	curdma
	mvi	A,1
	sta	cursec
	call	SETUP
	call	read
;---------------
; read 16K from destination disk
;
	lda	dstdsk
	sta	curdsk
	lxi	H,buffdestination
	shld	curdma
	mvi	A,1
	sta	cursec
        call	SETUP
	call	read
.page
.sbttl	'Compare 2 CPM tracks'
;
	lxi	d,buffsource
	lxi	h,buffdestination
	lxi	b,4000h	; number of conpares
comploop:
	ldax	d	; accum pointed to by DE
	cmp	m	; m pointed to by HL
	cnz	notsame	; if not same print it out
	inx	h	; ELSE, increment destination
	inx	d	; and source registers,
	dcx	b	; decrement counter
	mov	a,b
	ora	c
	jrnz	comploop ;and go back if not done
;
	mvi	E,'v'	; ELSE, Advance to next track
	mvi	C,conout
	call	BDOS	; print 'v' after 16K verified
	lhld	curtrk
	inx	h
	shld	curtrk	; increment track number
	lded	numtrk
	jumpdenothl verloop
	lxi	D,verdon$
	jmpr	end	 ; tell user done
;---------------
;Error exits from Copypart 
;
srcdsterr:		; source and dest same
	lxi	D,sam$
	jmpr	end
;
driverr:		; CP/M drive not available
	lxi	D,notavl$
	jmpr	end
;
stxerr: 		; command line systax error
	lxi	D,stxmsg
	jmpr	end
;
sizerr: lxi	D,siz$  ; partitions are not same size
;
end:
	call	prtmsg
	jmp	wboot

.page
.sbttl 	'Process user request'
;--------------
; Subroutine askusr:  Ask user for copy information
; Reg in:  None
; Reg out: None
; Destroyed: All
;
askusr:	
..src:	lxi	D,asksrc$ ;get source drive
	call	prtmsg
	call	conin	; get user response
	cpi	ctrlc
	jz	wboot
	call	chkdsk	  
	jrnc	..src
	sta	srcdsk
..dst:	lxi	D,askdst$ ;get dest drive
	call	prtmsg
	call	conin
	cpi	ctrlc
	jz	wboot
	call	chkdsk
	jrnc	..dst
	sta	dstdsk
	lxi	H,srcdsk ;compare source and
	cmp	m	 ;destination drive
	jrnz    ..cksiz	 
	lxi	D,sam$   ;same source and destination
	call	prtmsg
	jmpr	askusr
..cksiz:
	lda	dstdsk  ; see if partition sizes
	call	GETSIZE	; are the same
	sded	numtrk
	lda	srcdsk
	call	GETSIZE	; source size
	lhld	numtrk	; HL = size of dest disk
	jumpdenothl sizerr 
;
	lxi	D,verqst$
	call	prtmsg
	call	conin
	cpi	'V'
	jrnz	..nocom
	sta	verflag	  ;'V' = verify flag
	sub	A	  ;ELSE, null in A
	ret
..nocom:
	sub	A
	sta	verflag ; null in verify flag
	ret
.page
.sbttl	'Convert CPM disk drive to number'
;---------------
; Subroutine Chkdsk: Convert disk letter to number
; Regs in:  A = disk selection (A-Q) from command line
; Regs out: A = legal disk number (0-15)
;	    Carry flag set if good
; Destroyed: A,C, Carry flag
;
chkdsk:
	ani	0DFh	; upper case
	cpi	'R'	; first invalid drive
	jrnc	..ng	; no carry, no good
	cpi	'A'	; first valid drive
	jrc	..ng	; no good, no carry
	sui	'A'	; ELSE, valid drive,
	push	PSW	; save it, see if
	mov	C,A	; this drive is ok
	call	seldsk	
	mov	A,L	
	ora	A	; HL = 0 if it is not
	lxi	D,notavl$
	jz	end	; so if zero, error
	pop	PSW	; ELSE, restore drive
	stc		; set carry,
	ret		; and go back
;
..ng:	sub	A	; clear accum, reset
	ret		; carry flag and return
;---------------
; Subroutine eatblnk:  Eat blanks in com line, next
;		       char in accum
; Regs in:  None
; Regs out: A = next character in command line
; Destroyed: A, HL
;
eatblnk:lxi	H,comcntr ; if we have gone thru
	dcr	m	  ; the command line,
	mov	A,m	  ; counter is 0
	cpi	0	  ; and dont want to see
	rz		  ; next char
	lhld	comaddr	  ; ELSE, get next char
	inx	H
	shld	comaddr	  ; save addr for next one
	mov	A,m
	cpi	' '	; skip blanks for chars
	jrz	eatblnk
	cpi	':'	; disregard colon
	jrz	eatblnk
	ret
.page
.sbttl	'Get disk size'
;---------------
; Extract disk size from disk parameter table in BIOS
;  Regs in:  A = disk to be sized (0-3)
;  Regs out: de = disk size (in CP/M tracks, 16-512)
GETSIZE:
	push	PSW	;save the accum
	mvi	C,setdsk ;CP/M 13, Reset Disk System
	call	BDOS
	mov	A,L	;if version # = 0, don't jump
	ora	A
	jrnz	CPM2	;we have a 2 version of CP/M
	floptest
	inx	H
	inx	H
	inx	H
	inx	H
	mov	A,M	; get "sectors per block - 1"
	inr	A
	slar	A	; compute tracks per disk
	jc	d1
	mvi	d,0
	jmpr	dset
d1:	mvi	d,1
dset:	mov	e,a
	ret
; Subroutine Getsize continues on the next page
.page
CPM2:	
	floptest
	inx	H
	inx	H
	inx	H
	mov	A,m	;HL address + 2 memory
	cpi	3	;implied block size of 1k
	jrz	siz256	;used only for 256K partition
	cpi	5	;implied block size of 4k
	jrz	siz8meg ;used only for 8 meg partition
        lxi     D,3
	dad	D	;HL addr + 5
	mov	E,m
	inx	H	;DE = L and L + 1
	mov	D,m	;DE = blocks
 	inx	D
	srlr	E
	srlr	E
	srlr	E	;divide low order of blocks by 8
	mov	a,d
	slar	a	;divide high order of blocks by
			;8 getting bits to go into 
                        ;low order byte (remainder)
	slar	a
	slar	a
	slar	a	;shift low bits to hi bits
	slar	a	;and then to the hi bits of
	ora	e	;E
	mov	e,a
	srlr	d
	srlr	d
	srlr	d	;divide high order bytes by 8
	ret
siz256:	lxi	D,16	;# of tracks on 256 K part
	ret
siz8meg:lxi	D,200h  ;# of tracks on 8 meg partition
	ret
.page
.sbttl	'Set up read/write'
;--------------
; Setup a CP/M disk operation
SETUP:
	lda	curdsk
	mov	C,A
	call	SELDSK	; set disk number
	lbcd	curtrk
	call	SETTRK	; set track number
	lda	cursec
	mov	C,A
	lxi	D,1Eh	; set sector number jump
	call	BIOS	; for the BIOS
	lbcd	curdma
	lxi	D,21h	; set DMA address JUNP
	call	BIOS	; for the BIOS
	ret
;--------------
; Advance to next sector
;  Regs out:  Z flag set if end of track
NEXTSEC:
	lda	cursec
	cpi	128
	rz
	inr	A	
	sta	cursec  ; increment sector number
	lhld	curdma
	lxi	D,128
	dad	D
	shld	curdma	; increment DMA address
	lda	cursec
	mov	C,A
	lxi	D,1Eh	; set sector number jump
	call	BIOS	; for the BIOS
	lbcd	curdma
	lxi	D,21h	; set DMA address JUNP
	call	BIOS	; for the BIOS
	ret
.page
.sbttl	'Disk read/write routines'
;---------------
; Subroutine Read:  Read from the source disk
; Reg in:  None
; Reg out: None
; destroyed: All
;
Read:
..1:
	lxi	D,rdjmp	; read jump for
	call	BIOS	; the BIOS
	call	NEXTSEC
        jrnz	..1
	ret
;---------------
; Subroutine Write:  Write to the destination disk
; Reg in:  None
; Reg out: None
; destroyed: All
;
Write:
..1:
	lxi	D,wrtjmp ;Write jump for
	call	BIOS	; the BIOS
	call	NEXTSEC
        jrnz	..1
	ret
.page
.sbttl	'Not same, print location and bytes'
;--------------
; Subroutine notsame:  bytes not the same, print loc
; Reg in:  BC,DE,HL
; Reg out: None
; Destroyed: All
;
notsame:
	shld	saveh
	sded	saved
	sbcd	saveb
	lxi	d,diff$
	call	prtmsg
	lhld	curtrk
	call	prthl
	mvi	e,','
	mvi	c,conout
	call	bdos
	lxi	h,4000h
	lbcd	saveb
	ora	a
	dsbc	b
	call	prthl
	mvi	e,','
	mvi	c,conout
	call	bdos
	lded	saved
	ldax	d
	call	prthex
	mvi	e,','
	mvi	c,conout
	call	bdos
	lhld	saveh
	mov	a,m
	call	prthex
	lhld	saveh
	lded	saved
	lbcd	saveb
	ret
.page
; print subroutines for the errors
;
SPRTHLM:		;SP PRT (HL)(M)
	MOV	A,M
	PUSH	PSW	;SAVE (M)
	CALL	PRTHL
	POP	PSW
	JMPR	SPRTHEX
PRTHL:	PUSH	H
	MOV	A,H
	CALL	SPRTHEX
	POP	H
	MOV	A,L
	JMPR	PRTHEX
;
SPRTHEX:		;SPACE AND PRINT (A) IN HEX
	PUSH	PSW
	MVI	e," "
	mvi	c,conout
	call	bdos
SPH2:	POP	PSW
PRTHEX:	PUSH	PSW
	RAR
	RAR
	RAR
	RAR
	CALL	PRTNBL
	POP	PSW
PRTNBL:	ANI	0FH	;PRINT NIBBLE
	ADI	030H
	CPI	03AH
	JRC	SML
	ADI	7
SML:	MOV	e,A
	mvi	c,CONOUT
	call	bdos
	RET
;--------------
; Subroutine prtmsg:  Print message to console
;
prtmsg:	mvi	C,prtstrg
	call	BDOS
	ret
;--------------
; Subroutine conin:  Get input from user
;
conin:	mvi	C,conslin
	call	BDOS
	ani	0DFh	; upper case all responses
	ret
.page
.sbttl	'BIOS interface'
;----------
; CP/M BIOS interface
SELDSK:	lxi	D,18h	; set disk number
	jmpr	BIOS
SETTRK:	lxi	D,1Bh	; set track number
	jmpr	BIOS
CPMMAP:	lxi	D,60h	; get disk parameter table
BIOS:
	lhld	1
	dad	D
	pchl
.page
.sbttl 'Define storage'
;---------------
comaddr:.word	80h	; com line addr start at 80h
dstdsk:	.byte	0	; destination disk (0-3)
srcdsk:	.byte	0	; source disk (0-3)
numtrk:	.word	0	; CP/M tracks on disk (16-512)
curdsk:	.byte	0	; current disk (0-3)
curtrk:	.word	0	; current track (0-511)
cursec:	.byte	0	; current sector (1-128)
curdma:	.word	0	; current DMA address
verflag:.byte	0	; set to 'V' for verify
comcntr:.byte	0	; count down command line
;
; Storage for compare copy routines
;
saveb:	.blkw	1	;save bc register
saved:	.blkw	1	;save de register
saveh:	.blkw	1	;save hl register
.page
.sbttl	'CRT messages'
;
log$:   .ascii	[cr][lf]'COPYPART version '
	.byte	version+'0','.',revision+'0'
	.byte	patch,cr,lf
	.ascii	[cr][lf]'Copy from hard disk '
	.ascii	'parition to hard disk parition.'
crlf:	.ascii	[cr][lf]'$'
stxmsg:	.ascii	[cr][lf]'Command line syntax error, '
	.ascii		'please try again.$' 
asksrc$:.ascii	[cr][lf]'Enter the Source drive: $'
askdst$:.ascii	[cr][lf]'Enter the Destination '
	.ascii		'drive: $'
notavl$:.ascii	[cr][lf]'This drive not available, '
	.ascii		'please try again.$'
flop$:  .ascii	[cr][lf]'Source or destination disk '
	.ascii		'is assigned to a floppy disk '
	.ascii		'drive.  ASSIGN can help.$'
siz$:   .ascii	[cr][lf]'Source and destination '
	.ascii		'partition sizes are not '
	.ascii		'the same.  ASSIGN can help.$'
sam$:   .ascii	[cr][lf]'Source and destination '
	.ascii		'drives are the same.$'
verqst$:.ascii	[cr][lf]'Enter "V" if you want the '
	.ascii		'parition copy verified, '
	.ascii		'ELSE any other character.  $'
cpyst$:	.ascii	[cr][lf]'Copy is started.$'
verst$:	.ascii	[cr][lf]'Verify started.$'
done$:  .ascii	[cr][lf]'Partition copy completed.$'
verdon$:.ascii	[cr][lf]'Verify completed.$'
diff$:  .ascii	[cr][lf]'track, byte no, source, '
	.ascii		'destination$'
;
	.byte	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	.byte	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	.byte	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
stack:
	.ascii	'END'
;
;	buffers for 16k cpm track for each partition
;
buffsource: .blkb	4000h
buffdestination: .blkb	4000h
;
	.end

