	title	'Copysys - updated sysgen program 6/82'
; System generation program
VERS	equ	30		;version x.x for CP/M x.x
;
;**********************************************************
;*							  *
;*							  *
;*		Copysys source code			  *
;*							  *
;*							  *
;**********************************************************
;
FALSE	equ	0
TRUE	equ	not FALSE
;
;
NSECTS	equ	26		;no. of sectors
NTRKS	equ	2		;no. of systems tracks
NDISKS	equ	4		;no. of disks drives
SECSIZ	equ	128		;size of sector
LOG2SEC	equ	7		;LOG2 128
SKEW	equ	2		;skew sector factor
;
FCB	equ	005Ch		;location of FCB
FCBCR	equ	FCB+32		;current record location
TPA	equ	0100h		;Transient Program Area
LOADP	equ	1000h		;LOAD Point for system
BDOS	equ	05h		;DOS entry point
BOOT	equ	00h		;reboot for system
CONI	equ	1h		;console input function
CONO	equ	2h		;console output function
SELD	equ	14		;select a disk
OPENF	equ	15 		;disk open function
CLOSEF	equ	16		;open a file
DWRITF	equ	21		;Write func
MAKEF	equ	22		;mae a file
DELTEF 	equ	19		;delete a file
DREADF	equ	20		;disk read function
DRBIOS	equ	50		;Direct BIOS call function
EIGHTY	equ	080h		;value of 80
CTLC	equ	'C'-'@'		;ConTroL C
Y	equ	89		;ASCII value of Y
;
MAXTRY	equ	01		;maximum number of tries
CR	equ	0Dh		;Carriage Return
LF	equ	0Ah		;Line Feed
STACKSIZE equ	016h		;size of local stack
;
WBOOT	equ	01		;address of warm boot
;
SELDSK	equ	9		;Bios func #9 SELect DiSK
SETTRK	equ	10		;BIOS func #10 SET TRacK
SETSEC	equ	11		;BIOS func #11 SET SECtor
SETDMA	equ	12		;BIOS func #12 SET DMA address
READF	equ	13		;BIOS func #13 READ selected sector
WRITF	equ	14		;BIOS func #14 WRITe selected sector

;
	org	TPA		;Transient Program Area
	jmp	START
	dw	0,0,0,0,0,0,0,0
	dw	0,0,0,0,0,0,0,0
	dw	0,0,0,0,0,0,0,0
	dw	0,0,0,0,0,0,0,0
	dw	0,0,0,0,0
	db	0,0,0
	db	'COPYRIGHT 1982, '
	db	'DIGITAL RESEARCH'
	db	'151282'
	db	0,0,0,0
	db	'654321'
;
; Translate table-sector numbers are translated here to decrease
; the systen tie for missed sectors when slow controllers are
; involved.  Translate takes place according to the "SKEW" factor
; set above.
;
OST:	db	NTRKS		;operating system tracks
SPT:	db	NSECTS		;sectors per track
TRAN:
TRELT	set	1
TRBASE	set	1
	rept	NSECTS
	db	TRELT		;generate first/next sector
TRELT	set	TRELT+SKEW
	if	TRELT gt NSECTS
TRBASE	set	TRBASE+1
TRELT	set	TRBASE
	endif
	endm
;
; Now leave space for extensions to translate table
;
	if	NSECTS lt 64
	rept	64-NSECTS
	db	0
	endm
	endif
;
; Utility subroutines
;
MLTBY3:
;multiply the contents of regE to get jmp address
	mov	a,e		;Acc = E
	sui	1
	mov	e,a		;get ready for multiply
	add	e
	add	e
	mov	e,a
	ret			;back at it
;
SEL:
	sta	TEMP
	lda	V3FLG
	cpi	TRUE
	lda	TEMP
	jnz	SEL2
;
	sta	CREG		;CREG = selected register
	lxi	h,0000h
	shld	EREG		;for first time

	mvi	a,SELDSK
	sta	BIOSFC		;store it in func space
	mvi	c,DRBIOS
	lxi	d,BIOSPB
	jmp	BDOS
SEL2:
	mov	c,a
	lhld	WBOOT
	lxi	d,SELDSK
	call	MLTBY3
	dad	d
	pchl
;
TRK:
; Set up track
	sta	TEMP
	lda	V3FLG
	cpi	TRUE
	lda	TEMP
	jnz	TRK2

