; COPYRIGHT (C) 1977, CROMEMCO, INC.
;
; RETYPED FROM MANUAL AND MODIFIED TO ASSEMBLE WITH Z80ASM 1.6
; DECEMBER 2014, UDO MUNK
;
	ORG	0C000H	;START OF PROM
;
;
STACK:	EQU	7CH	;MUST LEAVE ROOM FOR
;			;4 BYTES OF TEMP STORAGE
;			;ABOVE THE STACK
;			;(STACK)   = DISK FLAGS
;			;(STACK+1) = DISK LETTER (A-D)
;			;(STACK+2) - (STACK+3): ROOM FOR
;			;UP TO 2 SEMI-COLONS AS PART OF
;			;THE DISK PROMPT.
;
;
NDRIVES:  EQU	4	;MAX. NO. OF DISK DRIVES
;
; BIT ASSIGNMENT FOR THE DISK FLAGS
;
FASTSEEK: EQU	7
DISKMODE: EQU	5
MAXI:	  EQU	4
;THE DISK NUMBER (0-3) OCCUPIES BITS 0 & 1
;
;
DSTAT:	EQU	30H	;DISK STATUS PORT
DCOMMND:EQU	30H	;DISK COMMAND PORT
DSEC:	EQU	32H	;DISK SECTOR PORT
DDATA:	EQU	33H	;DISK DATA PORT
DFLAGS:	EQU	34H     ;DISK FLAGS PORT
DCONTR:	EQU	34H	;DISK CONTROL PORT
DTRACK:	EQU	31H	;DISK TRACK PORT
;
IMASK:	EQU	3	;INTERRUPT MASK PORT
BAUD:	EQU	0	;BAUD RATE PORT
PARLEL:	EQU	4	;PARALLEL PORT
BOOTSW:	EQU	40H	;BOOT SWITCH
MAXIM:	EQU	10H	;MASK FOR MAXI DISK
HDLDM:	EQU	20H	;HEAD LOAD MASK
;
;
STAT:	EQU	0	;STATUS PORT
DATA:	EQU	1	;DATA PORT
COMMND:	EQU	2	;COMMAND PORT
DAV:	EQU	40H	;DATA-AVAILABLE MASK
TBE:	EQU	80H	;XMITTER-BUF-EMPTY MSK
;
CASE:	EQU	0
;
CR:	EQU	0DH
LF:	EQU	0AH
ESC:	EQU	1BH
ALT:	EQU	7DH
;
;
;++++++++++++++++++++++++++++++++++++++++++++++++++++
;
START:	LD	HL,STACK
	LD	SP,HL
	EX	DE,HL		;DE -> TEMP STORATE
	DI
	CALL	INITBAUD	;INIT. THE SERIAL PORT
	SUB	A
	OUT	(IMASK),A	;MASK OUT 4FDC INTERUPTS
	IN	A,(DFLAGS)	;READ DISK FLAGS
	AND	BOOTSW		;LOOK AT BOOT SWITCH
	JR	Z,BOOTDK
	JP	MONITR
;
;
; MONITOR COMMAND
; QUIT THE MONITOR & BOOT CDOS IN
;
BOOTMC:
	CALL	SKSGCR		;REQUIRE A CR
;
;
; BOOT DISK
;
BOOTDK:
	LD	A,0D0H		;TERMINATE THE HOMING
	OUT	(DCOMMND),A	;OF THE DISK HEAD
BOT200:	IN	A,(DSTAT)
	RRA
	JR	C,BOT200
	DI
	LD	A,1 < MAXI	;MAXI FLAG
BOT300:
	LD	HL,0080H	;INIT. BUFFER PNTR
	LD	SP,HL		;& STACK PNTR
	PUSH	AF		;SAVE MINI/MAXI FLAG
	LD	B,H		;0 [DISK A)
	CALL	DHOME		;HOME DISK
	JR	NZ,BOT500	;DISK ERROR
	POP	AF		;GET MINI/MAXI FLAG
	PUSH	AF
	LD	B,H		;0 [DISK A]
	LD	E,1		;SECTOR 1
	CALL	DREAD		;READ THE SECTOR
	JP	Z,80H		;OK, GO EXECUTE
BOT500:	POP	AF		;GET MINI/MAXI FLAG
	XOR	1 < MAXI	;TOGGLE IT
	JR	BOT300
