	.title	'SYSTEM SERIALIZATION PROGRAM FOR DSC3/4'
	.sbttl	'NEWSER'
version	==	3
revision==	1
	.pabs
	.phex
	.loc	100h
;-------------------------------------
; CP/M serialization program
;
; Serial the diskette in drive B (i.e., put a serial
; number in all copies of CP/M on the diskette).
;
cr	=	0Dh	; define carriage return
lf	=	0Ah	; define line feed
sysstrt =	8900h	; start memory loc of system
sLOC1	=	8D0Ah	; loc of 1st serial number
sLOC2	=	9200h	; loc of 2nd serial number
CPM2s1	=	8D28h	; serial number in CP/M 2.2
CPM2s2	=	9200h	; serial number in CP/M 2.2
CPMbuf	=	0Ah	; CP/M console buffer func.
BDOS	=	05	; CP/M entry point.
cpmdsk	=	1	; drive containing CP/M
;
	jmp	start
;
; Console buffer (used to read serial number)
conbuf:	.byte	8,0	
	.byte	20h,20h,20h,20h,20h,20h,20h,20h
;
; Bytes for clearing the above CP/M buffer.
clrbyts:.byte	8,0
	.byte	20h,20h,20h,20h,20h,20h,20h,20h
;
; The serial number is stored here
serialnum:
	.byte	01,00	    ; these two bytes mean DMS
	.byte	00,00,00,00 ; these are filled by user
;
; System flag: 0 if CP/M 1.4, non-zero if CP/M 2.2
sysflg:	.byte	0
;----------
; Main Program
;
; Initialize
start:
	lxi	SP,stack  ; define the stack
	lxi	H,initMSG ; print initial message
	call	prtmsg
	mvi	C,12	; get version number
	call	BDOS
	mov	A,L	; store it away
	sta	sysflg
;
; Tell user to put CP/M source in B and type RETURN.
; Select drive B and read CP/M into memory starting
; at 900h.
getCPM:
	lxi	H,putMSG  ; print 'put disk in B' msg
	call	prtmsg
	call	waitcr	  ; wait for a car ret
	mvi	C,cpmdsk
	call	SELDSK
	lxi	H,READF	  ; read the system into memory
	shld	IOdir
	call	RWSYS
	lxi	H,crlf    ; space down a line
	call	prtmsg
;
; Print the current serial number of CP/M in memory
prtSERIAL:
	lxi	H,curMSG
	call	prtmsg
	lxi	H,sLOC1+2 ; serial number for CP/M 1.4
	lda	sysflg
	ora	A
	jrz	..ok
	lxi	H,CPM2s1+2; serial number for CP/M 2.2
..ok:	lxi	D,conbuf+2
	mvi	A,4
..1:	push	PSW
	mov	A,M
	adi	'0'
	xchg
	mov	M,A
	xchg
	inx	D	; next console buffer address
	inx	H	; next serial number address
	pop	PSW
	dcr	A
	jrnz	..1
	lxi	H,conbuf+2
	call	prtmsg	; print current serial number.
	lxi	H,crlf
	call	prtmsg	; space down a line.
;----------
; MAIN SERIALIZATION LOOP
;----------
;
; Get the serial number (4 digits) from the user
getSERIAL:
	lxi	H,SERmsg;Print 'serial number - ' msg
	call	prtmsg
	mvi	C,CPMbuf; CP/M console buffer function
	lxi	D,conbuf; DE = console buffer address
	call	BDOS	; Call CP/M
	lxi	H,conbuf+1 ; addr of entry length
	mov	A,M	; A = length of users entry.
	cpi	0	; Did user only enter a cr?
	jz	exit	; Yes. Exit gracefully.
	cpi	4	; Is serial number 4 chrs?
	jnz	serERR	; No. Abort
;
; Load the serial number from the console buffer
; into the serial number address.
	lxi	H,conbuf+2
	lxi	D,serialnum+2
	mvi	A,4
..2:	push	PSW
	mov	A,M
	cpi	'9'+1	; Is entry a number?
	jnc	serERR	; No. Abort
	sui	'0'	; Yes. Make ascii chr numeric
	xchg
	mov	M,A	; and load it into ser num addr
	xchg
	inx	D	; next console buffer address
	inx	H	; next serial number address
	pop	PSW
	dcr	A
	cpi	0
	jrnz	..2
;
; Valid serial number received.  Put the serial number
; into the two serial number memory locations.
putSERIAL:
	call	overlay
	lxi	H,clrbyts
	lxi	D,conbuf
	lxi	B,10
	ldir		; Clear the console buffer
	lxi	H,crlf
	call	prtmsg	; and space down.
