;********************************************************
;*							*
;*	BASIC HEX MONITOR FOR Z-80 PROCESSORS		*
;*					3-Aug-80	*
;*							*
;********************************************************
;
;
;
;
PROMPT:	CALL	PNEXT
	DEFB	CR,LF
	DEFM	'* '
	DEFB	EOT
	LD	HL,LINBUF
	LD	C,32
	CALL	GETLIN		;INPUT A BUFERED CONSOLE LINE
	JR	C,WHAT-$	;PRINT 'WHAT ?' IF INPUT ERROR

	XOR	A
	LD	(ESCFLG),A
	CALL	CRLFS
	LD	A,(LINBUF)	;GET FIRST CHARACTER IN LINE
	CP	CR
	JR	Z,PROMPT-$	;JUMP IF A NULL LINE
	LD	HL,CMDTAB	;SEARCH FOR A MATCHING CHARACTER
	LD	BC,CMDSIZ/3	; IN COMMAND SEARCH TABLE
	CALL	SEARCH
	JR	NZ,WHAT-$	;TRY AGAIN IF SEACRH FAILS
	PUSH	BC
	LD	IY,LINBUF+1
	CALL	PARAMS		;INPUT NUMERIC PARAMETERS FROM
	POP	IX		; LINE BUFFER AND TEST IF ERROR
	JR	C,WHAT-$
	LD	HL,(PARAM1)
	LD	DE,(PARAM2)
	LD	BC,(PARAM3)
	CALL	CALLX		;CALL SUBROUTINE @ IX
	JR	NC,PROMPT-$	;GO BACK TO PROMPT IF NO ERRORS

WHAT:	CALL	PNEXT
	DEFM	' what ?'
	DEFB	'G'-64		;SAY 'what ?' AND BEEP THE BELL
	DEFB	EOT
	JR	PROMPT-$
;
;
CALLX:	JP	(IX)		;CALL SUBROUTINE @ IX
;
;
;
CMDTAB:	DEFB	'R'
	DEFB	'O'
	DEFB	'I'
	DEFB	'G'
	DEFB	'T'
	DEFB	'F'
	DEFB	'M'
	DEFB	'C'
	DEFB	'B'
	DEFB	'D'
	DEFB	'S'

	DEFW	SWITCH		;SWITCH CONSOLE OUTPUT VECTOR
	DEFW	MEMDMP		;DUMP MEMORY IN HEX/ASCII
	DEFW	BOOT		;BOOT UP CP/M
	DEFW	BLOCK		;MEMORY BLOCK MOVE
	DEFW	VIEW		;MEMORY EXAMINE/CHANGE
	DEFW	FILL		;FILL MEMORY
	DEFW	TEST		;RAM DIAGNOSTIC
	DEFW	GOTO		;JUMP TO MEMORY LOCATION
	DEFW	INCMD		;READ FROM INPUT PORT
	DEFW	OUTCMD		;WRITE TO OUTPUT PORT
	DEFW	DSKCMD		;DISPLAY DISK SECTOR DATA

CMDSIZ	EQU	$-CMDTAB
;
;
;********************************************************
;*							*
;*	MONITOR COMMAND ACTION ROUTINES PACKAGE		*
;*							*
;********************************************************
;
;
;
;
;
;	-- DISK BOOT LOADER COMMAND --
;
BOOT:	LD	C,0		;SELECT DRIVE 0 FOR BOOT LOAD
	CALL	SELECT
	JR	NZ,DSKERR-$
	CALL	HOME		;HOME HEAD TO TRACK 0
	JR	NZ,DSKERR-$	;ERROR IF NOT READY OR AT TR0
	LD	HL,0080H	;POINT TO CP/M READ BUFFER
	LD	C,1		;SELECT SECTOR 1
	CALL	READ		;READ TRACK 0/ SECTOR 1
	JR	NZ,DSKERR-$
	POP	AF		;CLEAN UP STACK
	JP	0080H		;GO EXECUTE LOADER