;
;
;HOME DISK DRIVE
;
;INPUT - B CONTAINS DISK NUMBER (0,1,2,3)
;	 A BIT 4 CONTAINS 1 IF MAXI
;
;OUTPUT - B CONTAINS STATUS
;	  ZERO FLAG RESET IF ERROR
;
;REGISTERS A,F,B,C ARE CHANGED
;
;
DHOME:	CALL	SELECT		;SELECT DISK
	OUT	(DCONTR),A	;OUTPUT SELECT BYTE
	LD	D,98H		;ERROR MASK
	AND	MAXIM		;MAXI DISK?
	LD	A,7FH		;TURN OFF HIGH SPEED SEEK
	OUT	(PARLEL),A
	LD	A,0FH		;LOAD MINI RESTORE COMMAND
	JR	Z,EXECUTE	;NO, ITS A MINI
	LD	A,0DH		;MAXI RESTORE COMMAND
	JR	EXECUTE		;EXEC COMMAND & WAIT TIL DONE
;
;
;
;SEEK TO DESIRED TRACK
;
;TRACK REGISTER MUST HAVE BEEN PREVIOUSLY LOADED
;(MAY BE DONE BY INITIALLY DOING A HOME)
;
;INPUT - B CONTAINS DISK DRIVE (0,1,2,3)
;	 D CONTAINS TRACK
;	 A BIT 7 = 1 FOR FAST SEEK
;	 A BIT 4 = 1 FOR MAXI
;
;OUTPUT - B CONTAINS STATUS
;	  ZERO FLAG RESET IF ERROR
;
;REGISTERS A,F,B,C,D ARE CHANGED
;
DSEEK:	PUSH	AF		;SAVE DISK FLAGS
	CALL	SELECT		;SELECT DISK
	OUT	(DCONTR),A	;OUTPUT CONTROL BYTE
	OUT	(C),D		;OUTPUT DESIRED TRACK
	LD	D,98H		;ERROR MASK
	POP	AF		;GET FLAGS
	RLA			;FAST SEEK?
	JR	C,DSK500
	AND	MAXIM < 1	;MASK FOR MINI/MAXI
	LD	A,01FH		;LOAD SEEK COMMAND FOR MINI
	JR	Z,EXECUTE	;MINI DISK
	LD	A,1DH		;LOAD COMMAND FOR MAXI
;
;
EXECUTE:
	OUT	(DCOMMND),A	;OUTPUT COMMAND
;
EXCCHK:
	IN	A,(DFLAGS)	;WAIT FOR COMPLETION
	RRA
	JR	NC,EXCCHK	;[UNTIL INTREQ]
EREXIT:
	IN	A,(DSTAT)	;DISK STATUS
	LD	B,A		;SAVE STATUS
	AND	D		;MASK FOR ERRORS
	RET
;
;
DSK500:	LD	A,6FH		;TURN ON FAST SEEK
	OUT	(PARLEL),A
	LD	A,18H		;SEEK COMMAND
	CALL	EXECUTE
DSK540:	IN	A,(PARLEL)	;FAST SEEK DONE?
	AND	40H
	JR	NZ,DSK540
	LD	A,7FH		;TURN OFF FAST SEEK
	OUT	(PARLEL),A
	SUB	A		;NO ERROR CHECKING, SAY OK
	LD	B,A
	RET
;
;
;READ 1 SECTOR FROM DISK
;
;INPUT - B CONTAINS DISK DRIVE (0,1,2,3)
;	 E CONTAINS SECTOR
;	 A BIT 4 = 1 FOR MAXI
;	 HL CONTAINS BUFFER ADDRESS
;
;OUTPUT - B CONTAINS STATUS
;	  Z FLAG IS SET IF NO ERRORS
;	  HL PTS PAST BUFFER
;
;REGISTERS A,F,B,C,D,E,H,L ARE CHANGED
;
;
DREAD:	CALL	SETUP		;SET UP FOR READ
	ADD	A,88H		;ADD READ COMMAND TO
				;HEAD LOAD FLAG
	LD	D,9CH		;ERROR MASK
;
	OUT	(DCOMMND),A	;OUTPUT READ COMMAND
DRD250:	IN	A,(DFLAGS)	;WAIT FOR REQUEST
	RRA			;CHECK FOR INTREQ
	JR	C,EREXIT	;END OF SECTOR OR ERROR
	INI			;READ A BYTE
	JP	NZ,DRD250	;NOT DONE YET
	JR	EXCCHK		;WAIT FOR INTREQ