;
; Select drive B and wait for a carriage return.
; Write the serialized CP/M and O/S to the disk.
..3:	mvi	C,cpmdsk
	call	SELDSK
	lxi	H,iniMSG  ; Print 'type return' msg
	call	prtmsg
	call	waitcr	  ; wait for a cr
	jrnz	..3	  ; if not cr then ask again
	lxi	H,WRITF   ; write system from memory
	shld	IOdir
	call	RWSYS
	lxi	H,donMSG  ; print function complete
	call	prtmsg
;
; Serialize all files containing CP/M
	lxi	B,80h
	call	SETBYT	; restore DMA length
	mvi	C,0Dh	; reset disk system
	call	BDOS
	lda	sysflg
	ora	A
	jrnz	..CPM2
	lxi	H,sysstrt+100h
	lxi	D,HMASTER
	call	SERUM
	lxi	H,sysstrt+100h
	lxi	D,HSTATION
	call	SERUM
	lxi	H,sysstrt+100h
	lxi	D,HARDCPM
	call	SERUM
	lxi	H,sysstrt-800h
	lxi	D,FMASTER
	call	SERUM
	lxi	H,sysstrt-800h
	lxi	D,FSTATION
	call	SERUM
	jmp	getCPM
..CPM2:
	lxi	H,sysstrt+100h
	lxi	D,HARD2CPM
	call	SERUM
;
; We're done with this diskette!
	jmp	getCPM ; do the next diskette
;----------
; Files to be serialized:
;
HMASTER:.ascii	'HMASTER COM'
HSTATION:.ascii	'HSTATIONCOM'
HARDCPM:.ascii	'HARDCPM COM'
HARD2CPM:.ascii	'HARDCPM2COM'
FMASTER:.ascii	'FMASTER COM'
FSTATION:.ascii 'FSTATIONCOM'
;----------
; Routine:	SERUM
;
; Regs in:  HL = DMA address
; 	    DE = file name address
;
; Serialize a CP/M disk file
SERUM:
	shld	DMAstart
	sded	FILEadr
	lxi	H,0
	shld	DMAend
	mvi	C,14h	; read sequential
	call	RorW
	jrz	..noser
	call	overlay
	lhld	DMAadr
	shld	DMAend
	mvi	C,15h	; write sequential
	call	RorW
	lxi	D,FCB
	mvi	C,10h	; close file
	call	BDOS
	sub	A
	sta	FCB+9
	lxi	H,FCB+1
	call	PRTMSG
	lxi	H,yesSER
	call	PRTMSG
	ret
..noser:
	sub	A
	sta	FCB+9
	lxi	H,FCB+1
	call	PRTMSG
	lxi	H,noSER
	call	PRTMSG
	ret
RW:	.word	0
FILEadr:.word	0
DMAadr:	.word	0
DMAstart:.word	0
DMAend:	.word	0
FCB:	.blkb	36
;---------
; Routine:	Read or write a file
;
RorW:
	sbcd	RW	; read or write code
	mvi	E,cpmdsk
	mvi	C,0EH	; select disk
	call	BDOS
	lxi	H,FCB
	mvi	M,0
	lxi	D,FCB+1
	lxi	B,35
	ldir		; zero out the FCB
	lhld	FILEadr
	lxi	D,FCB+1
	lxi	B,11
	ldir		; store file name in FCB
	lxi	D,FCB
	mvi	C,0FH	; open file
	call	BDOS
	inr	A
	rz		; return if file not exist
;
; Read or write entire file
	lhld	DMAstart
..nxtrec:
	shld	DMAadr
	xchg
	mvi	C,1Ah	; set DMA address
	call	BDOS
	lxi	D,FCB
	lbcd	RW	; read/write sequential
	call	BDOS
	ora	A
	rnz		; return if end-of-file
	lhld	DMAadr
	lxi	D,128
	dad	D
	lded	DMAend
	dsbc	D
	dad	D
	jrnz	..nxtrec
	ret		; return if entire file written
;----------
; Routine:	overlay
;
; Write new serial number over old
overlay:
	lda	sysflg
	ora	A
	jrnz	..CPM2
	lxi	H,serialnum	
	lxi	D,sLOC1
	lxi	B,6
	ldir		; Copy ser num into 1st ser loc
	lxi	H,serialnum
	lxi	D,sLOC2
	lxi	B,6
	ldir		; Copy ser num into 2nd ser loc
	ret
