	TITLE	'SYSGEN - SYSTEM GENERATION PROGRAM 8/79'
;	SYSTEM GENERATION PROGRAM, VERSION FOR MDS
;	
;	UPDATED 4/2014 BY MIKE DOUGLAS FOR MITS 88-HDSK
;	(SEARCH FOR 88-HDSK FOR CHANGES AND COMMENTS)
;	
;	Updated 10/2014 by Mike Douglas to copy or create
;	the pack descriptor (track 0, sector 0) as part
;	of the SYSGEN procedure. (Version 2.1)
;
;	Update 12/2015 by Mike Douglas to account for
;	the BIOS change of the setsec call from 0-indexed
;	to 1-indexed. (Version 2.2)
;
VERS	EQU	22	;X.X
;
;	COPYRIGHT (C) DIGITAL RESEARCH
;	         1976, 1977, 1978, 1979
;

;NSECTS	EQU	26	;NO. OF SECTORS PER TRACK
NSECTS	EQU	96	;NO. OF SECTORS PER TRACK (88-HDSK)
;NTRKS	EQU	2	;NO. OF OPERATING SYSTEM TRACKS
NTRKS	EQU	1	;NO. OF OPERATING SYSTEM TRACKS (88-HDSK)
;NDISKS	EQU	4	;NUMBER OF DISK DRIVES
NDISKS	EQU	2	;NUMBER OF DISK DRIVES (88-HDSK)
SECSIZ	EQU	128	;SIZE OF EACH SECTOR
LOG2SEC	EQU	7	;LOG 2 SECSIZ
SKEW	EQU	1	;SECTOR SKEW FACTOR (ONLY USE 1 FOR 88-HDSK)
;
FCB	EQU	005CH	;DEFAULT FCB LOCATION
FCBCR	EQU	FCB+32	;CURRENT RECORD LOCATION
TPA	EQU	0100H	;TRANSIENT PROGRAM AREA
LOADP	EQU	800H	;LOAD POINT FOR SYSTEM DURING LOAD/STORE (88-HDSK)
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
TRELT	SET	1	;FIRST/NEXT TRAN ELEMENT (88-HDSK)
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	 (COMMENTED OUT FOR 88-HDSK)
;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
	MVI	B,0	;MSB OF TRACK = 0
	MOV	C,M	;LSB OF TRACK
	CALL	TRK	;TO SET TRACK
	MVI	A,-1	;COUNTS 0, 1, 2, . . . 25 (95 for 88-HDSK)
	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=96 AND M=0 1 2...25 (95 for 88-HDSK)
	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
