	.title 'Read and write the floppy system tracks'
	.ident	SYSGEN
	.sbttl	'Table of contents'
version	==	2
revision==	6	;Last revised 06/14/83 D. Stein
patch	==	' '
	.pabs
	.phex
	.loc	100h
;
; Table of Contents				1
; Program description				2
; Update history				3
; Equates					4
; Get users requests				5
; Beginning of Subroutines			7
;	Get and test users drive		7
; 	Ask for CCP buffer input		9
; 	Get users command string		10
; 	CRT I/O subroutines			12
; 	Read or write 5 in. disks		14
; 	Read or write 8 in. disks		15
; 	BIOS calls				16
; Storage					17
; CRT messages					18
.page
.sbttl	'Program despription'
;
;     CP/M system generation program
;
; Sysgen reads the operating system from the operating
; system tracks into memory.  The user can save the com 
; file with the command "save 47 cpmnew.com".  This 
; file can be loaded using DDT or ZDTI to modify it.
; The modified com file can then be loaded back on 
; the operating system tracks with sysgen.  
;
; Sysgen can also be used to load a new operating 
; system on the operating system tracks.
;
; Memory mapping for CP/M 2.2 is as follows:
;
;  0  FF 100 8FF 900 9FF A00 11FF 1200 1FFF 2000 2FFF
; |-----|-------|-------|--------|---------|--------|
; |     |       |boot   | CCP    | BDOS    | BIOS   |
; |     |sysgen |loader | 2.2    | 2.2     | 2.2    |
; |-------------------------------------------------|
;
; Disk mapping for CP/M 2.2 on an 8" diskette is:
;
; track 0 (single density)
; Sectors 1-2  Loader (1 page)
; Sectors 3-18 CCP    (8 pages)
; Sectors 19-26 BDOS  (4 pages)
;
; Track 1 (double density)
;
; Sectors 1-10 BDOS   (10 pages) continued
; Sectors 11-26 BIOS  (16 pages)
;
; Disk mapping for CP/M 2.2 on a 5" diskette is:
;
; track 0 (double density)
; Sectors 1    Loader (1 page)
; Sectors 2-9  CCP    (8 pages)
; Sectors 10-16 BDOS  (7 pages)
;
; Track 1 (double density)
;
; Sectors 1-7  BDOS   (7  pages) continued
;
; track 2 (double density)
;
» Sectoró 1-16 BIOÓ  (1¶ pages)
.page
.sbttl	'Update history'
; Revision 1:  	Add documentation for memory mapping.
;
; Ver 2.0  	Changes for CPMMAP added.  Error
;		checking added to make sure that the
;		selected drive is indeed a floppy.
;			- 01JUN82MdG
;
; Ver 2.0A	Minor things corrected. Replaced
;		occurrences of sbi with sui.
;
; Ver 2.3       User can optionally stuff the
;		CCP's execute-upon-startup buffer.
;					D. Stein 3/83
; 2.4 4/20/83	Recombine code so 5 and 8 inch use
;		same sysgen.	DRB
; 'A' 5/20/83	Add code to clean up a few routines
;		that stop garbage on screen.  DRB
; 2.5 05/23/83	The routine in getdsk that checks for
;		correct drive select did not work. The
;		Accum was not reset if user asked for
;		an incorrect drive.  Message added to
;		tell user and sent back.  DRB
; 2.6 06/13/83  Control characters are now accepted as
;		input into the CCP command buffer.
;		Changed getcmdstrg and getdsk to abort
;		on an initial ^C.  Changed messages.
;		Removed extraneous code from getdsk.
;		If user tries to use a command line
;		for file input to the program, an error
;		message is printed.
;					D. Stein
.page
.sbttl	'Equates'
;
; Low memory Equates
;
CPMboot	==	0	; Warm boot addr
WBOOT	==      1	; addr for jump table
BDOS	==	5
comlength==	80h	; Length of CP/M command line
;
; ASCII Equates

cr	==	0Dh
lf	==	0Ah
tab	==	09h
ctrlC	==	03h
ctrlZ	==	1Ah
BELL	==	07h
BACKSP	==	08h
ESC	==	1Bh
DEL	==	07Fh	; last valid ASCII char
uparow	==	5Eh	; ASCII up arrow
period	==	2Eh	; ASCII period
SPACE	==	20h
;
; High memory Equates