..CPM2:
	lxi	H,serialnum
	lxi	D,CPM2s1
	lxi	B,6
	ldir		; Copy ser num into 2nd ser loc
	lxi	H,serialnum
	lxi	D,CPM2s2
	lxi	B,6
	ldir		; Copy ser num into 2nd ser loc
	ret
;----------
; Routine: 	serERR
; Regs  in:	none
; Regs out:	none
;
; Inform user of his 'serial number' error and boot.
serERR:
	lxi	H,badSERmsg
	call	prtmsg	
	jmp	exit	   ; warm boot
;----------
; Routine: 	exit
;
; Gracefully exit the program
exit:
	lxi	H,exitMSG ; print an exit message
	call	prtmsg
	jmp	0	  ; Warm boot
;----------
; Routine:	waitcr
;
; Wait for a carriage return from the console.
waitcr:
	call	CONIN	; get a char
	cpi	3	; 3 is Control-C
	jz	exit	; warm boot if control-C
	cpi	cr	; is char a carriage return?
	push	PSW	; Yes.
	lxi	H,crlf  ; print crlf
	call	prtmsg
	pop	PSW
	ret
;----------
; Print a message 
prtmsg:
	mov	A,M	; get next char
	ora	A
	rz		; return if zero
	push	H	; save HL
	mov	C,A	; output the char
	call	CONOUT
	pop	H	; restore HL
	inx	H	; increment to next char
	jmpr	prtmsg	; print it
;----------
; Read or Write the system (depending on IOdir)
RWSYS:
	call	HOME	; Home to track 0
	lxi	B,sysstrt ; load system start address
	call	SETDMA
	mvi	C,1	; start at sector 1
	call	SETSEC
	lxi	B,26*128 ; transfer entire track
	call	SETBYT
	call	RDWR	; read or write data
	mvi	C,1	; go to track 1
	call	SETTRK
	lxi	B,sysstrt+26*128 ; load address is 26
	call	SETDMA	; sectors later
	mvi	C,1	; start at sector 1
	call	SETSEC
	call	CPMMAP	; determine density
	lxi	B,52*128 ; assume double
	ani	0C0h	; mask out for density
	cpi	0	; check for single density
	jrnz	..4
	lxi	B,26*128 ; if single load 26 sectors
..4:	call	SETBYT
	call	RDWR	; transfer the data
	ret
;----------
; Call bios directly using WBOOT in low memory
WBOOT	=	1
CONIN:
	lhld	WBOOT
	lxi	D,06h
	dad	D
	pchl
CONOUT:
	lhld	WBOOT
	lxi	D,09h
	dad	D
	pchl
HOME:
	lhld	WBOOT
	lxi	D,15h
	dad	D
	pchl
SELDSK:
	lhld	WBOOT
	lxi	D,18h
	dad	D
	pchl
SETTRK:
	lhld	WBOOT
	lxi	D,1Bh
	dad	D
	pchl
SETSEC:
	lhld	WBOOT
	lxi	D,1Eh
	dad	D
	pchl
SETDMA:
	lhld	WBOOT
	lxi	D,21h
	dad	D
	pchl
RDWR:
	lhld	WBOOT
	lded	IOdir
	dad	D
	pchl
IOdir:	.word	0
READF	=	24h
WRITF	=	27h
CPMMAP:
	lhld	WBOOT
	lxi	D,60h
	dad	D
	pchl
SETBYT:
	lhld	WBOOT
	lxi	D,66h
	dad	D
	pchl
;----------
; Messages
;
initMSG:
	.ascii	[cr][lf]'CP/M Serialization Program '
	.byte	version+'0','.'
	.byte	revision/10+'0',revision@10+'0'
crlf:	.asciz	[cr][lf]
putMSG:	.ascii	[cr][lf]'Put Diskette to be serialized'
	.asciz	' in drive B, and type RETURN. '
CURmsg:	.asciz	'Current CP/M serial number is '
SERmsg:	.ascii	[cr][lf]'Enter serial number '
	.asciz	'(or RETURN to exit) - '
iniMSG:	.ascii	[cr][lf]'Type RETURN to serialize '
	.asciz	'the diskette. '
BadSERmsg:
	.asciz	[cr][lf]'Invalid serial number entered.'
donMSG: .asciz	[cr][lf]'Diskette serialized.'[cr][lf]
yesSER:	.asciz	' serialized.'[cr][lf]
noSER:	.asciz	' not found.'[cr][lf]
exitMSG:.asciz	[cr][lf]'Exiting NEWSER.'
	.blkb	30
stack	=	.
	.end
