	TITLE	'SYSGEN - SYSTEM GENERATION PROGRAM 8/79'
;	SYSTEM GENERATION PROGRAM
;
;	UPDATED FOR CP/M 2.2 ON POLYMORPHIC SINGLE DENSITY
;	CONTROLLER WITH POLEX BY MIKE DOUGLAS, FEB 2018
;
VERS	EQU	20	;X.X
;
;	COPYRIGHT (C) DIGITAL RESEARCH
;	         1976, 1977, 1978, 1979
;
NSECTS	EQU	20	;NO. OF SECTORS PER TRACK
NTRKS	EQU	3	;NO. OF OPERATING SYSTEM TRACKS
NDISKS	EQU	3	;NUMBER OF DISK DRIVES
SECSIZ	EQU	128	;SIZE OF EACH SECTOR
LOG2SEC	EQU	7	;LOG 2 SECSIZ
SKEW	EQU	1	;SECTOR SKEW FACTOR
;
FCB	EQU	005CH	;DEFAULT FCB LOCATION
FCBCR	EQU	FCB+32	;CURRENT RECORD LOCATION
TPA	EQU	0100H	;TRANSIENT PROGRAM AREA
LOADP	EQU	900H	;LOAD POINT FOR SYSTEM DURING LOAD/STORE
BDOS	EQU	5H	;DOS ENTRY POINT
BOOT	EQU	0	;JMP TO 'BOOT' TO REBOOT SYSTEM
CONI	EQU	1	;CONSOLE INPUT FUNCTION
CONO	EQU	2	;CONSOLE OUTPUT FUNCTION
SELF	EQU	14	;SELECT DISK
OPENF	EQU	15	;DISK OPEN FUNCTION
DREADF	EQU	20	;DISK READ FUNCTION
;
MAXTRY	EQU	10	;MAXIMUM NUMBER OF RETRIES ON EACH READ/WRITE
CR	EQU	0DH	;CARRIAGE RETURN
LF	EQU	0AH	;LINE FEED
STACKSIZE	EQU	16	;SIZE OF LOCAL STACK
;
WBOOT	EQU	1	;ADDRESS OF WARM BOOT (OTHER PATCH ENTRY
;			POINTS ARE COMPUTED RELATIVE TO WBOOT)
SELDSK	EQU	24	;WBOOT+24 FOR DISK SELECT
SETTRK	EQU	27	;WBOOT+27 FOR SET TRACK FUNCTION
SETSEC	EQU	30	;WBOOT+30 FOR SET SECTOR FUNCTION
SETDMA	EQU	33	;WBOOT+33 FOR SET DMA ADDRESS
READF	EQU	36	;WBOOT+36 FOR READ FUNCTION
WRITF	EQU	39	;WBOOT+39 FOR WRITE FUNCTION
;
	ORG	TPA	;TRANSIENT PROGRAM AREA
	JMP	START
	DB	'COPYRIGHT (C) 1978, DIGITAL RESEARCH '
;
;	TRANSLATE TABLE - SECTOR NUMBERS ARE TRANSLATED
;	HERE TO DECREASE THE SYSGEN TIME FOR MISSED SECTORS
;	WHEN SLOW CONTROLLERS ARE INVOLVED.  TRANSLATION TAKES
;	PLACE ACCORDING TO THE "SKEW" FACTOR SET ABOVE.
;
OST:	DB	NTRKS	;OPERATING SYSTEM TRACKS
SPT:	DB	NSECTS	;SECTORS PER TRACK (CAN BE PATCHED)
TRAN:			;BASE OF TRANSLATE TABLE
TRELT	SET	1	;FIRST/NEXT TRAN ELEMENT
TRBASE	SET	1	;BASE FOR WRAPAROUND
	REPT	NSECTS	;ONCE FOR EACH SECTOR ON A TRACK
	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
MULTSEC:
	;MULTIPLY THE SECTOR NUMBER IN A BY THE SECTOR SIZE
	MOV L,A! MVI H,0 ;SECTOR NUMBER IN HL
	REPT LOG2SEC	;LOG 2 OF SECTOR SIZE
	DAD	H
	ENDM
	RET ;WITH HL = SECTOR * SECTOR SIZE