CCPlen	==	0A00h+7	; Holds length of CCP buffer
CCPbuf	==	0A00h+8	; Loc of cold boot buffer
maxlen	==	127	; max chars in CCP buffer
sysstrt ==	900h	; start memory loc of system
BIOSSTART ==	2000h	
;
; BIOS Equates
;
READF	==	24h	; Add to warmboot addr for
WRITF	==	27h	; Read and Write calls to BIOS
;
; Device types
DD	==	1<5
MINI2	==	5<5
.page
.sbttl	'Get users requests'
	ei		  ; enable interrupts
	lxi	SP,stack  ; define the stack
	lxi	H,logMSG  ; print log in message
	call	prtmsg
;----
; Digital Research's version of SYSGEN handles a
;file name on the command line.  The file is used
;as the source of the op sys.  DMS SYSGEN does not
;have this function, and if a command line has been
;entered, an error message is printed.  Ver 2.6
	lda	comlength ; Get CP/M com length
	cpi	2	  ; Did user give file name?
	jnc	NOfile	  ; Yes. Not handled.

..0:	lxi	H,srcQST  ; print source question
	call	getdsk	  ; get result disk
	jrz	..1	  ; if cr then skip source

	sta	srcdsk
	lxi	H,srcMSG  ; print verify message
	call	prtmsg
	call	waitcr	  ; wait for a cr
	jrnz	..0	  ; if not cr then reask

	lxi	H,READF	  ; read the system into memory
	shld	IOdir
	lda	medtype	  ; If media type is DD, it
	cpi	DD	  ; is 8 inch disk
	cz	RW8in
	jrz	..don	  ; and we are done

	call	RW5in	  ; ELSE, 5 inch, do it
..don:	lxi	H,donMSG  ; done, do it again
	call	prtmsg