;
	mvi	a,00h
	sta	BREG		;zero out B register
	mov	a,c		;Acc = track #
	sta	CREG		;set up PB
	mvi	a,SETTRK	;settrk func #
	sta	BIOSFC
	mvi	c,DRBIOS
	lxi	d,BIOSPB
	jmp	BDOS
TRK2:
	lhld	WBOOT
	lxi	d,SETTRK
	call	MLTBY3
	dad	d
	pchl			;gone to set track
;
SEC:
; Set up sector number
	sta	TEMP
	lda	V3FLG
	cpi	TRUE
	lda	TEMP
	jnz	SEC2
;
	mvi	a,00h
	sta	BREG		;zero out BREG
	mov	a,c		; Acc = C
	sta	CREG		;CREG = sector #
	mvi	a,SETSEC
	sta	BIOSFC		;set up bios call
	mvi	c,DRBIOS
	lxi	d,BIOSPB
	jmp	BDOS
SEC2:
	lhld	WBOOT
	lxi	d,SETSEC
	call	MLTBY3
	dad	d
	pchl
;
DMA:
; Set DMA address to value of BC
	sta	TEMP
	lda	V3FLG
	cpi	TRUE
	lda	TEMP
	jnz	DMA2
;
	mov	a,b		;
	sta	BREG		;
	mov	a,c		;Set up the BC
	sta	CREG		;register pair
	mvi	a,SETDMA	;
	sta	BIOSFC		;set up bios #
	mvi	c,DRBIOS
	lxi	d,BIOSPB
	jmp	BDOS
DMA2:
	lhld	WBOOT
	lxi	d,SETDMA
	call	MLTBY3
	dad	d
	pchl
;
READ:
; Perform read operation
	sta	TEMP
	lda	V3FLG
	cpi	TRUE
	lda	TEMP
	jnz	READ2
;
	mvi	a,READF
	sta	BIOSFC
	mvi	c,DRBIOS
	lxi	d,BIOSPB
	jmp	BDOS
READ2:
	lhld	WBOOT
	lxi	d,READF
	call	MLTBY3
	dad	d
	pchl
;
WRITE:
; Perform write operation
	sta	TEMP
	lda	V3FLG
	cpi	TRUE
	lda	TEMP
	jnz	WRITE2
;
	mvi	a,WRITF
	sta	BIOSFC		;set up bios #
	mvi	c,DRBIOS
	lxi	d,BIOSPB
	jmp	BDOS
WRITE2:
	lhld	WBOOT
	lxi	d,WRITF
	call	MLTBY3
	dad	d
	pchl
;
MULTSEC:
; Multiply the sector # in rA by the sector size
	mov	l,a
	mvi	h,0		;sector in hl
	rept	LOG2SEC
	dad	h
	endm
	ret			;with HL - sector*sectorsize
;
GETCHAR:
; Read console character to rA
	mvi	c,CONI
	call	BDOS
; Convert to upper case
	cpi	'A' or 20h
	rc
	cpi	('Z' or 20h)+1
	rnc
	ani	05Fh
	ret
;
PUTCHAR:
; Write character from rA to console
	mov	e,a
	mvi	c,CONO
	call	BDOS
	ret
;
CRLF:
; Send Carriage Return, Line Feed
	mvi	a,CR
	call	PUTCHAR
	mvi	a,LF
	call	PUTCHAR
	ret
;

CRMSG:
; Print message addressed by the HL until zero with leading CRLF
	push	d
	call	CRLF
	pop	d		;drop through to OUTMSG
OUTMSG:
	mvi	c,9
	jmp	BDOS
;
SELCT:
; Select disk given by rA
	mvi	c,0Eh
	jmp	BDOS
;
DWRITE:
; Write for file copy
	mvi	c,DWRITF
	jmp	BDOS
;
DREAD:
; Disk read function
	mvi	c,DREADF
	jmp	BDOS
;
OPEN:
; File open function
	mvi	c,OPENF
	jmp	BDOS
;
CLOSE:
	mvi	c,CLOSEF
	jmp	BDOS
;
MAKE:
	mvi	c,MAKEF
	jmp	BDOS
;
DELETE:	
	mvi	c,DELTEF
	jmp	BDOS
;
;
;
DSTDMA:
	mvi	c,26
	jmp	BDOS
;
SOURCE:
	lxi	d,GETPRM	;ask user for source drive
	call	CRMSG
	call	GETCHAR		;obtain response
	cpi	CR		;is it CR?
	jz	DFLTDR		;skip if CR only
	cpi	CTLC		;isit ^C?
	jz	REBOOT