;
GETCHAR:
;	READ CONSOLE CHARACTER TO REGISTER A
	MVI C,CONI! CALL BDOS!
;	CONVERT TO UPPER CASE BEFORE RETURN
	CPI 'A' OR 20H ! RC	;RETURN IF BELOW LOWER CASE A
	CPI ('Z' OR 20H) + 1
	RNC	;RETURN IF ABOVE LOWER CASE Z
	ANI 5FH! RET
;
PUTCHAR:
;	WRITE CHARACTER FROM A 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 H,L TIL ZERO
	;WITH LEADING CRLF
	PUSH H! CALL CRLF! POP H ;DROP THRU TO OUTMSG0
OUTMSG:
	MOV A,M! ORA A! RZ
;	MESSAGE NOT YET COMPLETED
	PUSH H! CALL PUTCHAR! POP H! INX H
	JMP	OUTMSG
;
SEL:
;	SELECT DISK GIVEN BY REGISTER A
	MOV C,A! LHLD WBOOT! LXI D,SELDSK! DAD D! PCHL
;
TRK:	;SET UP TRACK
	LHLD	WBOOT	;ADDRESS OF BOOT ENTRY
	LXI	D,SETTRK	;OFFSET FOR SETTRK ENTRY
	DAD	D
	PCHL		;GONE TO SETTRK
;
SEC:	;SET UP SECTOR NUMBER
	LHLD	WBOOT
	LXI	D,SETSEC
	DAD	D
	PCHL
;
DMA:	;SET DMA ADDRESS TO VALUE OF B,C
	LHLD	WBOOT
	LXI	D,SETDMA
	DAD	D
	PCHL
;
READ:	;PERFORM READ OPERATION
	LHLD	WBOOT
	LXI	D,READF
	DAD	D
	PCHL
;
WRITE:	;PERFORM WRITE OPERATON
	LHLD	WBOOT
	LXI	D,WRITF
	DAD	D
	PCHL
;
DREAD:	;DISK READ FUNCTION
	MVI	C,DREADF
	JMP	BDOS
;
OPEN:	;FILE OPEN FUNCTION
	MVI C,OPENF ! JMP BDOS
;
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 CP/M DURING SYSGEN
	SHLD	DMADDR
;
;	CLEAR TRACK TO 00
	MVI	A,-1	;START WITH TRACK EQUAL -1
	STA	TRACK
;
RWTRK:	;READ OR WRITE NEXT TRACK
	LXI	H,TRACK
	INR	M	;TRACK = TRACK + 1
	LDA	OST	;NUMBER OF OPERATING SYSTEM TRACKS
	CMP	M	;= TRACK NUMBER ?
	JZ	ENDRW	;END OF READ OR WRITE
;
;	OTHERWISE NOTDONE, GO TO NEXT TRACK
	MOV	C,M	;TRACK NUMBER
	CALL	TRK	;TO SET TRACK
	MVI	A,-1	;COUNTS 0, 1, 2, . . . 25
	STA	SECTOR	;SECTOR INCREMENTED BEFORE READ OR WRITE
;
RWSEC:	;READ OR WRITE SECTOR
	LDA	SPT	;SECTORS PER TRACK
	LXI	H,SECTOR
	INR	M	;TO NEXT SECTOR
	CMP	M	;A=26 AND M=0 1 2...25 (USUALLY)
	JZ	ENDTRK	;
;
;	READ OR WRITE SECTOR TO OR FROM CURRENT DMA ADDR
	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),TRAN(SECTOR)
	CALL	SEC	;SET UP SECTOR NUMBER
	POP	B	;RECALL TRAN(0),TRAN(SECTOR)
	MOV	A,C	;TRAN(SECTOR)
	SUB	B	;-TRAN(0)
	CALL	MULTSEC	;*SECTOR SIZE
	XCHG		;TO DE
	LHLD	DMADDR	;BASE DMA ADDRESS FOR THIS TRACK
	DAD	D	;+(TRAN(SECTOR)-TRAN(0))*SECSIZ
	MOV	B,H
	MOV	C,L	;TO BC FOR SEC CALL
	CALL	DMA	;DMA ADDRESS SET FROM B,C