;
;
;	-- DISK SECTOR READ COMMAND --
;
DSKCMD:	CP	3		;CHECK PARAMETER COUNT
	SCF
	RET	NZ
	LD	C,L		;USE FIRST ARG AS UNIT#
	CALL	SELECT
	JR	NZ,DSKERR-$
	LD	HL,PARAM2
	LD	C,(HL)		;USE SECOND ARG AS TRACK#
	CALL	SEEK
	JR	NZ,DSKERR-$
	LD	HL,PARAM3
	LD	C,(HL)		;USE THIRD ARG AS SECTOR#
	LD	HL,0080H
	CALL	READ
	SET	0,A		;MARK ERROR BYTE AS DUE TO READ
	JR	NZ,DSKERR-$
	LD	HL,0080H
	LD	DE,8
	JP	DUMP		;DUMP DISK READ BUFFER AND RETURN
;
;
DSKERR:	LD	C,A		;SAVE 1771 STATUS
	CALL	PNEXT
	DEFM	'disk error '
	DEFB	EOT
	LD	B,8		;PRINT 1771 ERROR BYTE IN BINARY
DSKR2:	XOR	A
	RL	C
	ADC	A,'0'		;TRANSFORM A INTO ASCII '1' OR '0'
	CALL	OUTPUT
	DJNZ	DSKR2-$		;REPEAT FOR 8 BITS
	OR	A
	RET
;
;
;
;	-- MEMORY DUMP COMMAND --
;
MEMDMP:	DEC	A		;CHECK PARAMETER COUNT
	JR	Z,MDMP2-$
	DEC	A
	JR	Z,MDMP3-$
MDMP1:	LD	HL,(LAST)
MDMP2:	LD	DE,16
	JR	MDMP3B-$

MDMP3:	EX	DE,HL
	SBC	HL,DE		;DERRIVE BYTECOUNT FOR DUMP RANGE
	LD	B,4
MDMP3A:	SRL	H		;DIVIDE BYTECOUNT BY 16
	RR	L
	DJNZ	MDMP3A-$
	INC	HL
	EX	DE,HL
MDMP3B:	CALL	DUMP		;DUMP DE*16 BYTES STRTING AT HL
	LD	(LAST),HL
	RET	
;
;
DUMP:	PUSH	HL		;SAVE STARTING ADDRESS
	CALL	PUT4HS		;PRINT STARTING ADDRESS IN HEX
	CALL	SPACE
	LD	B,16
DUMP2:	LD	A,(HL)		;GET A DATA BYTE @ HL
	INC	HL
	CALL	PUT2HS		;PRINT THE DATA IN HEX
	DJNZ	DUMP2-$		;REPEAT 16 TIMES
	POP	HL		;RESTORE STARTING ADDRESS
	LD	B,16
DUMP3:	LD	A,(HL)		;GET BACK DATA BYTE @ HL
	INC	HL
	RES	7,A
	CP	20H
	JR	C,DUMP4-$
	CP	7FH
	JR	C,DUMP5-$
DUMP4:	LD	A,'.'		;PRINT A DOT IF DATA < 20 OR > 7F
DUMP5:	CALL	OUTPUT		;PRINT ASCII CHARACTER IN A
	DJNZ	DUMP3-$
	CALL	CRLFS
	RET	NZ		;EXIT IF ESCAPE REQUEST IS INDICATED
	DEC	DE
	LD	A,D
	OR	E
	JR	NZ,DUMP-$
	RET
;
;
;
;
;	-- MEMORY EXAMINE COMMAND --
;
VIEW:	CALL	MDATA
	CALL	ECHO
	CP	CR
	JR	Z,VIEW4-$
	CP	'-'
	JR	Z,VIEW5-$
VIEW2:	CALL	ASCHEX
	CCF	
	RET	NC
	RLCA	
	RLCA	
	RLCA	
	RLCA	
	LD	C,A
	CALL	ECHO
	CALL	ASCHEX
	CCF	
	RET	NC
	OR	C
VIEW3:	LD	(HL),A
	CALL	CHECK
VIEW4:	INC	HL
	INC	HL
VIEW5:	DEC	HL
	JR	VIEW-$
;
;
;
;	-- JUMP TO MEMORY LOCATION COMMAND --
;
GOTO:	DEC	A		;CHECK PARAMETER COUNT
	SCF
	RET	NZ
	PUSH	HL
	POP	IX
	CALL	CALLX		;CALL ADDRESS PASSED IN HL
	OR	A
	RET			;RETURN IF WE GET BACK AGAIN
;
;
;
;	-- MEMORY READ/WRITE DIAGNOSTIC COMMAND --
;
TEST:	CP	2		;CHECK PARAMETER COUNT
	SCF
	RET	NZ
	INC	DE
	LD	E,D		;GET ENDING PAGE ADDRESS INTO E
	LD	D,H		;GET STARTING PAGE ADDRESS INTO D
	LD	B,0		;INITIALIZE PASS COUNTER