;	For 88-HDSK, the page descriptor (256 bytes) is not
;	present in the file, so adjust loading point by
;	256 bytes.
;
	MVI	C,(LOADP+100h-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
;	For 88-HDSK, the page descriptor (256 bytes) is not
;	present in the file, so adjust loading point by
;	256 bytes.

	LXI	H,LOADP+100h	
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
;	For 88-HDSK, prompt the user for optional pack
;	descriptor input (pack label, date)

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
	CALL	GETPDI		;88-HDSK, get pack desc info
;	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,'MITS 88-HDSK SYSGEN VER '
	DB	VERS/10+'0','.',VERS MOD 10+'0',CR,LF
	DB	'(FOR CP/M 2.2B VER 1.5 OR NEWER)',CR,LF
	DB	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	LF,'DESTINATION DRIVE NAME (OR RETURN TO REBOOT)',0
PUTMSG:	DB	LF,'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 OR B)',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:



;**********************************************************************
;
;   88-HDSK Pack Descriptor Update Routines
;
;**********************************************************************
BRDCON	equ	10		;BDOS read console buffer

COMBUF:	ds	76		;console input buffer
MAXDAY:	ds	1		;maximum day in a given month

mCLbl:	db	LF,'DEFAULT DISK LABEL: ',0
mLabel:	db	'DISK LABEL (20 CHARS MAX) OR RETURN FOR NO CHANGE: ',0

mCDate:	db	LF,'DEFAULT DISK DATE: '
curMon: db	'mm/'
curDay:	db	'dd/'
curCen:	db	'19'
curYr:	db	'yy',0

mMonth:	db	'MONTH (1-12) OR RETURN FOR NO CHANGE: ',0
mDay:	db	'DAY (1-'
mDay2:	db	'xx): ',0
mYear:	db	'YEAR (YYYY): ',0

;----------------------------------------------------------------------
;  GETPDI - Optionally get pack descriptor information from the user
;----------------------------------------------------------------------
; First, display the current disk label

GETPDI:	lxi	h,mCLbl		;hl->current disk label prompt
	call	CRMSG
	lxi	h,PDYEAR	;force a zero terminator at end of...
	mov	a,m		;  current disk label
	push	psw		;save for later
	mvi	m,0
	lxi	h,PDLABL	;hl->actual disk label text
	call	OUTMSG
	pop	psw		;restore year
	sta	PDYEAR
	
; Prompt for new label

	lxi	h,mLabel	;hl->disk label prompt
	call	CRMSG
	lxi	h,COMBUF
	mvi	m,75		;allow user to type extra chrs
	xchg			;buffer address to de
	mvi	c,BRDCON	;get a line of user input
	call	BDOS
	lxi	h,COMBUF+1	;count of chrs typed
	mov	a,m
	ora	a
	jz	DSPDAT		;nothing entered, go display date

; Move in the new disk label

	mov	b,a		;b=length entered
	lxi	d,PDLABL-1	;label in pack descriptor
	mvi	c,20		;keep 1st 20 characters
LABLUP:	inx	h
	inx	d
	mvi	a,' '		;pad with spaces
	dcr	b
	jm	LL2
	mov	a,m
LL2:	stax	d
	dcr	c
	jnz	LABLUP

;-----------------------------------------------
;Ask for date. COMBUF is already set up.
;-----------------------------------------------
; Display the current date as mm/dd/yyyy

DSPDAT:	lxi	h,curMon	;hl->current month in message
	lda	PDMON		;a=current month
	call	BIN2DEC		;convert to ASCII in message

	lxi	h,curDay	;hl->current day in message
	lda	PDDAY		;a=current day
	call	BIN2DEC		;convert to ASCII in message

	lda	PDYEAR		;a=current year
	lxi	h,'19'
	sui	100		;1900s if less than 100
	jc	DOYEAR
	lxi	h,'20'
	sui	100
	jc	DOYEAR
	inr	h		;'21'
	sui	100
DOYEAR:	shld	curCen		;store century in ASCII
	lxi	h,curYr		;hl->current year in message
	adi	100		;a=YY
	call	BIN2DEC

	lxi	h,mCDATE	;hl->current date message
	call	CRMSG		;display current date

; Prompt for and input new date. Start with the month.

ASKMON:	lxi	h,mMonth	;hl->month prompt
	call	CRMSG
	call	GETDEC		;result in de
	jc	ASKMON		;bogus?
	jz	NODATE		;no input?
	mov	a,d		;too high?
	ora	a
	jnz	ASKMON
	mov	a,e
	sta	PDMON
	dcr	a
	cpi	12		;12 months per year
	jnc	ASKMON

	lxi	h,DPMTAB-1	;days per month
	dad	d		;look up for this month
	mov	a,m		;a=max days in month
	sta	MAXDAY		;save for later

; Input day of month. "A" has max day in the month

	lxi	h,mDay2		;hl->max day in message
	call	BIN2DEC		;put in the day of month message

ASKDAY:	lxi	h,mDay		;hl->day prompt
	call	CRMSG
	call	GETDEC		;result in de
	jc	ASKDAY		;bogus?

; Validate the day of month entered

	lxi	h,MAXDAY	;hl->max day for the month	
	mov	a,e		;a=day entered by operator
	dcr	a		;test day against bounds
	cmp	m
	jnc	ASKDAY		;too small or too large
	inr	a		;restore original day
	sta	PDDAY

; Input the year

ASKYR:	lxi	h,mYear		;h->year prompt
	call	CRMSG
	call	GETDEC		;result in de
	jc	ASKYR		;bogus?

	lxi	h,-1900		;offset to 1900
	dad	d
	mov	a,h		;too high?
	ora	a
	jnz	ASKYR

	mov	a,l
	sta	PDYEAR

NODATE:	ret

;--------------------------------------------------------------
; BIN2DEC - Binary byte in A to 2 ASCII digits at (HL)
;   HL is preserved
;--------------------------------------------------------------
BIN2DEC: mvi	m,'0'		;init destination 10s digit

LOOP10:	sui	10		;count 10s
	jc	DO1S		;done with 10s, do 1s
	inr	m
	jmp	LOOP10

DO1S:	adi	'0'+10		;form ASCII 1s digit
	inx	h		;move to 1s position
	mov	m,a
	dcx	h		;restore hl
	ret
	
;=====Subroutine====================
;Get CR-terminated decimal parameter
;parameter the console
; On entry:
;  COMBUF(0) is initialized >>4
; On Exit:
;   de=value, 0 if no chrs
;   z set if no characters typed
;   carry set if bogus digit
; trashes all registers
;===================================
GETDEC:	lxi	d,COMBUF
	mvi	c,BRDCON	;get a line of user input
	call	BDOS

	lxi	h,COMBUF+1	;count of chrs typed
	mov	a,m
	ora	a		;no digits?
	rz

	cpi	5		;too many digits?
	cmc
	rc			;y: ret with carry set

	mov	b,a		;b=chr count

	lxi	d,0		;initial value
	xra	a		;clear carry

GDLOOP:	dcr	b		;does not affect carry
	rm			;get all digits

	inx	h		;next chr
	mov	a,m

	sui	'0'		;de-ASCII
	cpi	10		;carry means below 10
	cmc
	rc			;bogus? ret with carry set

	push	h		;save input pointer
	xchg			;previous value to hl

				;..and save pointer
	dad	h		;x2
	mov	d,h
	mov	e,l
	dad	h		;x4
	dad	h		;x8
	dad	d		;x10

	mov	e,a		;add in new digit
	mvi	d,0
	dad	d		;will clear carry if no error
	xchg			;result to de
	pop	h
	jmp	GDLOOP

;===Table===========================
; Days per month
; No attempt to deal with leap years
;===================================
DPMTAB:	db	31,29,31,30,31,30
	db	31,31,30,31,30,31


	ORG	LOADP
;========================================================
;Pack Descriptor Page in location to write with CP/M
;========================================================
PDESC:
PDLABL:	db	'MITS HD CP/M 2.2    '	;pack name (20 chrs)
PDYEAR:	db	80		;(14)year
PDMON:	db	1		;(15)month
PDDAY:	db	1		;(16)day
	db	0,0,0
PDCBS:	dw	0		;(1A)Current backup set
PDATP1:	dw	1		;(1C)Allocation Table Pointer
PDATP2:	dw	1		;(1E)Allocation Table Pointer
PDATP3:	dw	1		;(20)Allocation Table Pointer
PDDTB1:	dw	00C0h		;(22)Directory Table Pointer
PDDTB2:	dw	00C0h		;(24)Directory Table Pointer
PDDTB3:	dw	00C0h		;(26)Directory Table Pointer
PDOISP:	dw	0007h		;(28)OS Image Start Page
PDOIL:	dw	0001h		;(2A)OS Image Length (1 sector)
PDMF:	dw	1		;(2C)mount flag
	dw	0		;(2E)
	dw	0,0,0,0,0,0,0,0	;(30)
PDCATP:	dw	1		;(40)Current Alloc. Table Ptr
PDATL:	dw	0131h		;(42)Allocation Table length
PDCDTP:	dw	00C0h		;(44)Current Directory Table Ptr
PDDTL:	dw	100h		;(46)Directory Table Length
	dw	3		;(48)
	dw	0		;(4A)
PDLVP:	dw	4C1Fh		;(4C)Last Page on Volume
PDNFG:	dw	0922h		;(4E)Number of Free Groups
PDNTG:	dw	39h		;(50)Number of Table Groups
PDUG:	dw	4		;(52)# of unallocatable groups
PDSPA1:	dw	0FFFFh		;(54)Swap area pointer
PDSPA2:	dw	0FFFFh		;(56)Swap area pointer
	dw	0,0,0,0		;(58)
	dw	0,0,0,0,0,0,0,0	;(60)
	dw	0,0,0,0,0,0,0,0	;(70)
	dw	0,0,0,0,0,0,0,0	;(80)
	dw	0,0,0,0,0,0,0,0	;(90)
	dw	0,0,0,0,0,0,0,0	;(A0)
	dw	0,0,0,0,0,0,0,0	;(B0)
	dw	0,0,0,0,0,0,0,0	;(C0)
	dw	0,0,0,0,0,0,0,0	;(D0)
	dw	0,0,0,0,0,0,0,0	;(E0)
	dw	0,0,0,0,0,0,0,0	;(F0)
	END