;	DMA ADDRESS SET, CLEAR RETRY COUNT
	XRA	A
	STA	RETRY	;SET TO ZERO RETRIES
;
TRYSEC:	;TRY TO READ OR WRITE CURRENT SECTOR
	LDA	RETRY
	CPI	MAXTRY	;TOO MANY RETRIES?
	JC	TRYOK
;
;	PAST MAXTRIES, MESSAGE AND IGNORE
	LXI	H,ERRMSG
	CALL	OUTMSG
	CALL	GETCHAR
	CPI	CR
	JNZ	REBOOT
;
;	TYPED A CR, OK TO IGNORE
	CALL	CRLF
	JMP	RWSEC
;
TRYOK:
;	OK TO TRY READ OR WRITE
	INR	A
	STA	RETRY	;RETRY=RETRY+1
	LDA	RW	;READ OR WRITE?
	ORA	A
	JZ	TRYREAD
;
;	MUST BE WRITE
	CALL	WRITE
	JMP	CHKRW	;CHECK FOR ERROR RETURNS
TRYREAD:
	CALL	READ
CHKRW:
	ORA	A
	JZ	RWSEC	;ZERO FLAG IF R/W OK
;
;	ERROR, RETRY OPERATION
	JMP	TRYSEC
;
;	END OF TRACK
ENDTRK:
	LDA	SPT	;SECTORS PER TRACK
	CALL	MULTSEC	;*SECSIZ
	XCHG		;TO DE
	LHLD	DMADDR	;BASE DMA FOR THIS TRACK
	DAD	D	;+SPT*SECSIZ
	SHLD	DMADDR	;READY FOR NEXT TRACK
	JMP	RWTRK	;FOR ANOTHER TRACK
;
ENDRW:	;END OF READ OR WRITE, RETURN TO CALLER
	RET
;
;
START:
;
	LXI	SP,STACK	;SET LOCAL STACK POINTER
	LXI	H,SIGNON
	CALL	OUTMSG
;
;	CHECK FOR DEFAULT FILE LOAD INSTEAD OF GET
;
	LDA	FCB+1	;BLANK IF NO FILE
	CPI	' '
	JZ	GETSYS	;SKIP TO GET SYSTEM MESSAGE IF BLANK
	LXI	D,FCB	;TRY TO OPEN IT
	CALL	OPEN	;
	INR	A	;255 BECOMES 00
	JNZ	RDOK	;OK TO READ IF NOT 255
;
;	FILE NOT PRESENT, ERROR AND REBOOT
;
	LXI	H,NOFILE
	CALL	CRMSG
	JMP	REBOOT
;
;	FILE PRESENT
;	  READ TO LOAD POINT
;
RDOK:
	XRA	A
	STA	FCBCR	;CURRENT RECORD = 0
;
;	PRE-READ AREA FROM TPA TO LOADP
;
	MVI	C,(LOADP-TPA)/SECSIZ
;	PRE-READ FILE
PRERD:
	PUSH	B	;SAVE COUNT
	LXI	D,FCB	;INPUT FILE CONTROL COUNT
	CALL	DREAD	;ASSUME SET TO DEFAULT BUFFER
	POP	B	;RESTORE COUNT
	ORA	A
	JNZ	BADRD	;CANNOT ENCOUNTER END-OF FILE
	DCR	C	;COUNT DOWN
	JNZ	PRERD	;FOR ANOTHER SECTOR
;
;	SECTORS SKIPPED AT BEGINNING OF FILE
;
	LXI	H,LOADP
RDINP:
	PUSH	H
	MOV	B,H
	MOV	C,L	;READY FOR DMA
	CALL	DMA	;DMA ADDRESS SET
	LXI	D,FCB	;READY FOR READ
	CALL	DREAD	;
	POP	H	;RECALL DMA ADDRESS
	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