TEST1:	LD	H,D		;POINT HL TO START OF BLOCK
	LD	L,0
TEST2:	LD	A,L
	XOR	H		;GENERATE TEST BYTE
	XOR	B
	LD	(HL),A		;STORE BYTE IN RAM
	INC	HL
	LD	A,H
	CP	E		;CHECK FOR END OF TEST BLOCK
	JR	NZ,TEST2-$
;				NOW READ BACK EACH BYTE & COMPARE
	LD	H,D
	LD	L,0		;POINT HL BACK TO START
TEST3:	LD	A,L
	XOR	H		;RE-GENERATE TEST BYTE DATA
	XOR	B
	CALL	CHECK		;VERIFY MEMORY DATA STILL GOOD
	RET	NZ		;EXIT IF ESCAPE REQUEST IS INDICATED
	INC	HL		; ELSE GO ON TO NEXT BYTE
	LD	A,H
	CP	E		;CHECK FOR END OF BLOCK
	JR	NZ,TEST3-$
	INC	B		;BUMP PASS COUNT
	LD	A,'+'
	CALL	OUTPUT		;PRINT '+' AND ALLOW FOR EXIT
	JR	Z,TEST1-$	;DO ANOTHER PASS IF NO ESCAPE
	RET
;
;
;
CHECK:	CP	(HL)
	RET	Z		;RETURN IF (HL)=A
	PUSH	AF
	CALL	MDATA		;PRINT WHAT WAS ACTUALLY READ
	CALL	PNEXT
	DEFM	'should='
	DEFB	EOT
	POP	AF
	JP	PUT2HS		;PRINT WHAT SHOULD HAVE BEEN READ
;
;
MDATA:	CALL	CRLFS
	CALL	PUT4HS
	LD	A,(HL)
	JP	PUT2HS
;
;
;
;	-- FILL MEMORY WITH CONSTANT COMMAND --
;
FILL:	CP	3		;CHECK IF PARAMETER COUNT=3
	SCF	
	RET	NZ
FILL1:	LD	(HL),C
	PUSH	HL
	OR	A
	SBC	HL,DE		;COMPARE HL TO END ADDRESS IN DE
	POP	HL
	INC	HL		;ADVANCE POINTER AFTER COMPARISON
	JR	C,FILL1-$
	RET
;
;
;
;
;	-- MEMORY BLOCK MOVE COMMAND --
;
BLOCK:	CP	3		;CHECK IF PARAMETER COUNT=3
	SCF	
	RET	NZ
	CALL	BLOCAD
	LD	A,C
	OR	B
	RET	Z		;EXIT NOW IF BC=0
	LDIR
	RET
;
;
;
BLOCAD:	EX	DE,HL
	OR	A		;CLEAR CARRY
	SBC	HL,DE		;GET DIFFRENCE BETWEEN
	EX	DE,HL		;HL & DE FOR BYTECOUNT
	PUSH	DE
	PUSH	BC
	POP	DE		;GET OLD BC INTO DE
	POP	BC
	INC	BC		;GET COUNT+1 INTO BC
	RET	
;
;
;
;
;	-- READ FROM INPUT PORT COMMAND --
;
INCMD:	DEC	A		;CHECK IF PARAMETER COUNT=1
	SCF
	RET	NZ
	LD	C,L		;POINT C TO INPUT PORT
IN1:	CALL	CRLFS
	LD	A,C
	CALL	PUT2HS
	IN	A,(C)
	CALL	PUT2HS
	CALL	ECHO
	CP	CR
	JR	Z,IN2-$
	CP	'-'
	JR	Z,IN3-$
	OR	A
	RET
 
IN2:	INC	C
	INC	C
IN3:	DEC	C
	JR	IN1-$
;
;
;
;	-- WRITE TO OUTPUT PORT COMMAND --
;
OUTCMD:	CP	2		;CHECK IF PARAMETER COUNT=2
	SCF
	RET	NZ
	LD	C,L		;POINT C TO OUTPUT PORT
	OUT	(C),E		;OUTPUT DATA PASSED IN E
	OR	A
	RET