; Let user optionally stuff the CCP`s cold boot buffer
;
..1:	call	askcmdstrg ; Print out current CCP buf
			  ; and ask for four choices.
	call	clearbuf  ; Zero CP/M buf area.
..ask:	call	CONIN	  ; Fetch a chr.
	cpi	ctrlC	  ;  If	user wants to abort,
	jz	exit	  ;	then exit.
	cpi	cr	  ;  If user wants no change,
	jrz	writsys	  ;  	then skip all this.
	cpi	ESC	  ;  If user wants entry delete
	jrz	..entdel  ;	then copy empty buffer.
	ani	0DFh	  ;  Make entry upper case.
	cpi	'C'	  ;  If not a new entry request,
	jrnz	..ask	  ;	then error.  Ask again.
	mov	C,A	  ;   ELSE process entry input.
	call	CONOUT	  ; Echo the chr.
	call	getcmdstrg; Put users new buf in conbuf
			  ; A returns with buf length.
	ora	A	  ; User just entered a CR?
	jrnz	..bufswap ; No. Copy in his entry.
			  ; Yes. Fall thru to delete.
..entdel:
	lxi	H,DELmsg  ; Yes.  ESC means kill CCPbuf.
	call	prtmsg	  ; Tell him 'line deleted'.
	sub	A	  ; Use 0 length for CCP buf.
..bufswap:
	sta	CCPlen	    ; Save combuf length
	call	cvtUP	    ; Make the buf upper case.
	lxi	H,conbuf
	lxi	D,CCPbuf  
	lxi	B,maxlen+1  ; Copy users buf into the
	LDIR		    ; CCP init-exec-buffer.
;

writsys:lxi	H,crlf
	call	prtmsg	  ; Space down.
	lxi	H,dstQST  ; print destination question
	call	getdsk	  ; get result disk
	jrz	exit	  ; warm boot if cr
	sta	dstdsk
	lxi	H,dstMSG
	call	prtmsg
	call	waitcr	  ; wait for a cr
	jrnz	writsys	  ; start over
	lxi	H,WRITF   ; write system from memory
	shld	IOdir
	lda	medtype	  ; If media type is DD, it
	cpi	DD	  ; is 8 inch disk
	cz	RW8in
	jrz	..don	  ; and we are done
	call	RW5in	  ; ELSE, 5 inch, do it
..don:	lxi	H,donMSG  ; done, do it again
	call	prtmsg
	jmpr	writsys	  ; let user do it again
NOfile:	lxi	H,NOmsg	  ; Arrive here when user
	call	prtmsg	  ; entered a command line.
exit:	lxi	H,exitMSG ; print an exit message
	call	prtmsg
	jmp	CPMBOOT
.page
.sbttl 	'Get and test users drive'
;
; Beginning of Subroutines
;-------------------------------
;  Subroutine:	getdsk
;    Regs  in:	none
;    Regs out:	none
;		Zflag set if user enters CR only.
;		Aborts upon ^C.
; Get disk name, select disk, and return 0 if cr
getdsk:	push	H	; Save msg addr in case of err
	call	prtmsg
	call	CONIN	; get user response
	cpi	ctrlC	
	jz	exit
	pop	H	; Restore msg addr.
	ani	0DFh	; convert to upper case
	cpi	'A'
	jrz	..drok
	cpi	'B'
	jrz	..drok
	cpi	'C'
	jrz	..drok
	cpi	'D'
	jrz	..drok
	cpi	cr	; If cr entered,
	rz		;    then return zero.
	push	H	; Invalid entry. 
	lxi	H,errmsg
	call	prtmsg
	pop	H	; Print error message
	jmpr	getdsk	; and ask again.
..drok:	sta	retdsk
	lxi	H,retMSG
	call	prtmsg
	lda	retdsk
	sui	'A'	; test char
	mov	C,A
	call	SELDSK	; select the disk
	call	CPMMAP
	dcx	H
	mov	A,M	; Dev type in Acc
	sta	medtype
	cpi	DD
	jrz	..medok
	cpi	MINI2
	jrz	..medok
	lxi	H,medMSG
	call	prtmsg
	jmp	CPMboot
..medok:
	lda	retdsk
	ora	A	; set not zero flag
	ret
.page
.sbttl	'Ask for CCP buffer input'
;-------------
; Print current CCP buff contents, ask user for input
;
askcmdstrg:
	lxi	H,CCPinfo ; give user options
	call	prtmsg
	lxi	H,CCPcur
	call	prtmsg
	lda	CCPlen	  ; If buffer =0, no chars
	cpi	0	  ; in buffer so send
	lxi	H,blnkmsg ; blank msg to screen
	jrz	..prnt	  ; ELSE,
	lxi	H,CCPbuf  ; print contents of
..prnt:	call	prtcmdstrg ;command string buffer
	lxi	H,CCPmsg  ; Print more info.
	call	prtmsg	  ; And ask for a response.
	ret
;--------------
; Clear the console buffer. (Pad it with blanks.)
clearbuf:
	lxi	H,conbuf
	lxi	D,conbuf+1
	mvi	M,0
	lxi 	B,maxlen+1 ; BC = length of conbuf+1
	LDIR		   ; Pad the buf with nulls.
	ret
;--------------
; Make sure console buffer is upper case
cvtUP:
	lxi	H,conbuf
..chk:	mov	A,M	; Get a chr from the buffer.
	ora	A	; Is it 0? (Null terminates.)
	rz		; Yes. Return. 
	cpi	'a'	; Upper case or not a letter?
	jrc	..loop	; Yes. Loop while not done.
	cpi	'z'+1	; Is not a lower case letter?
	jrnc	..loop	; Yes. Loop while not done.
	ani	0DFh	; Make letter upper case, &
	mov	M,A	; overwrite it in conbuf.
..loop:	inx	H	; Get to next chr in the buffer
	jmpr	..chk	; and keep checking.
.page
.sbttl	'Get users command string'
;------------
;  Subroutine getcmdstrg: Get user command string or CR
;
;    Regs  in:	none
;    Regs out: 	A = length of console buffer
;	    OR if the user enters CR as his first chr,
;		A = 0
;   Destroyed:	any/all
getcmdstrg:
	lxi	H,COMmsg
	call	prtmsg	    ; Print user info.
	mvi	B,maxlen+1  ; B = length + 1.
	lxi	H,conbuf    ; HL = con buf addr.
..getcon:
	push	B	; Save length.
	push	H	; Save buf addr.
	call	CONIN	; get the chr.
	pop	H	; Restore buf addr.
	pop	B	; Restore length.
	cpi	SPACE	; Is this a command chr?
	jrc	..cmdCHR; Yes.  Handle it specially.
	cpi	DEL
	jrz	..DEL
	call	prtchr	; Echo the input chr.
..putbuf:
	mov	M,A	; Put the chr in conbuf.
	inx	H	; Get to next conbuf addr.
	djnz	..getcon ;do it til B=0

	mvi	A,BELL	; if B=0, buffer full
	call	prtchr	; Ring on buf overflow
..DEL:	mov	A,B	 ; Check buf length
	cpi	maxlen+1 ; against maxlenth.
	jrz	..getcon ; Ignore extra dels.
	call	..wipeout; Erase the chr on the screen.
	inr	B	 ; Bump UP the length,
	dcx	H	 ; bump DOWN the buf addr,
	mov	A,M	
	cpi	SPACE	 ; If the chr was a ctrl chr,
	cc	..wipeout; erase again to remove the ^.
	mvi	M,0	 ; Put back the orig null,
	jmpr	..getcon ; Fetch more chrs.

..wipeout:
	mvi	A,BACKSP ; Do BS,S,BS combo.
	call	prtchr
	mvi	A,SPACE
	call	prtchr
	mvi	A,BACKSP
	call	prtchr
	ret
;
;	Subroutine continues on next page
.page
; process non-printing chrs.
;
..cmdCHR:
	cpi	BACKSP
	jrz	..DEL	; Handle BS as a DEL.

	cpi	cr	; Is this a car ret?
	jrnz	..doctl	; No. Check other ctrl chrs.

	mvi	A,maxlen ; Yes, we are done
	dcr	B	; maxlen - (maxlen+chars)
	sub	B	; = number of chars
	ret		; Return with num of chrs.
;***	cpi	0
;	rnz		; accum is 0, CR only
;	lda	CCPlen	; so save old buffer
;	ora	A	; If length is zero,
;	jrz	..arnd	;    then skip around copy.
;	mov	C,A	
;	mvi	B,0	; length for ldir
;	lxi	D,conbuf
;	lxé	H,CCPbuf
;	ldir		; save old buffer
;.arnd:	ret		
;
..doctl:cpi	ctrlZ+2	; Is it valid ctl A-Z or ESC? 
	jrnc	..bad	; No.  Handle a bad chr.
;
	mov	C,A	; Yes. Save the ctrl chr in C.
	mvi	A,'^'	; Print the up arrow.
	call	prtchr
	mov	A,C
	adi	'A'-1	; Make the ctrl chr ascii.
	call	prtchr	; Print the ascii chr.
	mov	A,C	; Restore original chr.
	jmp	..putbuf; and go back to main routine.
;
..bad:	mvi	A,BELL	; Char is invalid.  Beep 
	call	prtchr  ; the CRT, rejoin loop.
	jmp	..getcon
.page
.sbttl	'CRT I/O subroutines'
;---------------
; Subroutine prtcmdstrg:  Print CCP's command string
; Reg in:  HL = Addr of Command buffer
; Reg out: None
; Destroyed: A, HL
;
prtcmdstrg:
;
	mov	A,M	; get next char
	ora	A
	rz		; return if zero
	push	H	; save HL
	cpi	SPACE	
	jrnc	..ckhi	; check hi bit
	push	PSW
	mvi	A,uparow ;CP/M's arrow for all else
	call	prtchr
	pop	PSW	; restore char
	ori	40h	; make char upper case ASCII
	jmpr	..asc	; and print it
..ckhi: cpi	del	; if = or > del, print a dot
	jrc	..asc
	mvi	A,period
..asc:	mov	C,A	; output the char
	call	CONOUT
	pop	H	; restore HL
	inx	H	; increment to next char
	jmpr	prtcmdstrg
;--------------
; Regs  in: 	A = chr to be printed
; Regs out: 	none
; Destroyed:	DE
prtchr:
	push	PSW
	push	B
	push	H
	mov	C,A
	call	CONOUT
	pop	H
	pop	B
	pop	PSW
	ret
.page
;---------------
; Wait for a cr
waitcr:
	call	CONIN	; get a char
	cpi	cr	; loop if cr
	push	PSW
	lxi	H,crlf
	call	prtmsg
	pop	PSW
	ret
;---------------
; Print a message on the CRT 
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
.page
.sbttl	'Read or write 5 in. disks'
;
RW5in:
	call	HOME	; Home to track 0
	lxi	B,sysstrt ; load system start address
	call	SETDMA
	lxi	B,1	; start at sector 1
	call	SETSEC
	lxi	B,16*256 ; transfer entire track
	call	SETBYT
	call	RDWR	; read or write data
	lxi	B,1	; go to track 1
	call	SETTRK
	lxi	B,sysstrt+16*256 ; load address is 26
	call	SETDMA	; sectors later
	lxi	B,1	; start at sector 1
	call	SETSEC
	lxi	B,16*256 ; assume double
..1:	call	SETBYT
	call	RDWR	; transfer the data
	lxi	B,2	; Track 2
	call	SETTRK
	lxi	B,1	; Sector 1
	call	SETSEC
	lxi	B,BIOSSTRT ; DMA addr
	call	SETDMA
	lxi	B,16*256 ; Full track
	call	SETBYT
	call	RDWR
	ret
.page
.sbttl	' Read or write 8 in. disks'
RW8in:
	push	PSW	; save zero flag
	call	HOME	; Home to track 0
	lxi	B,sysstrt ; load system start address
	call	SETDMA
	lxi	B,1	; start at sector 1
	call	SETSEC
	lxi	B,26*128 ; transfer entire track
	call	SETBYT
	call	RDWR	; read or write data
	lxi	B,1	; go to track 1
	call	SETTRK
	lxi	B,sysstrt+26*128 ; load address is 26
	call	SETDMA	; sectors later
	lxi	B,1	; start at sector 1
	call	SETSEC
	lxi	B,52*128 ;assume double, cant be single
	call	SETBYT
	call	RDWR	; transfer the data
	pop	PSW	; restore zero flag
	ret
.page
.sbttl	'BIOS calls'
;--------------
; Call BIOS directly using WBOOT in low memory
;
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
CPMMAP:
	lhld	WBOOT
	lxi	D,60h
	dad	D
	pchl
SETBYT:
	lhld	WBOOT
	lxi	D,66h
	dad	D
	pchl
.page
.sbttl 'Storage'
;
IOdir:	.word	0
conbuf:	.blkb	maxlen ; Users conbuf here
	.byte	00     ; Terminates prtmsg.
medtype:.byte	0
.page
.sbttl	'CRT messages'
;
logMSG: .ascii	[cr][lf] 'SYSGEN for DMS 5" & 8" '
	.ascii		'floppy disks,  Version '
	.byte	version+'0','.'
	.byte	revision/10+'0',revision@10+'0'
	.byte	patch,cr,lf,0
NOmsg:	.ascii	[cr][lf]'SYSGEN does not '
	.asciz		'accept file input.'
srcQST:	.ascii	[cr][lf]'Source disk name:'
	.asciz		' (return to skip) '
srcMSG:	.ascii	'SOURCE ON '
srcdsk:	.byte	'A'
	.asciz	', type return to continue.'
dstQST:	.ascii	[cr][lf]'Destination disk name:'
	.asciz		' (return to reboot) '
dstMSG:	.ascii	'DESTINATION ON '
dstdsk:	.byte	'A'
	.asciz	', type return to continue.'
errmsg:	.ascii	[cr][lf]'Incorrect entry. '
	.asciz	'Please use A,B,C,D, or CR.'
donMSG: .asciz	'FUNCTION COMPLETE'[cr][lf]
CCPinfo:.ascii	[cr][lf][lf]'CP/M lets you execute '
	.asciz		'a CP/M command on cold boot.'
CCPcur:	.asciz	[cr][lf][lf]'Your current command is: '
BLNKmsg:.asciz		' No cold boot command'
CCPmsg:	.ascii	[cr][lf][lf]'         '
	.ascii		'Hit RETURN for '
	.ascii		'no change to this command. '
	.ascii	[cr][lf]'         '
	.ascii		'    C      to enter a '
	.ascii		'new command '
	.ascii	[cr][lf]'         '
	.ascii		'    ESC    to eliminate '
	.ascii		'this command.'
	.ascii	[cr][lf]'         '
	.ascii		'    ctrl-C to exit '
	.ascii		'the program.'
	.ascii	[cr][lf]'(The old command is erased '
	.ascii		'when you enter the new one.)'
	.ascii	[cr][lf][lf]'Enter a RET, C, ESC, or '
	.asciz		'ctrl-C  '
COMmsg:	.ascii	[cr][lf][lf]'Note: Entering only RETUR'
	.ascii		'N will eliminate the '
	.ascii		'current command.'
	.ascii	[cr][lf][lf]'Enter new command '
	.ascii	'(Use CR to end, DEL or BS to erase) '
	.asciz	[cr][lf]' >'
DELmsg:	.asciz	'Entry deleted.'
medMSG: .ascii	[cr][lf]'You have an incorrect media '
	.ascii		'type.'
	.ascii	[cr][lf]'Use ASSIGN to check your '
	.ascii		'drive assignments.'[lf]
exitMSG:.asciz	[cr][lf]'Exit SYSGEN.'
retMSG:
retdsk:	.byte	'A'
crlf:	.asciz	[cr][lf]
;
	.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:
	.end