;
BADRD:	;EOF ENCOUNTERED IN INPUT FILE

	LXI	H,BADFILE
	CALL	CRMSG
	JMP	REBOOT
;
;
GETSYS:
	LXI	H,ASKGET	;GET SYSTEM?
	CALL	CRMSG
	CALL	GETCHAR
	CPI	CR
	JZ	PUTSYS	;SKIP IF CR ONLY
;
	SUI	'A'		;NORMALIZE DRIVE NUMBER
	CPI	NDISKS		;VALID DRIVE?
	JC	GETC		;SKIP TO GETC IF SO
;
;	INVALID DRIVE NUMBER
	CALL	BADDISK
	JMP	GETSYS		;TO TRY AGAIN
;
GETC:
;	SELECT DISK GIVEN BY REGISTER A
	ADI	'A'
	STA	GDISK		;TO SET MESSAGE
	SUI	'A'
	CALL	SEL		;TO SELECT THE DRIVE
;	GETSYS, SET RW TO READ AND GET THE SYSTEM
	CALL	CRLF
	LXI	H,GETMSG
	CALL	OUTMSG
	CALL	GETCHAR
	CPI	CR
	JNZ	REBOOT
	CALL	CRLF
;
	XRA	A
	STA	RW
	CALL	GETPUT
	LXI	H,DONE
	CALL	OUTMSG
;
;	PUT SYSTEM
PUTSYS:
	LXI	H,ASKPUT
	CALL	CRMSG
	CALL	GETCHAR
	CPI	CR
	JZ	REBOOT
	SUI	'A'
	CPI	NDISKS
	JC	PUTC
;
;	INVALID DRIVE NAME
	CALL	BADDISK
	JMP	PUTSYS	;TO TRY AGAIN
;
PUTC:
;	SET DISK FROM REGISTER C
	ADI	'A'
	STA	PDISK	;MESSAGE SET
	SUI	'A'
	CALL	SEL	;SELECT DEST DRIVE
;	PUT SYSTEM, SET RW TO WRITE
	LXI	H,PUTMSG
	CALL	CRMSG
	CALL	GETCHAR
	CPI	CR
	JNZ	REBOOT
	CALL	CRLF
;
	LXI	H,RW
	MVI	M,1
	CALL	GETPUT	;TO PUT SYSTEM BACK ON DISKETTE
	LXI	H,DONE
	CALL	OUTMSG
	JMP	PUTSYS	;FOR ANOTHER PUT OPERATION
;
REBOOT:
	MVI	A,0
	CALL	SEL
	CALL	CRLF
	JMP	BOOT
BADDISK:
	;BAD DISK NAME
	LXI	H,QDISK
	CALL	CRMSG
	RET
;
;
;
;	DATA AREAS
;	MESSAGES
SIGNON:	DB	CR,LF,'SYSGEN v'
	DB	VERS/10+'0','.',VERS MOD 10+'0'
	DB	' for Polymorphic SD Controller and POLEX'
	DB	CR,LF,0
ASKGET:	DB	'Source drive name (or RETURN to skip)',0
GETMSG:	DB	'Source on '
GDISK:	DS	1	;FILLED IN AT GET FUNCTION
	DB	', then type RETURN',0
ASKPUT:	DB	'Destination drive name (or RETURN to reboot)',0
PUTMSG:	DB	'Destination on '
PDISK:	DS	1	;FILLED IN AT PUT FUNCTION
	DB	', then type RETURN',0
ERRMSG:	DB	'Permanent error, type RETURN to ignore',0
DONE:	DB	'Function complete',0
QDISK:	DB	'Invalid drive name (use A, B, OR C)',0
NOFILE:	DB	'No source file on disk',0
BADFILE:
	DB	'Source file incomplete',0
;
;	VARIABLES
SDISK:	DS	1	;SELECTED DISK FOR CURRENT OPERATION
TRACK:	DS	1	;CURRENT TRACK
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
	DS	STACKSIZE*2
STACK:	EQU	$

	END