;
;
;	-- SWITCH CONSOLE OUTPUT DEVICE COMMAND --
;
SWITCH:	LD	HL,COFLAG
	INC	(HL)		;TOGGLE CONSOLE OUTPUT TYPE FLAG
	BIT	0,(HL)
	LD	HL,SIOOUT
	JR	Z,SWIT2-$	;JUMP IF ZERO TO ONE TRANSITION
	LD	HL,CRTOUT
SWIT2:	LD	(CONOUT+1),HL	;STORE NEW CONSOLE OUTPUT ADDRESS
	RET
;
;
;********************************************************
;*							*
;*	CONSOLE I/O PACKAGE AND UTILITY ROUTINES	*
;*							*
;********************************************************
;
;
;
GETLIN:	LD	B,C		;SAVE MAX LINE LENGTH PARAMETER IN B
GLIN1:	CALL	ECHO		;GET A CHARACTER FROM THE CONSOLE
	CP	CR		;CHECK FOR CARRIAGE RETURN
	JR	Z,GLIN2-$
	CP	'H'-64		;CHECK FOR CTL-H BACKSPACE
	JR	Z,GLIN4-$
	CP	' '
	RET	C		;OTHER CONTROL CHARACTERS ARE ILLEGAL
	LD	(HL),A
	INC	HL		;STORE CHARACTER IN BUFFER
	DEC	C
	JR	NZ,GLIN1-$	;GET ANOTHER IF THERE'S MORE ROOM
	SCF	
	RET			;RETURN WITH CARRY=1 IF TOO
				;MANY CHARACTERS ARE ENTERED
GLIN2:	LD	(HL),A		;PUT CARRIAGE RETURN ON END OF LINE
	RET			;RETURN WITH CARRY BIT=0
 
GLIN4:	DEC	HL		;DELETE LAST CHARACTER FROM BUFFER
	CALL	PNEXT
	DEFB	' ','H'-64	;PRINT A SPACE TO OVERWRITE THE
	DEFB	EOT		; LAST CHARACTER, THEN DO A BACKSPACE
	INC	C
	LD	A,B		;MAKE SURE YOU'RE NOT TRYING TO
	SUB	C		;BACKSPACE PAST THE START OF THE LINE
	JR	NC,GLIN1-$
	RET	
;
;
;
SEARCH:	CPIR			;SEARCH TABLE @HL FOR MATCH WITH A
	RET	NZ		;EXIT NOW IF SEARCH FAILS
	ADD	HL,BC
	ADD	HL,BC		;ADD RESIDUE FROM CPIR BYTECOUNT
	ADD	HL,BC		; TO HL 3 TIMES TO GET POINTER
	LD	C,(HL)		; TO ADDRESS PART OF TABLE ENTRY
	INC	HL
	LD	B,(HL)
	RET			;EXIT WITH Z=1 TO INDICATE MATCH
;
;
;
;
PARAMS:	LD	BC,0
	LD	A,(IY+0)
	CP	CR		;CHECK IF LINE TERMINATES
	JR	NZ,PARA2-$	; IMMEDIATELY WITH A RETURN
	XOR	A
	RET			;RETURN WITH PARAM COUNT=0 IF SO
 
PARA1:	INC	C
	INC	C
	BIT	3,C
	SCF
	RET	NZ		;ERROR IF > 4 NUMBERS ENTERED
PARA2:	PUSH	BC		;SAVE PARAMETER COUNT
	CALL	GETHEX		;READ A NUMBER FROM LINE BUFFER
	POP	BC
PARA4:	RET	C		;ERROR IF RESULT OVER 16 BITS
	LD	IX,PARAM1	;POINT TO PARAMETER STORAGE AREA
	ADD	IX,BC		;ADD PARAMETER COUNT IN BC
	LD	(IX+0),L
	LD	(IX+1),H	;STORE DATA RETURNED FROM 'GETHEX'
	CP	' '
	JR	Z,PARA1-$	;GET ANOTHER ITEM IF SPACE
	CP	','
	JR	Z,PARA1-$	;GET ANOTHER ITEM IF COMMA
	CP	CR
	SCF			;ELSE CHECK FOR CARRIAGE RETURN
	RET	NZ		; AND EXIT WITH CY=1 IF NOT
PAREND:	LD	A,C
	SRL	A		;A=COUNT OF NUMBERS ENTERED
	INC	A
	RET