;
;
;WRITE A SECTOR TO THE DISK
;
;INPUT - B CONTAINS DISK DRIVE (0,1,2,3)
;	 E CONTAINS SECTOR
;	 A BIT 4 = 1 FOR MAXI
;	 HL CONTAINS BUFFER ADDRESS
;
;OUTPUT - B CONTAINS STATUS
;	  Z FLAG IS SET IF NO ERRORS
;	  HL PTS PAST BUFFER
;
;REGISTERS A,F,B,C,D,E,H,L ARE CHANGED
;
;
DWRITE:	CALL	SETUP		;SET UP FOR WRITE
	ADD	A,0A8H		;ADD WRITE COMMAND TO
				;HEAD LOAD FLAG
	LD	D,0FCH		;ERROR MASK
	OUT	(DCOMMND),A	;OUTPUT WRITE COMMAND
DWR250:	IN	A,(DFLAGS)	;WAIT FOR REQUEST
	RRA			;CHECK FOR INTREQ
	JR	C,EREXIT	;END OF SECTOR OR ERROR
	OUTI			;READ A BYTE
	JP	NZ,DWR250	;NOT DONE YET
	JR	EXCCHK		;WAIT FOR INTREQ
;
;
;SET UP FOR READ OR WRITE
;
;INPUT - B CONTAINS DISK DRIVE (0,1,2,3)
;	 E CONTAINS SECTOR
;	 A BIT 4 CONTAINS 1 IF MAXI
;
;OUTPUT - D CONTAINS SELECT BYTE
;	  A CONTAINS HEAD LOAD FLAG
;	  B CONTAINS 128 (# OF BYTES)
;	  C CONTAINS DATA PORT

;REGISTERS A,F,B,C,D ARE CHANGED
;
;
SETUP:
	CALL	SELECT		;GET SELECT BYTE
	OR	80H		;TURN ON AUTO WAIT
	LD	D,A		;SAVE CONTROL BYTE
	LD	A,E		;SECTOR #
	OUT	(DSEC),A
;
;CHECK WHETHER DISK HEAD LOADED
;
	IN	A,(DFLAGS)		;READ FLAGS
	AND	HDLDM			;HEAD LOADED?
	LD	A,D			;CONTROL BYTE
	OUT	(DCONTR),A		;[THIS MUST BE DONE AFTER
;					;THE INPUT FROM DFLAGS
;					;BECAUSE OF AUTO WAIT]
	LD	A,4			;HEAD NOT LOADED
	RET	Z
	SUB	A			;HEAD LOADED
	RET
;
;
;SELECT DISK DRIVE
;
;INPUT - B CONTAINS DISK DRIVE (0,1,2,3)
;	 A BIT 4 CONTAINS 1 IF MAXI
;
;OUTPUT - A CONTAINS SELECT BYTE
;	  B CONTAINS 128
;	  C CONTAINS DATA PORT #
;
;REGISTERS A,F,B,C ARE CHANGED
;
;
SELECT:	AND	MAXIM		;GET MAXI FLAG ONLY
	LD	C,A		;SAVE FLAG
	INC	B		;CALCULATE DISK SELECT
	SUB	A
	SCF
SEL300: RLA
	DJNZ	SEL300
	OR	C		;MAXI FLAG
	OR	20H		;MOTOR ON
	LD	BC,8000H+DDATA
	RET
;
;
;
;CHECK INPUT & RETURN WITH DATA IF READY
;
CHKIN:	IN	A,(STAT)
	AND	DAV
	RET	Z
	IN	A,(DATA)
	RET
;
;
; GET CHARACTER FROM INPUT.
;
GBYTE:	CALL	CHKIN
	JR	Z,GBYTE
	AND	7FH	
	RET
;
;
; COMMAND
; CHANGE BAUD RATE OF THE SERIAL PORT
;
INITBR:
	CALL	SKSGCR		;REQUIRE CR
;				;[CONTINUE BELOW]
;
;
; INITIALIZE BAUD RATE OF THE CURRENT DEVICE.
;
; PUSH CARRIAGE-RETURN TO SELECT THE PROPER BAUD
; RATE FOR THE CURRENT TERMINAL.  (THE MAXIMUM
; NUMBER OF CARRIAGE-RETURNS REQUIRED IS FOUR.)
;
; ANY OF THE FOLLOWING BAUD RATES CAN BE SELECTED:
; 19200, 9600, 4800, 2400, 1200, 300, 150, 110.
;
INITBAUD: LD	HL,BAUDRS
	LD	C,BAUD
	LD	A,19H		;OCTUPLE THE CLOCK
IT1:	OUT	(COMMND),A	;& RESET CURRENT DEVICE
	OUTI
	CALL	GBYTE
	CALL	GBYTE
	CP	CR
	LD	A,9		;SLOW THE CLOCK
	JR	NZ,IT1
	RET
;
;
; COMMAND
; CHANGE LOCATION OF THE SYSTEM STACK
;
KICKSTK:
	CALL	L1NCR
	JR	LOADIX		;IX STORES INITIAL SP VALUE
;
;
;----------------------------------------------------------
; MONITOR ENTRY POINT
;----------------------------------------------------------
;
; ENTER THE MONITOR WITH THE STK PNTR LOADED & WITH
; DE -> THE DISK FLAGS.  [THIS IS ALSO
; THE TOP OF THE STACK.]
;
MONITR:
	CALL	PMSGFOLLOWING
	DEFB	CR,CR,'CROMEMCO RDOS',('1'+80H)
;
LOADIX:
	SUB	A
	LD	(DE),A		;CLEAR DISK MODE
	PUSH	DE
	POP	IX		;IX STORES INITIAL SP VALUE
;
CLEANSTACK:
	LD	SP,IX		;RE-INITIALIZE SP
;
;
; GET COMMAND.
; RETURNS VALUE IN HL & JUMPS TO THAT ADDR.
;
	CALL	CRLF
CMND:	LD	HL,CMND		;SET-UP RETURN
	PUSH	IX
	EX	(SP),HL		;RETN ADDR ON STK
	LD	C,(HL)		;HL -> DISK FLAGS
	BIT	DISKMODE,C
	INC	HL		; -> DISK LETTER
	CALL	NZ,PMSG		;DISK MODE PROMPT
	CALL	PMSGFOLLOWING
	DEFB	(';'+80H)	;THE REGULAR PROMPT
;
	CALL	SKSGO		;GET THE COMMAND
	JR	NZ,CM6
	LD	(IX+0),0	;CR. RESET DISK MODE.
	RET
;
CM6:	SUB	'A'+CASE	; < 'A'?
	JR	C,ERROR
	CP	('W'-'A')+1	; > 'W'?
	JR	NC,ERROR
	LD	E,A
	LD	D,0
;
	CALL	SKSGO		;NEXT COMMAND CHARACTER
	CP	';'
	JR	Z,DISKSELECT
	EX	DE,HL
	ADD	HL,HL		;TIMES 2
	LD	DE,CMNDTBL
	ADD	HL,DE		; + TBL ADDR
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	EX	DE,HL
	CP	'M'+CASE	;(USED IN SUBST & DISPL)
	JP	(HL)
;
;
; DISK SELECT
; ENTER WITH E CONTAINING THE DISK NUMBER
;
DISKSELECT:
	LD	A,E		;DISK NUMBER
	CP	NDRIVES		;A THROUGH D ONLY
	JR	NC,ERROR
	LD	B,E		;SAVE DISK #
	PUSH	IX
	POP	HL		; -> DISK FLAGS
	OR	(1 < DISKMODE)+(1 < MAXI)+(1 < FASTSEEK)
	LD	(HL),A		;DISK # & FLAGS
	LD	D,H
	LD	E,L
	INC	DE		; -> DISK LETTER
	LD	A,B
	ADD	A,'A'
	LD	(DE),A		;DISK LETTER
	CALL	GCHR
	CP	';'
	JR	NZ,DS2
	RES	FASTSEEK,(HL)	;NOT FAST SEEK
	INC	DE
	LD	(DE),A		;PART OF DISKMODE PROMPT
	CALL	GCHR
	CP	';'
	JR	NZ,DS2
	RES	MAXI,(HL)	;MINI FLOPPY
	INC	DE
	LD	(DE),A
	SUB	A
;
DS2:	CALL	SKSGCR		;ALSO EXCGS DE & HL
	SET	7,(HL)		;MARK END-OF-MSG
;
	LD	A,(DE)		;DISK FLAGS
	CALL	DHOME
	LD	A,'H'		;IN CASE OF HOME ERROR
;
DERRCK:
	RET	Z		;IF NO ERROR, DONE
;
PERRMSG:
	CALL	PMSGFOLLOWING
	DEFB	' ERR',(' '+80H)
	CALL	PCHR		;ERROR LETTER
	LD	A,B		;ERROR NUMBER
;
;
; PRINT THE 2 HEX DIGITS IN THE A-REGISTER
; AND CLEAN STACK.
;
P2HXCLEAN:
	CALL	P2HEX
	JR	CLEANV
;
;
; PRINT CRLF
;
CRLF:
	LD	A,CR
	JR	PCHR
;
;
; COMMAND
; EXAMINE INPUT PORT
;
EXMINPUT:
	CALL	L1NCR
	LD	C,E		;PORT #
	IN	A,(C)
	JR	P2HXCLEAN	;PRINT THE VALUE, CRLF
;
;
; ERROR & ESCAPE. RETURN TO CMND WITH SP
; RE-INITIALIZED.
;
ERROR:
	CALL	PMSGFOLLOWING
	DEFB	('?'+80H)
ESCAPE:
CLEANV:
	JP	CLEANSTACK
;
;
; GET NEXT SECTOR FOR THE READ & WRITE DISK
; ROUTINES.  PRESERVES HL AND, BEFOR RETURNING,
; POPS DE AND BC FROM THE STACK.
;
NEXTSC:
	EXX
	POP	HL		;RETURN ADDR
	EXX
	POP	DE
	JR	Z,NS2		;SKIP IF NO ERROR
	DEC	D		;TRY AGAIN?
	JR	Z,PERRMSG
	JR	NS4		;YES. USE OLD MEM PNTR
;
NS2:	LD	BC,-81H		;NO ERROR
	ADD	IY,BC		;BUMP THE INCREMENT
	INC	IY
	EX	(SP),HL		;USE LATEST MEM PNTR
	LD	D,10		;RELOAD RETRIAL COUNTER
;
NS4:	POP	HL		;MEM PNTR
	POP	BC
	LD	A,C		;RELOAD DISK FLAGS
	EXX
	PUSH	HL		;RETURN ADDR
	EXX
	RET	NZ		;IF ERROR, DONE
;
	CALL	NC,PTRKSC	;IF NEGATIVE, DONE:
	JR	NC,CLEANV	;PRINT TRK, SEC, CLEAN STK.
;
	INC	E		;BUMP SECTOR #
	CALL	CHKSECNO
	RET	NC		;DONE IF # OK
	IN	A,(DTRACK)	;GET TRACK #
	INC	A		;BUMP IT
	LD	E,A
	PUSH	BC
	CALL	SEEKNXT		;SEEK NEXT TRACK
	POP	BC
	LD	A,C		;DISK FLAGS
	LD	E,1		;SECTOR 1
	RET
;
;
; PRINT SPACE. ALTERS A.
;
SPACE:	LD	A,' '		;(CONTINUE BELOW)
;
;
; PRINT THE CHARACTER IN THE A-REGISTER.
; (CHKS INPUT FOR ESC.) PRESERVES ALL REGS.
;
PCHR:	PUSH	AF		;SAVE THE CHAR
PC1:	AND	7FH
	CP	ESC
	JR	Z,ESCAPE
	CP	ALT		;ALT MODE?
	JR	Z,ESCAPE
	CALL	CHKIN	
	JR	NZ,PC1
;
PC2:	IN	A,(STAT)
	AND	TBE
	JR	Z,PC2
	POP	AF
	PUSH	AF
	AND	7FH
	OUT	(DATA),A
	CP	CR
	JR	NZ,PC3
	CALL	PMSGFOLLOWING
	DEFB	LF,0,80H
PC3:	POP	AF
	RET
;
;
; GET CHARACTER.  RETURNS IT IN A.
; ALTERS F.
;
GCHR:	CALL	GBYTE
	CALL	PCHR
	CP	61H		;CONVERT LOWER CASE
	RET	C		;TO UPPER.
	SUB	20H
	RET
;
;
; LOADS HL WITH SOURCE ADDR, BC & DE
; WITH THE INCREMENT.  ENDS WITH A CRLF.
;
L2NCR0:	SUB	A
;
L2NCR:	CALL	LD2N
;
; SKIP INITIAL SPACES.
; IF DELIMITER NOT A CR, ERROR
;
SKSGCR:	CALL	SKSG		;WRITE FOR NON-SPACE
	JR	NZ,ERROR	;IF NOT CR, ERROR
	EX	DE,HL
	RET
;
;
; PRINT THE NUMBER IN HL, FOLLOWED BY A COLON.
; PRESERVES ALL REGISTERS EXCEPT A.
;
PCADDR:	CALL	CRLF
;
PADDR:	CALL	PNHL
	LD	A,':'
	JR	PCHR
;
;
; COMMAND
;
VERIF:	CALL	L3NCR		;GET 3 OPERANDS
;
; COMPARES TWO AREAS OF MEMORY.  ENTER WITH
; SOURCE IN HL, DESTINATION IN DE & COUNT
; IN BC.  ALTERS ALL REGISTERS.
;
VRFY:
	LD	A,(DE)
	CPI			;COMPARE TO SOURCE
	DEC	HL
	CALL	NZ,PNHL		;PRINT SOURCE ADDR
	CALL	NZ,PSNM		; & CONTENTS
	EX	DE,HL
	CALL	NZ,PSNM		; & DEST CONTENTS
	CALL	NZ,PSNHL	; & DEST ADDR
	CALL	NZ,CRLF
	EX	DE,HL
	INC	HL
	INC	DE
	RET	PO		; IF BC=0, DONE.
	JR	VRFY
;
;	COMMAND
;
MOVE:
	CALL	L3NCR		;OPERANDS
	PUSH	HL
	PUSH	DE
	PUSH	BC
	LDIR
	POP	BC
	POP	DE
	POP	HL
	JR	VRFY
;
;
;
; LOAD TWO NUMBERS.  LOADS DE WITH THE BEGINNING
; ADDR, N1. LOADS BC & HL WITH THE INCREMENT
; N2-N1+1 (OR WITH N2 IF THE OPR IS 'S').
; RETURNS WITH LAST DELIMITER IN A.
;
;
LD2N:	CALL	GNHL		;N1 TO HL, DELIM TO A
	EX	DE,HL		;SAVE N1 IN DE
	CALL	SKSG		;GET NEXT NON-SPACE
	CP	'S'+CASE	;SWATH?
	JR	NZ,L2N1
;
	CALL	GNHLO		;YES. INCREMENT TO HL.
	JR	L2N2
;
L2N1:	CALL	GNHL		;INCREMENT
	OR	A		;CLEAR CY
	SBC	HL,DE		;N2-N1
	INC	HL		;INCLUDE END POINT
L2N2:	LD	B,H
	LD	C,L		;BC GET THE INCRM
	PUSH	HL
	POP	IY		;& SO DOES IY.
	RET
;
;
; LOAD 3 OPERANDS. HL GETS THE SOURCE, BC
; THE INCREMENT, AND DE THE 3RD OPERAND.
;
L3NCR0:	SUB	A
;
L3NCR:	CALL	LD2N
;  (CONTINUE BELOW)
;
;
; ENTER WITH SPACE OR THE FIRST DIGIT
; OF A NUMBER IN A.  LOADS HL WITH
; A NEW NUMBER & THEN EXCHANGES
; DE & HL. FINISHES WITH A CRLF.
;
L1NCR:	CALL	GNHL		;SKIP SPACES, LOAD HL
	JR	SKSGCR		;WAIT FOR A CR
;
;
; CLEARS HL. IF ENTERED WITH HEX CHAR IN A,
; SHITS IT INTO HL. O/W, IGNORES LEADING
; SPACES. FIRST CHAR MUST BE HEX. CONTINUES
; SHIFT UNTIL A NON-HEX CHAR RECEIVED & THEN
; RETURNS WITH THE LATTER IN A.
; PRESERVES B,C,D,E.
;
;
GNHLO:	SUB	A
;
GNHL:	PUSH	BC		;SAVE
	LD	HL,0		;CLEAR BUFFER
; STRIP LEADING SPACES & GET CHAR
	CALL	SKSG
; FIRST CHAR MUST BE HEX
	CALL	HEXSH		;IF HEX, SHIFT INTO HL
	JP	C,ERROR		;O/W, ERROR
GN1:	CALL	GCHR
	CALL	HEXSH		;IF HEX SHIFT INTO HL
	LD	A,B		;RESTORE CHAR
	JR	NC,GN1		;IF HEX, CONTINUE
	POP	BC		;IF NON-HEX,DONE
	RET
;
;
; IF A CONTAINS HEX CHAR, SHIFTS BINARY EQUIVALENT
; INTO HL.  IF NOT HEX, RET WITH CY SET. SAVES
; ORIGINAL CHAR IN B
;
HEXSH:	LD	B,A
	SUB	'0'		; < '0'?
	RET	C
	ADD	A,'0'-('G'+CASE)
	RET	C
	SUB	'A'-'G'
	JR	NC,HX1		;OK IF >= 'A'
	ADD	A,('A'+CASE)-('9'+1)
	RET	C
HX1:	ADD	A,('9'+1)-'0'
; THE A-REG NOW CONTAINS THE HEX DIGIT IN BINARY.
; (THE HIGH-ORDER NIBBLE OF A IS 0.)
HXSH4:	ADD	HL,HL		;SHIFT 4 BITS INTO HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	OR	L
	LD	L,A
	RET
;
;
; RETURNS WITH A NON-SPACE IN THE A-REG.
; IF ENTERED WITH A-REG CONTAINING A NULL
; OR A SPACE, GETS NEW CHARS UNTIL FIRST
; NON-SPACE OCCURS. ALTERS AF.
;
SKSGO:	SUB	A
;
SKSG:	OR	A		;DOES A CONTAIN NULL?
SK1:	CALL	Z,GCHR
	CP	20H		;SPACE?
	JR	Z,SK1
	CP	CR
	RET
;
;
;
; PRINT SPACE FOLLOWED BY THE NUMBER POINTED
; TO BY HL. ALTERS A ONLY.
;
PSNM:	CALL	SPACE
; (CONTINUE BELOW)
;
; PRINTS THE NUMBER POINTED TO BY HL.
; PRESERVES ALL REGISTERS BUT A.
;
PNM:	LD	A,(HL)
	JR	P2HEX
;
;
;
; PRINT THE NUMBER IN HL.
; PRESERVES ALL BUT A.
;
PSNHL:	CALL	SPACE
;
PNHL:	LD	A,H
   	CALL	P2HEX
	LD	A,L
;				;(CONTINUE BELOW)
;
; PRINT THE NUMBER IN THE A-REGISTER
; PRESERVES ALL REGISTERS.
;
P2HEX:	CALL	P1HEX
	RRA
P1HEX:	RRA
	RRA
	RRA
	RRA
	PUSH	AF
	AND	0FH		;MASK
        CP      10              ; <= 9?
	JR	C,PH1
	ADD	A,7		;A THRU F
PH1:	ADD	A,30H		;ASCII BIAS
	CALL	PCHR		;PRINT IT
	POP	AF
	RET
;
;
; PRINT MESSAGE. ENTER WITH ADDR OF MSG
; IN HL.  THE MESSAGE IS TERMINATED
; AFTER PRINTING A CHARACTER WHOSE
; PARITY BIT WAS SET.
; PRESERVES FLAGS, INCREMENTS HL.
;
;
;
PMSG:	PUSH	AF		;SAVE
PS1:	LD	A,(HL)
	INC	HL
	CALL	PCHR
	RLA			;LAST CHARACTER?
	JR	NC,PS1		;IF NOT, LOOP
	POP	AF
	RET
;
;
; PRINTS THE MESSAGE FOLLOWING THE CALL
; TO THIS ROUTINE.
; PRESERVES ALL REGISTERS
;
PMSGFOLLOWING:
	EX	(SP),HL
	CALL	PMSG
	EX	(SP),HL
	RET
;
;
; COMMAND
;
; GO <ADDR>
; EXECUTION BEGINS AT ADDR.
;
GO:
	POP	HL		;CLEAN STACK
	CALL	L1NCR		;GET ADDR
	EX	DE,HL
	JP	(HL)
;
;
; COMMAND.  DISPLAY MEMORY.
;
; DM <STARTING ADDR> <ENDING ADDR OR SWATH>
;
DSPM:
	JR	NZ,ERRORV	;IF NOT 'M', ERROR
	CALL	L2NCR0		;GET OPERANDS
DSPM1:	LD	D,16		;BYTE COUNT
	CALL	PCADDR		;ADDRESS
DM2:	CALL	PSNM		;MEM CONTENTS
	CPI			;INC HL & DEC BC
	JP	PO,CRLF
	DEC	D
	JR	Z,DSPM1
	LD	A,D
	AND	3
	CALL	Z,SPACE
	JR	DM2
;
;
SHANDLER:
	JR	Z,SUBSM		;IF 'M', SUBSM
;
;
; DISK SEEK
;
SEEKR:
	BIT	DISKMODE,C
	JR	Z,ERRORV
	CALL	L1NCR		;E = TRACK #
SEEKNXT:
	LD	A,76		;MAX TRACK #, MAXI DISK
	LD	D,39		;MAX TRACK #, MINI DISK
	CALL	CHKNO		;CHECK #
	JR	C,ERRORV
	LD	D,E		;TRACK #
	CALL	DSEEK
	LD	A,'S'		;IN CASE OF SEEK ERROR
;
DERCKV:	JP	DERRCK		;DISK ERROR CHECK
;
;
ERRORV:	JP	ERROR
;
;
; COMMAND.  SUBSTITUTE MEMORY LOCATION.
;
; SM <ADDR>
;
SUBSM:
	SUB	A
	CALL	L1NCR
	EX	DE,HL		;HL GETS ADDR
SM1:	CALL	Z,PCADDR
	CALL	Z,SPACE
; PRINT CURRENT VALUE, REQUEST NEW VALUE &
; PRINT IT IF GIVEN
	CALL	PNM		;PRINT (HL)
	CALL	PMSGFOLLOWING
	DEFB	('.'+80H)	;THE PROMPT
	CALL	GCHR
	CP	'.'+1		;IF <= '.',
	CALL	C,PCHR		;NO SUBSTITUTION.
	JR	C,SM2
	EX	DE,HL
	CALL	GNHL		;GET NEW VALUE
	EX	DE,HL
	LD	(HL),E
SM2:	CP	CR
	CALL	NZ,SPACE
;
	RET	Z		;IF CR, DONE.
	INC	HL
	LD	A,7		;PRINT ADDRESS IF IT
	AND	L		;ITS A MULTIPLE OF 8
	JR	SM1
;
;
RHANDLER:
	CP	'D'+CASE
	JR	NZ,ERRORV
;
; READ DISK
;
READDR:
	CALL	SECSETUP
RD2:	PUSH	BC
	PUSH	HL
	PUSH	DE
	CALL	DREAD
	LD	A,'R'		;IN CASE OF READ ERROR
	CALL	NEXTSC		;NEXT SECTOR [POPS STK.]
	JR	RD2
;
;
WHANDLER:
	CP	'D'+CASE
	JR	NZ,ERRORV
;
; WRITE DISK
;
WRITDR:
	CALL	SECSETUP
WD2:	PUSH	BC
	PUSH	HL
	PUSH	DE
	CALL	DWRITE
	LD	A,'W'		;IN CASE OF WRITE ERROR
	CALL	NEXTSC		;[POPS STACK]
	JR	WD2
;
;
; GET MEMORY ADDRESS, SECTOR # AND CHECK IT,
; AND LOAD B & C
;
SECSETUP:
	BIT	DISKMODE,C
	JR	Z,ERRORV
	PUSH	BC
	CALL	L3NCR0		;BUFFER ADDRS & SEC #
	POP	BC
	CALL	CHKSECNO
	JR	C,ERRORV
;
;
; PRINT TRACK & SECTOR #'S
;
PTRKSC:
	IN	A,(DTRACK)
	LD	D,A
	EX	DE,HL
	CALL	PSNHL		;PRINT TRK & SEC
	EX	DE,HL
	LD	A,C		;DISK FLAGS
	LD	D,10		;# OF RETRIALS
	RET
;
;
CHKSECNO:
	LD	A,26		;MAX SEC #, MAXI DISK
	LD	D,18		;MAX SEC #, MINI DISK
;
;
CHKNO:
	BIT	MAXI,C
	JR	NZ,CN2
	LD	A,D
CN2:	CP	E
	RET	C
	LD	A,C
	AND	NDRIVES-1
	LD	B,A		;DISK #
	LD	A,C		;DISK FLAGS
	RET
;
;
; COMMAND
; OUT <DATA-BYTE> <PORT NUMBER>
;
OUTP:	CALL	GNHL
	EX	DE,HL		;E GETS DATA
	CALL	L1NCR		;GET PORT NUMBER
;
	LD	C,E		; TO C
	OUT	(C),L
	RET
;
;
; BAUD RATES:
; 19200, 9600, 4800, 2400, 1200, 300, 150, 110.
;
BAUDRS:	DEFB	90H,0C0H,0A0H,90H,88H,84H,82H,1
;
;
CMNDTBL:
	DEFW	ERROR		;A
	DEFW	BOOTMC		;BOOT CDOS
	DEFW	ERROR		;C
	DEFW	DSPM		;DISPLAY MEMORY
	DEFW	EXMINPUT	;EXAMINE INPUT PORT
	DEFW	ERROR		;F
	DEFW	GO		;GO [TRANSFER OF CONTROL]
	DEFW	ERROR		;H
	DEFW	INITBR		;INITIALIZE BAUD RATE
	DEFW	ERROR		;J
	DEFW	KICKSTK		;KICK SYSTEM STACK
	DEFW	ERROR		;L
	DEFW	MOVE		;MOVE A BLOCK OF MEMORY
	DEFW	ERROR		;N
	DEFW	OUTP		;OUTPUT
	DEFW	ERROR		;P
	DEFW	ERROR		;Q
	DEFW	RHANDLER	;READ DISK
	DEFW	SHANDLER	;SUBSTITUTE MEM; SEEK TRACK
	DEFW	ERROR		;T
	DEFW	ERROR		;U
	DEFW	VERIF		;VERIFY BLOCKS OF MEMORY
	DEFW	WHANDLER	;WRITE DISK
;
;
LASTBYTE: EQU	$-1
;
	END