;
	sui	'A'		;normalize drive #
	cpi	NDISKS		;valid drive?
	jc	GETC		;skip to GETC if so
;
; Invalid drive
	call	BADDISK		;tell user bad drive
	jmp	SOURCE		;try again
;
GETC:
; Select disk given by Acc.
	adi	'A'
	sta	GDISK		;store source disk
	sui	'A'
	mov	e,a		;move disk into E for select func
	call	SEL		;select the disk
	jmp	GETVER
;
DFLTDR:
	mvi	c,25		;func 25 for current disk
	call	BDOS		;get curdsk
	adi	'A'
	sta	GDISK
	call	CRLF
	lxi	d,VERGET
	call	OUTMSG
	jmp	VERCR
;
GETVER:	
; Getsys set r/w to read and get the system
	call	CRLF
	lxi	d,VERGET	;verify source disk
	call	OUTMSG
VERCR:	call	GETCHAR
	cpi	CR
	jnz	REBOOT		;jmp only if not verified
	call	CRLF
	ret
;
DESTIN:
	lxi	d,PUTPRM	;address of message
	call	CRMSG		;print it
	call	GETCHAR		;get answer
	cpi	CR
	jz	REBOOT		;all done
	sui	'A'
	cpi	NDISKS			;valid disk
	jc	PUTC
;
; Invalid drive
	call	BADDISK		;tell user bad drive
	jmp	PUTSYS		;to try again
;
PUTC:
; Set disk fron rA
	adi	'A'
	sta	PDISK		;message sent
	sui	'A'
	mov	e,a		;disk # in E
	call	SEL		;select destination drive
; Put system, set r/w to write
	lxi	d,VERPUT	;verify dest prmpt
	call	CRMSG		;print it out
	call	GETCHAR		;retrieve answer
	cpi	CR	
	jnz	REBOOT		;exit to system if error
	call	CRLF
	ret
;
;
GETPUT:
; Get or put CP/M (rw = 0 for read, 1 for write)
; disk is already selected
	lxi	h,LOADP		;load point in RAM for DMA address
	shld	DMADDR
;
;
;

;
; Clear track 00
	mvi	a,-1		;
	sta	TRACK
;
RWTRK: 
; Read or write next track
	lxi	h,TRACK
	inr	m		;track = track+1
	lda	OST		;# of OS tracks
	cmp	m		;=track # ?
	jz	ENDRW		;end of read/write
;
; Otherwise not done
	mov	c,m		;track number
	call	TRK		;set to track
	mvi	a,-1		;counts 0,1,2,...,25
	sta	SECTOR
;
RWSEC:
; Read or write a sector
	lda	SPT		;sectors per track
	lxi	h,SECTOR	
	inr	m		;set to next sector
	cmp	m		;A=26 and M=0,1,..,25
	jz	ENDTRK
;
; Read or write sector to or from current DMA address
	lxi	h,SECTOR
	mov	e,m		;sector number
	mvi	d,0		;to DE
	lxi	h,TRAN	
	mov	b,m		;tran(0) in B
	dad	d		;sector translated
	mov	c,m		;value to C ready for select
	push	b		;save tran(0)
	call 	SEC
	pop	b		;recall tran(0),tran(sector)
	mov	a,c		;tran(sector)
	sub	b		;--tran(sector)
	call	MULTSEC		;*sector size
	xchg			;to DE
	lhld	DMADDR		;base DMA
	dad	d
	mov	b,h
	mov	c,l		;to set BC for SEC call
	call	DMA		;dma address set from BC
	xra	a
	sta	RETRY		;to set zero retries
;
TRYSEC:
; Try to read or write current sector
	lda	RETRY
	cpi	MAXTRY
	jc	TRYOK
;
; Past MAXTRY, message and ignore
	lxi	d,ERRMSG
	call	OUTMSG
	call	GETCHAR
	cpi	CR
	jnz	REBOOT
;
; Typed a CR, ok to ignore
	call	CRLF
	jmp	RWSEC
;
TRYOK:
; Ok to tyr read write
	inr	a
	sta	RETRY	
	lda	RW
	ora	a
	jz	TRYREAD
;
; Must be write
	call	WRITE
	jmp	CHKRW
TRYREAD:
	call	READ
CHKRW:
	ora	a
	jz	RWSEC		;zero flag if read/write ok
;
;Error, retry operation
	jmp	TRYSEC