;
;	GETHEX CONVERTS ASCII TO BINARY AND DOES
;	HIGH LIMIT CHECKS TO LESS THAN 17 BITS.
;	CARRY SET ON ILLEGAL CONVERSION RESULT
;	TERMINATING CHARACTER RETURNS IN A.
;	HL RETURNS WITH 16 BIT BINARY INTEGER
;
GETHEX:	LD	HL,0
	JR	GNUM3-$
	
GNUM1:	LD	B,4
GNUM2:	ADD	HL,HL		;MULTIPLY RESULT BY 16
	RET	C		;RETURN IF IT OVERFLOWS 16 BITS
	DJNZ	GNUM2-$
	LD	E,A		;APPEND NEW LOW ORDER DIGIT
	LD	D,0		;AND GET RESULT BACK INTO DE
	ADD	HL,DE
	RET	C		;RETURN IF OVERFLOW
GNUM3:	LD	A,(IY+0)	;GET A CHARACTER FROM LINE INPUT
	INC	IY		; BUFFER @ IY AND BUMP IY
	LD	C,A
	CALL	ASCHEX		;CONVERT ASCII TO NUMERIC
	JR	NC,GNUM1-$
	LD	A,C
	OR	A
	RET
;
;
ASCHEX:	SUB	'0'
	RET	C
	CP	10
	CCF
	RET	NC
	SUB	7
	CP	10
	RET	C
	CP	16
	CCF
	RET
;
;
;
PUT4HS:	LD	A,H
	CALL	PUT2HX
	LD	A,L
PUT2HS:	CALL	PUT2HX
	JP	SPACE
;
;
PUT2HX:	PUSH	AF
	RRA	
	RRA	
	RRA	
	RRA	
	CALL	PUTNIB
	POP	AF
PUTNIB:	AND	00001111B
	ADD	A,90H
	DAA
	ADC	A,40H
	DAA
	JP	OUTPUT
;
;
;	PMSG PRINTS THE STRING OF ASCII CHARACTERS
;	POINTED TO BY THE RELATIVE ADDRESS IN DE
;	UNTIL AN EOT IS ENCOUNTERED IN THE STRING.
;
EOT	EQU	04H
CR	EQU	0DH
LF	EQU	0AH
;
	
PNEXT:	EX	(SP),HL
	CALL	PMSG
	EX	(SP),HL
	RET
;
PMSG:	LD	A,(HL)
	INC	HL
	CP	EOT
	RET	Z
	CALL	OUTPUT
	JR	PMSG-$
;
;
;	CRLFS OUTPUTS A RETURN-LINEFEED-SPACE
;	TO THE CONSOLE DEVICE
;
CRLFS:	CALL	PNEXT
	DEFB	CR,LF,EOT
SPACE:	LD	A,' '
	JP	OUTPUT
;
;
;
;	ECHO INPUTS ONE CHARACTER FROM THE CONSOLE
;	DEVICE, PRINTS IT ON THE CONSOLE OUTPUT AND
;	THEN RETURNS IT IN REGISTER A WITH BIT 7 RESET
;
;	OUTPUT PRINTS THE CHARACTER IN REGISTER A ON
;	THE CONSOLE OUTPUT DEVICE AND THEN DOES A CHECK
;	FOR CONSOLE INPUT TO FREEZE OR ABORT OUTPUT.
;

ECHO:	CALL	CONIN		;INPUT A CHARACTER AND ECHO IT
	PUSH	AF
	CALL	CONOUT	
	POP	AF
	CP	'Z'+1
	RET	C
	SUB	32		;CONVERT UPPER CASE TO LOWER CASE
	RET
;
;
;
OUTPUT:	CALL	CONOUT
	CALL	CONST		;SEE IF CONSOLE INPUT IS PENDING
	JR	Z,OUTP2-$
	CALL	CONIN
	CP	CR		;SEE IF CARRIAGE RETURN WAS TYPED
	JR	Z,OUTP1-$
	CALL	CONIN		;WAIT FOR ANOTHER INPUT CHARACTER
	JR	OUTP2-$		; THEN RETURN TO CALLING ROUTINE

OUTP1:	LD	(ESCFLG),A	;SET ESCAPE FLAG TO NON-ZERO VALUE
OUTP2:	LD	A,(ESCFLG)
	OR	A		;RETURN CURRENT STATUS OF ESCAPE
	RET			; FLAG TO CALLING ROUTINE
;
;
;