;
; End of track
ENDTRK:
	lda	SPT		;sectors per track
	call	MULTSEC		;*secsize
	xchg			; to DE
	lhld	DMADDR		;base dma for this track
	dad	d		;+spt*secsize
	shld	DMADDR		;ready for next track
	jmp	RWTRK		;for another track
;
ENDRW:
; End of read or write
	ret
;
;*******************
;*
;*	MAIN ROUTINE
;*
;*
;*******************
;
START:

	lxi	sp,STACK
	lxi	d,SIGNON
	call	OUTMSG
;
;get version number to check compatability
	mvi	c,12		;version check
	call	BDOS
	mov	a,l		;version in Acc
	cpi	30h		;version 3 or newer?
	jc	OLDRVR		;
	mvi	a,TRUE
	sta	V3FLG		;
	jmp	FCBCHK
OLDRVR:	
	mvi	a,FALSE
	sta	V3FLG
;

; Check for default file liad instead of get
FCBCHK:	lda	FCB+1		;blank if no file
	cpi	' '
	jz	GETSYS		;skip to system message
	lxi	d,FCB		;try to open it
	call	OPEN
	inr	a		;255 becomes 00
	jnz	RDOK
;
; File not present
	lxi	d,NOFILE
	call	CRMSG
	jmp	REBOOT
;
;file present
RDOK:
	xra	a
	sta	FCBCR		;current record = 0
	lxi	h,LOADP
RDINP:
	push	h
	mov	b,h
	mov	c,l
	call	DMA		;DMA address set
	lxi	d,FCB		;ready fr read
	call	DREAD
	pop	h		;recall
	ora	a		;00 if read ok
	jnz	PUTSYS		;assume eof if not
; More to read continue
	lxi	d,SECSIZ 
	dad	d		;HL is new load address
	jmp	RDINP
;
GETSYS:
	call	SOURCE		;find out source drive
;
	xra	a		;zero out a
	sta	RW		;RW = 0 to signify read
	call	GETPUT		;get or read system
	lxi	d,DONE		;end message of get or read func
	call	OUTMSG		;print it out
;
; Put the system
PUTSYS:
	call	DESTIN		;get dest drive
;
	lxi	h,RW		;load address
	mvi	m,1
	call	GETPUT		;to put system back on disk
	lxi	d,DONE
	call	OUTMSG		;print out end prompt
;
;	FILE COPY FOR CPM.SYS
;
CPYCPM:
; Prompt the user for the source of CP/M3.SYS
;
	lxi	d,CPYMSG	;print copys prompt
	call	CRMSG		;print it
	call	GETCHAR		;obtain reply
	cpi	Y		;is it yes?
	jnz	REBOOT		;if not exit
				;else
;
;
	mvi	c,13		;func # for reset
	call	BDOS		;
	inr	a

	lxi	d,ERRMSG
	cz	FINIS
;
	call	SOURCE		;get source disk for CPM3.SYS
CNTNUE:
	lda	GDISK		;Acc = source disk
	sui	'A'
	mvi	d,00h
	mov	e,a		;DE = selected disk
	call	SELCT
; now copy the FCBs
	mvi	c,36		;for copy
	lxi	d,SFCB		;source file
	lxi	h,DFCB		;destination file
MFCB:

	ldax	d
	inx	d		;ready next
	mov	m,a
	inx	h		;ready next dest
	dcr	c		;decrement coun
	jnz	MFCB
;
	lda	GDISK		;Acc = source disk
	sui	40h		;correct disk
	lxi	h,SFCB
	mov	m,a		;SFCB has source disk #
	lda 	PDISK		;get the dest. disk
	lxi	h,DFCB		;
	sui	040h		;normalize disk
	mov	m,a
;
	xra	a		;zero out a
	sta	DFCBCR		;current rec = 0
;
; Source and destination fcb's ready
;
	lxi	d,SFCB		;
	call	OPEN		;open the file
	lxi	d,NOFILE	;error messg
	inr	a		;255 becomes 0
	cz	FINIS		;done if no file
;
; Source file is present and open
	lxi	d,LOADP		;get DMA address
	xchg			;move address to HL regs
	shld	BEGIN		;save for begin of write
;
	lda	BEGIN		;get low byte of
	mov	l,a		;DMA address into L
	lda	BEGIN+1		;
	mov	h,a		;into H also
COPY1:
	xchg			;DE = address of DMA
	call	DSTDMA		;
;
	lxi	d,SFCB		;
	call	DREAD		;read next record
	ora	a		;end of file?
	jnz	EOF		;skip write if so
;
	lda	CRNREC
	inr	a		;bump it
	sta	CRNREC
;
	lda	BEGIN
	mov	l,a
	lda	BEGIN+1
	mov	h,a
	lxi	d,EIGHTY
	dad	d		;add eighty to begin address
	shld	BEGIN
	jmp	COPY1		;loop until EOF
;
EOF:
	lxi	d,DONE
	call	OUTMSG
;
COPY2:
	call	DESTIN		;get destination drive for CPM3.SYS
	lxi	d,DFCB		;set up dest FCB
	xchg	
	lda	PDISK
	sui	040h		;normalize disk
	mov	m,a		;correct disk for dest
	xchg			;DE = DFCB
	call	DELETE		;delete file if there
;
	lxi	d,DFCB		;
	call	MAKE		;make a new one
	lxi	d,NODIR
	inr	a		;check directory space
	cz	FINIS		;end if none
;
	lxi	d,LOADP
	xchg
	shld	BEGIN
;
	lda	BEGIN
	mov	l,a
	lda	BEGIN+1
	mov	h,a
LOOP2:
	xchg
	call	DSTDMA
	lxi	d,DFCB
	call	DWRITE
	lxi	d,FSPACE
	ora	a
	cnz	FINIS
	lda	CRNREC
	dcr	a
	sta	CRNREC
	cpi	0
	jz	FNLMSG
	lda	BEGIN
	mov	l,a
	lda	BEGIN+1
	mov	h,a
	lxi	d,EIGHTY
	dad	d
	shld	BEGIN
	jmp	LOOP2
; Copy operation complete
FNLMSG:
	lxi	d,DFCB
	mvi	c,CLOSEF
	call	BDOS
;
	lxi	d,DONE
;
FINIS:
; Write message given by DE, reboot
	call	OUTMSG
;
REBOOT:
	mvi	c,13
	call	BDOS
	call	CRLF
	jmp	BOOT
;
BADDISK:
	lxi	d,QDISK
	call	CRMSG
	ret
;****************************
;*
;*
;*	DATA STRUCTURES     
;*
;*
;****************************
;
BIOSPB:
; BIOS Parameter Block
BIOSFC:	db	0		;BIOS function number
AREG:	db	0		;A register contents
CREG:	db	0		;C register contents
BREG:	db	0		;B register contents
EREG:	db	0		;E register contents
DREG:	db	0		;D register contents
HLREG:	dw	0		;HL register contents
;
SFCB:
DR:	ds	1
F1F8:	db	'CPM3    '
T1T3:	db	'SYS'
EXT:	db	0
CS:	db	0
RS:	db	0
RCC:	db	0
D0D15:	ds	16
CCR:	db	0
R0R2:	ds	3
;
DFCB:	ds	36
DFCBCR	equ	DFCB+32
;
;
V3FLG:	db	0		;flag for version #
TEMP:	db	0
SDISK:	ds	1		;selected disk
BEGIN:	dw	0
DFLAG:	db	0
TRACK:	ds	1		;current track
CRNREC:	db	0		;current rec count
SECTOR:	ds	1		;current sector
RW:	ds	1		;read if 0 write if 1
DMADDR:	ds	2		;current DMA address
RETRY:	ds	1		;number of tries on this sector
SIGNON:	db	'CP/M 3 COPYSYS - Version '
	db	VERS/10+'0','.',VERS mod 10 +'0'
	db	'$'
GETPRM:	db	'Source drive name (or return for default) $'
VERGET:	db	'Source on '
GDISK:	ds	1
	db	' then type return $'
PUTPRM:	db	'Destination drive name (or return to reboot) $'
VERPUT:	db	'Destination on '
PDISK:	ds	1
	db	' then type return $'
CPYMSG:	db	'Do you wish to copy CPM3.SYS? $'
DONE:	db	'Function complete$'
;
; Error messages......
;
QDISK:	db	'ERROR: Invalid drive name (Use A, B, C, or D)$'
NOFILE:	db	'ERROR: No source file on disk.$'
NODIR:	db	'ERROR: No directory space.$'
FSPACE:	db	'ERROR: Out of data space.$'
WRPROT:	db	'ERROR: Write protected?$'
ERRMSG: db	'ERROR: Possible incompatible disk format.'
	db	CR,LF,' Type return to ignore.$' 
CLSERR:	db	'ERROR: Close operation failed.$'
;
	ds	STACKSIZE * 3
STACK:
	end
