;
;VIDEO MONITOR - 80 BY BARRY A. WATZMAN 4/26/79
;1/05/80 VERSION - USES INTERRUPTS AND SOME Z80 CODE
;
;***********************************************
;
;DISK CONTROLLER EQUATES
;
DCOM	EQU	0F8H
DSKST	EQU	DCOM
TRACK	EQU	DCOM+1
SECTP	EQU	DCOM+2
DDATA	EQU	DCOM+3
WAIT	EQU	DCOM+4
;
;CONSOLE AND NON-DISK I/O EQUATES
;
;PORT A: SERIAL IS TTY FOR DIABLO 1620
;	 PARALLEL IS LPT: UNIVERSAL PARALLEL OUTPUT (ONLY) PORT
;
;PORT B: SERIAL IS SP2: ASSIGNABLE TO ANYTHING
;	 PARALLEL IS CRT: KEYBOARD
;
PORTA	EQU	60H		;SIO = TTY:  PIO: = LPT:
PORTB	EQU	30H		;SIO = SP2:  PIO: = CRT:
;
;VIDEO DRIVER EQUATES
;
VIO	EQU	0F000H
LINLEN	EQU	80
LPP	EQU	24
PGLEN	EQU	LPP*LINLEN
;
;VIDEO DRIVER AND MONITOR RAM AREAS IN UNUSED 128
;BYTES OF THE VIDEO REFRESH RAM MEMORY
;
	ORG	VIO
VIDBF	DS	PGLEN		;REFRESH RAM AREA
ROW	DS	1		;CURSOR ROW (0-23)
COLMN	DS	1		;CURSOR COLUMN (0-79)
POLFLG	DS	1		;INVERSE VIDEO FLAG
INVST	DS	1		;INVERTED COPY OF STATUS MODE BYTES
INSFLG	DS	1		;INSERT MODE TOGGLE
ESCFLG	DS	1		;ESCAPE FLAG
ESC2CH	DS	1		;STORAGE FOR 2ND CHAR OF ESC SEQ
ACTLTB	DS	2		;ADDR OF ALT CTL CHAR TABLE
AESCTB	DS	2		;ADDR OF ALT ESC CHAR TABLE
	DS	2		;WAS ALT CMD TABLE ADDR FOR MONITOR
ASCPTR	DS	2		;ADDR FOR ASCII POINTER IN DUMP
STRT	EQU	ASCPTR		;STARTING ADDR FOR MEMORY TESTS
CURADR	DS	2		;ABSOLUTE CURSOR ADDRESS
PROTFL	DS	1		;0FFH HERE ==> BIT 80H=PROTECT
CURCHR	DS	1		;STORE CHAR UNDER CURSOR
NEWCHR	DS	1		;STORE CHAR TO BE DISPLAYED
EXSTAT	DS	1		;COPY OF STATUS W/EXTRA BITS
TABTBL	DS	10		;TAB STORAGE (BIT MAPPED)
FMTDF	DS	5		;FOR COMPATABILITY W/VIO
PFFLG	DS	1		;PROT FLD ENCOUNTERED FLAG FOR TAB FNCT
FMTDF2	DS	3		;FOR COMPATABILITY
IORAM	DS	3		;ADDR OF RAM FOR I/O CMDS
ASCFLD	DS	16		;ASCII AREA
	DS	68
STATS	DS	1		;HARDWARE COMMAND BYTE
;
;PROGRAM ENTRY POINTS
;
;	ORG	8000H		;FOR DEBUG
;
MONITOR:JMP	MONTR		;MONITOR ENTRY POINT
	JMP	WMSTRT		;MONITOR WARM START
DSPLY:	JMP	CRTOUT		;CHARACTER DISPLAY ENTRY
	JMP	INITL
	LXI	SP,STATS-1
	CALL	INITL
LF80F:	CALL	GTCHR
	JMP	LF80F
;
;INITIALIZE NON-DISK I/O DEVICES
;
INITL:	DI			;NO INT. FOR NOW
	MVI	A,1		;RESET AND INT. ACK. OFF
	OUT	PORTA+2		;ON DEVICE A
	OUT	PORTB+2		;AND B
	MVI	A,0		;NO INT. ENABLED
	OUT	PORTA+3		;ON DEVICE A
	MVI	A,04H		;ENABLE SENS (KBD)
	OUT	PORTB+3		;ON DEVICE B
	MVI	A,88H		;1200 BAUD 1 STOP BIT
	OUT	PORTA		;FOR DIABLO
	MVI	A,0C0H		;9600 BAUD 1 STOP BIT
	OUT	PORTB		;FOR 2ND CONSOLE/RDR-PCH
	PUSH	H		;SAVE H,L
;
;NOW DO VIDEO INITIALIZATION
;
	XRA	A
	LXI	H,ROW		;POINT TO START OF VAR. STORAGE
	MVI	B,(ASCFLD+16-ROW)	;LENGTH OF STORAGE AREA
INIT1:	MOV	M,A		;CLEAR STORAGE
	INX	H		;POINT TO NEXT
	DCR	B		;DECR. COUNT
	JNZ	INIT1		;MORE IF NOT DONE
	LXI	H,VIDBF		;GET 1ST CHAR ADDRESS
	SHLD	CURADR		;SAVE ABSOLUTE CURSOR ADDR
	CALL	CLEAR		;CLEAR SCREEN
;
;FOLLOWING IS FOR VIO SOFTWARE COMPATABILITY
;
	LXI	H,80
	SHLD	FMTDF
	MVI	A,23
	STA	FMTDF+2
	LXI	H,1920
	SHLD	FMTDF+3
	LXI	H,VIO+7FFH
	SHLD	FMTDF2+1
;
	LXI	D,DISP3		;RETURN ADDR FOR ALL COMMANDS
	PUSH	D		;PUT IT ON STACK TOP
	MVI	A,8		;ASCII TEXT MODE, POS VID, 80X24
INIT2:	STA	EXSTAT		;SOFTWARE'S STATUS BYTE
	STA	STATS		;HARDWARE'S STATUS BYTE
	ANI	0CH		;MASK MODE BITS
	XRI	0CH		;INVERT THEM
	STA	INVST		;AND SAVE
;
;COME HERE AFTER PROCESSING AN ESCAPE SEQ.
;
ESCEX:	XRA	A		;GET A ZERO
	STA	ESCFLG		;CLEAR ESCAPE OUTSTANDING FLAG
	RET			;DONE
;
;CHARACTER OUTPUT ENTRY POINT
;
CRTOUT:	PUSH	H		;SAVE H,L
	LHLD	CURADR		;GET ABS. CURSOR ADDR
	LDA	CURCHR		;GET CHAR UNDER CURSOR
	MOV	M,A		;PUT IT BACK
	LXI	H,NEWCHR	;POINT AT CURRENT CHAR
	MOV	M,C		;SAVE NEW CHAR
	MOV	A,C		;GET IT BACK
	CPI	1BH		;ESCAPE ?
	JZ	ESCCH		;YES - PROCESS ESCAPE
	LDA	ESCFLG		;NO - PREV ESC OUTSTANDING ?
	ORA	A		;SET FLAGS
	JNZ	ESCCH		;YES - PROCESS ESCAPE
	MOV	A,C		;GET CHAR AGAIN
	CPI	7FH		;DELETE ?
	JZ	CTLCH		;YES - PROCESS
	LDA	INVST		;GET INVERTED STATUS
	ORA	A		;ZERO - E.G. USING ALL 256 CHARS
	JZ	DISP		;YES - HANDLE SEPARATELY
	MOV	A,C		;GET CHAR AGAIN
	CPI	0FFH		;PAD CHARACTER ?
	JZ	DISP3		;SKIP PROCESSING
	ANI	7FH		;MASK PARITY
	MOV	M,A		;SAVE IT THAT WAY
	CPI	20H		;SEE IF CNTL CHAR
	JM	CTLCH		;WE DON'T DISPLAY CNTL CHARS
;
;PUT CHAR ON SCREEN
;
DISP:	LHLD	CURADR		;GET CUR ADDR
	LDA	INSFLG		;GET INSERT MODE SWITCH
	ORA	A		;SET FLAGS
	JZ	DISP1		;IF NOT USING INSERT
	PUSH	H		;SAVE CURADR ON STACK
	CALL	SP2END		;COMPUTE SPACES TO END OF FIELD
	DCX	B		;DCR BY 1
	DAD	B		;ADD TO H,L = END ADDR
	MOV	D,H		;DUPE H,L IN D,E
	MOV	E,L
	DCX	D		;DCR D,E BY 1
	XCHG
	MOV	A,C
	ORA	B
	JZ	DISP5
	DB	0EDH,0B8H	;Z80: LDDR
DISP5:	POP	H		;RESTOR CURSOR ADDR
DISP1:	LDA	POLFLG		;GET CURSOR ON-OFF FLAG
	ANI	80H		;MASK REV. VIDEO BIT
	MOV	B,A		;SAVE IN B
	LDA	NEWCHR		;GET CHAR
	ORA	B		;TURN CURSOR ON (MAYBE)
	MOV	M,A		;STORE CHAR
	CALL	RIGHT		;MOVE CURSOR RIGHT 1 POSITION
DISP2:	CALL	CALCAD		;CALC ABS CUR ADDR FROM ROW, COL
DISP3:	LHLD	CURADR		;GET CURSOR ADDRESS
	MOV	A,M		;GET CHAR
	STA	CURCHR		;SAVE IT
	ORI	80H		;TURN CURSOR ON
	MOV	M,A		;PUT IT BACK
	LDA	INVST		;GET STATUS BYTE
	ORA	A		;SET FLAGS
	JNZ	DISP4
	MVI	M,7FH		;IN GRAPHICS MODE, DEL IS CURSOR
DISP4:	POP	H		;RESTORE H,L (ONLY REG. SAVED)
	RET			;AND GO HOME
;
;ROUTINE TO MOVE CURSOR RIGHT
;
RIGHT:	CALL	BMPCUR		;ADV. COL. NO.
	CZ	LINFD1		;UPDATE ROW ON OVERFLOW
	LDA	PROTFL		;GET PROTECT ON/OFF FLAG
	XCHG			;NEW CUR ADDR TO H,L
	ANA	M		;SEE IF THIS CHAR PROT.
	JM	RIGHT		;CUR CAN'T STOP ON PROT. FIELD
	RET			;DONE, CUR ON UPPROT. FIELD
;
;ROUTINE TO DO LINE-FEED (CURSOR DOWN)
;
LINFD:	LXI	H,ROW		;POINT TO CURRENT ROW
LINFD1:	INR	M		;INCREMENT IT
	MVI	A,LPP-1		;MAX ALLOWED ROW
	CMP	M		;OVERFLOW ?
	RP			;NO-DONE
	DCR	M		;OVERFLOW - DCR ROW
	LDA	EXSTAT		;GET EXTENDED STATUS BYTE
	ANI	8CH		;MASK MODE & SCROLL/PG BITS
	JM	LINFD2		;JUMP IF IN PAGE MODE
	CPI	0CH		;SEE IF IN GRAPHICS MODE
	JNZ	SCRLUP		;SCROLL IF IN SCROLL MODE
LINFD2:	XRA	A		;GET A ZERO
	MOV	M,A		;MAKE IT THE NEW ROW
	INX	H		;POINT TO COLUMN
	MOV	M,A		;GET IT
	RET			;RETURN W/COLUMN IN ACC
;
;COME HERE TO SCROLL SCREEN UP 1 LINE
;
SCRLUP:	LXI	B,PGLEN-LINLEN	;GET NO. CHRS TO MOVE
	LXI	H,VIDBF+LINLEN	;H,L = 1ST CHAR TO MOVE
	LXI	D,VIDBF		;GET ADDR OF VIDEO BUFFER (DEST)
	DB	0EDH,0B0H	;Z80: LDIR
	JMP	DELIN1		;CLEAR LAST LINE AND RETURN
;
;PROCESS CONTROL CHARS -- H,L=POINTER TO CURRENT CHARACTER
;
CTLCH:	XCHG			;CHAR POINTER TO D,E
	LHLD	ACTLTB		;GET ADDR OF ALT. TABLE
	MOV	A,H		;GET HIGH BYTE
	ORA	L		;SET FLAGS
	JZ	CTLCH1		;SKIP NEXT IF USING STD. TABLE
	CALL	TBSCH		;ELSE DO SEARCH OF ALT. TABLE
	JNZ	CTLCH3		;AND DISPATCH IF MATCH FOUND
CTLCH1:	LDA	NEWCHR		;GET CURRENT CHARACTER
CTLCH2:	LXI	H,CTLTAB	;POINT TO STD. TABLE
	CALL	TBSCH		;SEARCH IT
	JNZ	CTLCH3		;IF MATCH FOUND
	MOV	A,B		;GET CHAR BACK
	CPI	'G'-40H		;BELL ?
	JZ	DISP
	JMP	DISP3		;OTHER, DON'T DISPLAY
CTLCH3:	LXI	D,DISP2		;GET RET POINT FOR CNTL CHR ROUTINES
	PUSH	D		;AND PUT IT ON STACK TOP
	LXI	D,COLMN		;LET D,E POINT TO CURSOR COLUMN
	PCHL			;AND DISPATCH TO ROUTINE
;
;COME HERE TO PROCESS ESCAPE SEQUENCE, BOTH ON
;ESC. ITSELF & SUBSEQUENT CHARS.  H,L POINTS A NEWCHR.
;
ESCCH:	LXI	D,DISP2		;GET RETURN ADDR FOR WHEN DONE
	PUSH	D		;PUSH IT ON STACK
	XCHG			;SAVE CURRENT CHAR ADDR IN D,E
	LXI	H,ESCFLG	;POINT TO ESCAPE FLAG
	MOV	A,M		;GET IT
	INR	M		;INCREMENT IT IN MEMORY ONLY
	ORA	A		;SET FLAGS
	RZ			;NO ESC WAS OUTSTANDING, NOW IS
	DCR	A		;ESC WAS OUTSTANDING, DCR OLD FLAG
	INX	H		;POINT TO 2ND CHAR OF ESC. SEQ
	LDAX	D		;GET CHAR UNDER CURSOR
	JNZ	ESCCH2		;SKIP NXT IF ON 3RD OR 4TH CHAR (DCA)
	MOV	M,A		;ALL OTHERS, SAVE 2ND ESC. SEQ CHAR
ESCCH2:	LHLD	AESCTB		;POINT TO ALT. ESC. TABLE
	MOV	A,H		;GET HIGH ADDRESS BYTE
	ORA	L		;SET FLAGS
	LDAX	D		;GET ESC. SEQ 2ND CHAR
	JZ	ESCCH3		;USE STD. TABLE
	CALL	TBSCH		;ELSE SEARCH ALT. TABLE
	JNZ	ESCCH4		;EXEC. ROUTINE IF MATCH FOUND
ESCCH3:	LDA	ESC2CH		;ELSE RESTORE 2ND CHAR OF ESC. SEQ
	ANI	0DFH		;MASK OUT 20H BIT
	LXI	H,ESCTB		;POINT TO ESCAPE TABLE
	CALL	TBSCH		;SEARCH IT
	JZ	ESCEX		;NO MATCH FOUND, CLR ESC. FLAG
ESCCH4:	LDA	EXSTAT		;GET EXTENDED STATUS BYTE
	LXI	D,INIT2		;RET ADDR TO UPDATE STATUS BYTES
	PCHL			;EXECUTE ESCAPE SUBROUTINE
;
;ROUTINES TO MOVE CURSOR UP/LEFT ONE POSITION
;
UP:	DCX	D		;POINT AT ROW INSTEAD OF COL
LEFT:	LDAX	D		;GET COLUMN (ROW)
	ORA	A		;SET FLAGS
	RZ			;CAN'T BE LESS THAN ZERO, DONE
	DCR	A		;ELSE DECREMENT
	STAX	D		;AND PUT BACK
	RET			;THEN RETURN
;
;ROUTINE TO DO A CARRIAGE RETURN
;
CRET:	XRA	A		;GET A ZERO
	STA	INSFLG		;CLEAR INSERT MODE
	STAX	D		;STORE ZERO AS COLUMN
	RET
;
;ROUTINES TO TOGGLE THE PROTECT AND INSERT MODES
;
PROT:	LXI	H,PROTFL	;POINT AT PROTECT ON/OFF FLAG
	JMP	TOGLE		;COMPLEMENT IT & RETURN
INTOG:	LXI	H,INSFLG	;POINT AT INSERT MODE FLAG
TOGLE:	MOV	A,M		;GET IT
	CMA			;COMPLEMENT IT
	MOV	M,A		;PUT IT BACK
	RET			;DONE
;
;ROUTINE TO CLEAR SCREEN
;
CLEAR:	LXI	D,PGLEN		;NO CHARS/SCREEN
	LXI	H,VIDBF		;POINT TO START OF BUFFER
CLEAR1:	LDA	PROTFL		;GET PROTECT FLAG
	ANI	80H		;MASK HIGH ORDER BIT
	ANA	M		;AND WITH CHAR
	JM	CLEAR2		;DON'T CLEAR PROTECTED
	MVI	M,' '		;ELSE REPLACE WITH SPACE
CLEAR2:	INX	H		;INCREMENT POINTER
	DCX	D		;DECR. COUNT OF CHARS TO CLEAR
	MOV	A,D		;SEE IF COUNT IS 0
	ORA	E		;
	JNZ	CLEAR1		;NO - DO MORE,ELSE HOME CURSOR
;
;ROUTINE TO HOME CURSOR
;
HOME:	LXI	H,0		;MAKE H,L ZERO
	SHLD	ROW		;SAVE 0 AS BOTH ROW & COL
	RET			;DONE
;
;ROUTINE TO ERASE TO END OF LINE OR FIELD
;
EREOF:	CALL	SP2END		;GET COUNT OF CHRS TO CLEAR IN C
EREOF1:	LDA	PROTFL		;GET PROTECT FLAG
	ORA	A		;ZERO ?
	JZ	EREOF2		;YES - CLEAR TO END OF LINE
	MOV	A,M		;NO - GET CHAR
	ORA	A		;SET FLAGS
	JM	EREOF3		;DON'T CLEAR IF PROTECTED
EREOF2:	MVI	M,' '		;CLEAR OUT A CHAR
EREOF3:	INX	H		;INCREMENT POINTER
	DCR	C		;DECR. COUNT
	JNZ	EREOF1		;CONTINUE IF NOT DONE
	RET			;ELSE RETURN
;
;CONTROL-V (TOGGLE INVERSE VIDEO) ROUTINE
;
CNTLV:	LXI	H,POLFLG	;POINT TO INVERSE VIDEO FLAG
	JMP	TOGLE		;TOGGLE IT & RET
;
;ROUTINE TO DELETE A CHAR & CLOSE UP LINE
;
CHDEL:	CALL	SP2END		;SPACES TO END OF FIELD IN C
	LHLD	CURADR		;GET CURRENT CURSOR ADDR
	MOV	D,H		;DUPLICATE IN D,E
	MOV	E,L		;
	INX	H		;POINT TO NEXT CHAR
	DB	0EDH,0B0H	;Z80: LDIR
	MVI	A,' '		;GET SPACE
	DCX	D		;POINT TO LAST POS. IN FIELD
	STAX	D		;PUT SPACE THERE
	RET			;DONE
;
;ROUTINE TO COMPUTE NO. OF SPACES TO END OF LINE OR
;START OF NEXT PROTECTED FIELD & LEAVE ANS. IN REG. C
;
SP2END:	LDA	PROTFL		;GET PROTECT STATUS
	ANI	80H		;MASK 80H BIT
	MOV	D,A		;SAVE IN D
	LHLD	CURADR		;GET CURSOR ADDRESS
	PUSH	H		;SAVE ON STACK
	LDA	COLMN		;GET CURRENT COLUMN
	MOV	E,A		;SAVE IN E.
	LXI	B,0		;MAKE B,C ZERO
SP2END1:MVI	A,LINLEN	;GET MAX ALLOWED COLUMN
	SUB	E		;SUBTRACT CUR COLUMN
	INR	E		;INR CUR COLUMN
	INX	H		;INR CURSOR ADDR
	INR	C		;INR COUNT OF SPACES TO CLR
	DCR	A		;DCR COUNT SPACES LEFT THIS LINE
	JZ	SP2END2		;DONE IF WENT TO END OF LINE
	MOV	A,M		;ELSE GET CHAR AT CUR POSITION
	ANA	D		;SEE IF PROTECTED
	JP	SP2END1		;CONTINUE IF NOT PROTECTED
SP2END2:POP	H		;RESTORE CUR CURSOR ADDR TO H,L
	RET			;DONE
;
;COMMAND TABLE SEARCH ROUTINE FOR SINGLE CHAR COMMANDS
;
TBSCH:	MOV	B,A		;SAVE COMMAND TO LOOK UP IN B
TBSCH1:	MOV	A,M		;GET TABLE ENTRY
	LXI	D,ROW		;LET D POINT TO ROW,COL
	ORA	A		;SET FLAGS
	RZ			;END OF TABLE, RETN W/ZERO SET
	CMP	B		;ELSE CHECK FOR MATCH
	JNZ	TBSCH2		;AND INR. PAST ADDR. IF NO MATCH
	INX	H		;MATCH - POINT AT ADDR
	MOV	E,M		;GET LOW ADDR IN E
	INX	H		;POINT TO HIGH
	MOV	D,M		;GET IT IN D
	XCHG			;XCHG - ADDR TO H,L
	ORA	A		;SET FLAGS NON-ZERO
	RET			;AND RETURN
TBSCH2:	INX	H		;POINT TO LOW ADDR
	INX	H		;POINT TO HIGH ADDR
	INX	H		;POINT TO NEXT TABLE ENTRY
	JMP	TBSCH1		;AND TEST IT
;
;ROUTINE TO DELETE A LINE
;
DELIN:	CALL	INLIN1		;PUT CUR AT COL. 0 OF CUR LINE
	DAD	D		;H,L POINTS TO 1ST CHAR NEXT LINE
	MOV	A,B
	ORA	C
	JZ	DELIN1
	DB	0EDH,0B0H	;Z80: LDIR
DELIN1:	LXI	B,LINLEN	;LINE SIZE TO B,C
	XCHG
	JMP	FLWSP		;FILL WITH SPACES
;
;ROUTINE TO INSERT A LINE
;
INLIN:	CALL	INLIN1		;B,C = NO CHRS AFTER CURSOR - 1 LINE
	PUSH	H		;MAX COL SIZE
	DAD	D		;ADD LINE LEN TO CURSOR
	DAD	B		;ADD IN NO CHARS AFTR CUR - 1 LINE = END SCRN
	XCHG			;TO D,E (END OF SCREEN)
	DAD	B		;H,L=CUR, B,C=NO CHR AFTER - 1 LINE
	DCX	H		;DCR BY 1 FOR EA CHAR
	DCX	D
	MOV	A,B
	ORA	C
	JZ	INLIN5
	DB	0EDH,0B8H	;Z80: LDDR
INLIN5:	POP	B		;MAX COL SIZE
	INX	H		;POINT TO 1ST CHAR OF "NEW" LINE
FLWSP:	MVI	M,20H		;BLANK OUT CHAR
	INX	H		;INR POINTER
	DCR	C		;DCR COUNT
	JNZ	FLWSP		;DO TILL DONE
	RET
;
INLIN1:	XRA	A		;GET A ZERO
	STAX	D		;NEW COLUMN
	CALL	CALCAD		;UPDATE ABS CURR ADDR
	PUSH	H		;SAVE ON STACK
	XCHG			;AND IN D,E
	LXI	H,VIDBF+PGLEN	;GET END OF SCRN ADDR
	MOV	A,D		;NEGATE CURSOR
	CMA
	MOV	D,A
	MOV	A,E
	CMA
	MOV	E,A
	INX	D
	DAD	D		;HL = NO CHARS AFTER CURSOR
	PUSH	H		;SAVE ON STACK
	POP	B		;RESTORE BC (NO CHARS AFTER CUR)
	POP	H		;CURSOR IN HL
	XCHG			;DE = CUR
	LXI	H,LINLEN
	MOV	A,L		;TO A
INLIN2:	DCX	B		;DCR B, NO CHRS
	DCR	A		;CONTINUE TILL B,C REDUCED BY 1 LINE
	JNZ	INLIN2
	RET
;
;EXTENDED TEXT MODE (UPPER CHAR SET)
;
EXTXT:	ANI	0F3H		;CLEAR MODE BITS
	ORI	04H		;SET TO EXTENDED TEXT
	XCHG
	PCHL
;
;GRAPHICS MODE (ALL 256 BYTES AVAIL)
;
GRAPH:	ORI	0CH		;SET GRAPHICS BITS
	XCHG
	PCHL
;
;ASCII TEXT MODE (LOWER CHAR SET)
;
TXT:	ANI	0F3H		;CLEAR MODE BITS
	ORI	08H		;SET TO TEXT MODE
	XCHG
	PCHL
;
;TOGGLE SCROLL/PAGE MODE
;
SCTOG:	XRI	80H		;INVERT SCROLL BIT
	XCHG
	PCHL
;
;TOGGLE INVERSE VIDEO BIT
;
INVRT:	XRI	10H		;INVERT POLARITY BIT
	XCHG
	PCHL
;
;CLEAR ALL TABS
;
CATAB:	LXI	H,TABTBL	;POINT TO 80 BIT TAB TABLE
	MVI	B,0AH		;10 BYTES OF 8 TAB STOPS EA.
	XRA	A		;GET ZERO
C8TABS:	MOV	M,A		;CLEAR 8 TABS
	INX	H		;POINT TO NEXT BLOCK OF 8 TABS
	DCR	B		;DCR COUNT
	JNZ	C8TABS		;CONTINUE TILL DONE
	JMP	ESCEX		;THEN CLR ESC FLAG & GO HOME
;
;SET OR CLEAR TAB STOPS
;
SCTAB:	CALL	SCTAB2		;GET TAB BYTE, BIT NO. & BYTE COUNT
	XRI	80H		;CHANGE 80H BIT
SCTAB1:	RRC
	DCR	B		;DCR COUNT OF BIT OFFSET
	JNZ	SCTAB1		;KEEP ROTATING IF NOT DONE
	STAX	D		;PUT BYTE BACK
	JMP	ESCEX		;CLEAR ESC. FLAG & GO HOME
;
SCTAB2:	LDA	COLMN		;GET CURRENT COLUMN TO H
	MOV	H,A		;TO H
	INR	H		;INITIALIZATION FOR FOLLOWING ROUTINE
	LXI	D,TABTBL	;POINT TO TAB TABLE
SCTAB3:	MVI	C,8		;NO TAB STOPS PER BYTE IN TABLE
SCTAB4:	DCR	H		;POINT TO PREV. COLUMN
	JZ	SCTAB5		;JUMP IF COL. EQ. ZERO
	DCR	C		;ELSE DCR. BIT COUNT
	JNZ	SCTAB4		;AND REPEAT IF NOT ZERO
	INX	D		;BIT COUNT = 0, GET NEXT BYTE
	JMP	SCTAB3		;GO UNTIL COL. EQ. ZERO
SCTAB5:	LDAX	D		;GET TAB TABLE BYTE
	MOV	B,C		;SAVE BIT COUNT
SCTAB6:	RLC
	DCR	C		;DCR BIT COUNT
	JNZ	SCTAB6		;DO UNTIL BIT COUNT EQ. ZERO
	RET
;
;TAB TO NEXT TAB STOP OR UNPROTECTED FIELD
;
TAB:	XRA	A		;GET A ZERO
	STA	PFFLG		;CLEAR PROT. FIELD ENCOUNTERED FLAG
TAB1:	CALL	BMPCUR		;ADVANCE 1 POSITION
	JNZ	TAB2		;JUMP IF NOT ON LAST COL. OF LINE
	INR	M		;ELSE INR. ROW
	CMP	M		;ROW = MAX. ALLOWED ROW ?
	JM	LINFD2		;YES, OFF SCREEN, DO HOME
TAB2:	LDA	PROTFL		;GET PROTECT FIELD ON-OFF FLAG
	XCHG
	ANA	M		;SET SIGN FLAG
	MOV	A,M		;GET CHAR UNDER CURSOR
	LXI	D,PFFLG		;POINT TO PROTECTED FIELD ENCOUNTERED FLAG
	JP	TAB3		;IF CURRENT POSITION NOT PROTECTED
	STAX	D		;ELSE SET PROT. FIELD ENCOUNTERED FLAG
	JMP	TAB1		;AND TRY NEXT COL.
TAB3:	LDAX	D		;SEE IF PROT FIELD PREV. ENCOUNTERED
	ORA	A
	RM			;YES, NOW ON 1ST UNPROT AFTER PROTECTED FIELD
	CALL	SCTAB2		;SEE IF TAB STOP AT CUR. POSITION
	ORA	A		;SET FLAGS
	RM			;DONE IF TAB STOP SET
	JMP	TAB1		;TAB STOP NOT SET & STILL IN UNPROT FIELD
;
;UPDATE ABSOLUTE CURSOR ADDR
;
CALCAD:	LXI	D,LINLEN	;MAX ALLOWED COL.
	LHLD	ROW		;CUR ROW, COLUMN
	MOV	C,H		;DUPE IN B,C
	MOV	B,L
	LXI	H,VIDBF		;POINT TO DISPLAY BUFFER
	INR	B		;INITIALIZE LOOP
CALCAD1:DCR	B		;FOR EA. ROW AFTER FIRST
	JZ	CALCAD2
	DAD	D		;ADD NO. OF COLS/ROW TO SCREEN
	JMP	CALCAD1		;UNTIL DONE
CALCAD2:DAD	B		;THEN ADD CURRENT COL. NO
	SHLD	CURADR		;AND SAVE RESULT = NEW CURSOR ADDR
	RET
;
;BUMP CURSOR POSITION BY 1
;
BMPCUR:	LHLD	CURADR		;GET CUR ADDR
	INX	H		;INCREMENT IT
	SHLD	CURADR		;PUT IT BACK
	XCHG
	LXI	H,COLMN		;POINT TO CUR. COL. NO
	INR	M		;INCREMENT IT
	MVI	A,LINLEN	;GET MAX ALLOWED COL.
	SUB	M		;COMPARE TO NEW COLUMN
	RNZ
	MOV	M,A		;GONE OFF LINE, MAKE COL=0
	DCX	H		;POINT TO CURRENT ROW
	MVI	A,LPP-1		;MAX ALLOWED ROW
	RET
;
;DIRECT CURSOR ADDRESSING
;
DCA:	LXI	H,ESCFLG	;ESCAPE SEQ. FLAG
	LXI	D,ROW		;CURSOR ROW
	LDA	NEWCHR		;CUR. CHAR
	SUI	20H
	MOV	B,A		;SAVE CURRENT CHAR
	MOV	A,M		;GET ESC. FLAG
	SUI	3		;3RD CHAR OF ESC. SEQ ?
	RM			;NO, 1ST OR 2ND
	JNZ	DCA2		;IF 4TH CHAR OF ESC SEQ
	MVI	A,LPP-1		;MAX ALLOWED ROW
DCA1:	STAX	D		;SAVE AS CURRENT ROW
	CMP	B		;SEE IF SAME AS STRED CHAR
	RM			;DONE IF STORED CHAR > MAX. ROW
	MOV	A,B		;ELSE GET STORED CHAR
	STAX	D		;MAKE IT CUR ROW
	RET			;DONE
;
DCA2:	MVI	M,0		;CLEAR ESC FLAG
	MVI	A,LINLEN	;MAX ALLOWED COL
	INX	D		;POINT TO CUR COL
	DCR	A		;DCR MAX ALLOWED COL
	JMP	DCA1		;UPDATE COL
;
;CONTROL CODE TABLE FOR VIDEO DISPLAY
;
CTLTAB:	DB	0DH
	DW	CRET
	DB	0AH
	DW	LINFD
	DB	'K'-40H
	DW	UP
	DB	'L'-40H
	DW	RIGHT
	DB	'H'-40H
	DW	LEFT
	DB	'^'-40H
	DW	HOME
	DB	'Z'-40H
	DW	CLEAR
	DB	'U'-40H
	DW	EREOF
	DB	'V'-40H
	DW	CNTLV		;ENTER INVERSE VIDEO MODE
	DB	'I'-40H
	DW	TAB
	DB	7FH
	DW	CHDEL
	DB	'T'-40H
	DW	INTOG
	DB	'D'-40H
	DW	DELIN
	DB	'E'-40H
	DW	INLIN
	DB	'P'-40H
	DW	PROT
	DB	0
;
;ESCAPE SEQUENCE TABLE FOR VIDEO DISPLAY
;
ESCTB:	DB	'='-20H
	DW	DCA		;DIRECT CURSOR ADDR
	DB	'I'
	DW	SCTAB
	DB	'I'-40H
	DW	CATAB
	DB	'T'
	DW	TXT
	DB	'E'
	DW	EXTXT
	DB	'G'
	DW	GRAPH
	DB	'S'
	DW	SCTOG
	DB	'V'
	DW	INVRT
	DB	0
;
;AVAILABLE SPACE
;
	DS	128H
;
;MONITOR PROGRAM STARTS HERE
;
MONTR:	LXI	SP,STATS
	CALL	INITL		;INITIALIZE VIO & TU-ART
	LXI	H,SGNON		;SIGNON MESSAGE
	CALL	PMSG		;PRINT IT
WMSTRT:	DI			;TURN OFF INT.
	LXI	SP,STATS
	CALL	CRLF		;DO CR/LF
	MVI	C,'?'		;PROMPT
	CALL	CRTOUT
	CALL	GTCHR		;WAIT FOR INPUT
	LXI	D,WMSTRT	;PUSH RETURN ADDR ON STACK
	PUSH	D
	LXI	H,LFFC0		;AND NOW TRY STD. TABLE
	CALL	TBSCH
	RZ			;NO MATCH
	MVI	B,1
DOIT:	PCHL			;GO TO ROUTINE
;
;SIGN-ON MESSAGE & COPYRIGHT NOTICE
;
SGNON:	DB	'VM-80 By Barry A. Watzman',0
;
;BOOT COMMAND - DISK BOOTSTRAP FOR TARBELL DISK
;
BOOT:	XRA	A		;SEL DISK 0
	OUT	WAIT
	MVI	A,43H		;LOW SPEED STEP IN COMMAND
	OUT	DCOM		;IN CASE ON TRACK -1
	IN	WAIT		;WAIT FOR DONE
	MVI	A,43H		;AGAIN, FOR SAFETY
	OUT	DCOM
	IN	WAIT
	MVI	A,3		;LOW SPEED 1771 HOME COMMAND
	OUT	DCOM
	IN	WAIT
	XRA	A		;GET A ZERO
	MOV	L,A
	MOV	H,A
	PUSH	H		;0 IS BOOT ENTRY POINT
	INR	A		;A=1
	OUT	SECTP
	MVI	A,8CH		;READ SECTOR COMMAND
	OUT	DCOM
RLOOP:	IN	WAIT
	ORA	A		;SET FLAGS
	JP	RDONE		;INTRQ
	IN	DDATA
	MOV	M,A
	INX	H
	JMP	RLOOP
RDONE:	IN	DSKST		;READ DISK STATUS
	ORA	A		;
	RZ			;00 IS ON STACK
	MOV	C,A		;SAVE STATUS
	CALL	CRLF		;
	MOV	A,C		;GET STATUS BACK
	CALL	HXSPOT		;PRINT IN HEX
	POP	H		;TAKE BOOT ADDR OFF STACK
	RET
;
;GO COMMAND - JUMPS TO MEMORY ADDRESS WITH MONITOR RETURN ON STACK
;
EXEC:	CALL	GETADR		;GET ARGUMENT ADDR IN H,L
	CPI	0AH		;SEE IF CAR. RET WAS LAST
	RNZ
	PCHL			;GO TO IT
;
;EXAMINE AND SUBSTITUTE MEMORY
;
ENTR:	CALL	GETADR		;GET ADDR IN H,L
	CPI	0AH		;SEE IF CAR. RET. LAST
	RNZ
ENTR1:	CALL	CRLF		;START ON NEW LINE
ENTR2:	CALL	OUTADR		;OUTPUT ADDR IN H,L TO SCREEN
	MOV	A,M		;GET CURRENT VALUE
	MOV	E,A		;SAVE IN E
	CALL	HXSPOT		;DUMP IT FOLLOWED BY A SPACE
	XCHG
	CALL	GETAD1		;GET HEX VALUE IN H,L
	XCHG
	MOV	M,E		;STORE NEW VALUE IN MEMORY
	DCX	H		;POINT TO PREV. LOCATION
	CPI	'-'		;'-' TYPED ?
	JZ	ENTR1		;YES, DO PREV. LOCATION
	CPI	0AH		;CR/LF TYPED ?
	RNZ			;NO, EXIT ROUTINE
	INX	H		;POINT TO ORIG. LOCATION
	INX	H		;POINT TO NEXT LOCATION
	JMP	ENTR2		;DO IT
;
;ESCAPE COMMAND - USED ONLY TO SEND ESC. SEQ. TO VIO FROM KBD
;
ESCAP:	CALL	GTCHR
	CPI	'='
	RNZ
	CALL	GTCHR
	JMP	GTCHR
;
;DUMP COMMAND
;
DUMP:	CALL	LFD20		;START, END, DIFF IN B,C
	XCHG			;STARTING ADDR NOW IN D,E
	LXI	H,15		;MAKE IT MULTIPLE OF 16
	DAD	B		;
	MOV	A,L		;
	ANI	0F0H		;
	MOV	C,A		;THERE
	MOV	B,H
DUMP1:	CALL	NLCHK		;DO CR/LF
	LXI	H,ASCFLD	;POINT TO ASCII AREA
	SHLD	ASCPTR		;SAVE IT
	MVI	L,16		;80 COLS, USE 16 BYTES/LINE
	XCHG
	CALL	OUTADR		;WRITE ADDR FOLLOWED BY SPACE
	XCHG
DUMP2:	LDAX	D		;GET BYTE
	PUSH	H		;SAVE LINE COUNT IN L
	LHLD	ASCPTR
	MOV	M,A		;SAVE ASCII CHAR
	INX	H		;INX ASCII POINTER
	SHLD	ASCPTR		;PUT BACK POINTER
	CALL	HXSPOT		;DUMP BYTE FOLLOWED BY SPACE
	INX	D		;INR POINTER
	DCX	B		;DCR COUNT
	POP	H		;RESTORE COUNTER
	DCR	L		;DCR BYTES THIS LINE
	JNZ	DUMP2		;MORE IF NOT DONE
;
;PRINT THE BUILT UP ASCII
;
ASOUT:	PUSH	D		;SAVE H,L
	LXI	H,ASCFLD	;POINT TO THE ASCII AREA
	MVI	E,16		;COUNT
	CALL	PSPACE		;PRINT A SPACE
ASOU1:	MOV	A,M		;GET ASCII
	CPI	20H		;SEE IF CNTL CHAR
	JC	NOPRT
	CPI	7FH		;SEE IF DELETE
	JNC	NOPRT
	CPI	5FH		;SEE IF UNDERLINE
	JNZ	OKYDOK		;PRINT IT
NOPRT:	MVI	A,'.'		;PRINT A PERIOD
OKYDOK	CALL	CHROT		;PRINT THE CHAR
	INX	H
	DCR	E
	JNZ	ASOU1
	POP	D
DUMP3:	MOV	A,B		;TEST FOR DONE
	ORA	C
	RZ			;DONE
	JMP	DUMP1		;THEN DO NEXT PAGE
;
;FILL MEMORY WITH A CONSTANT
;
FILL:	CALL	LFDD7
	MOV	A,E
	MOV	M,A
	DCX	B
	MOV	D,H
	MOV	E,L
	INX	D
FILL1:	MOV	A,C
	ORA	B
	RZ
	DB	0EDH,0B0H	;Z80: LDIR
	RET
;
;TEST MEMORY BETWEEN TWO ADDRESSES
;
MEMCK:	CALL	LFF3A		;GET START & END IN D,E & H,L
	CPI	0AH		;WAS LAST CHAR CAR. RET.?
	RNZ			;ERROR IF NOT
	CALL	CPHLDE		;SUBTRACT END ADDR. FROM START
	RNC			;IF START > END
	SHLD	STRT		;SAVE AS STARTING ADDR
	XRA	A		;START WITH ZERO
	MOV	C,A		;PATTERN IN C
WRO:	MOV	M,C		;PUT PATTERN IN LOC
	CALL	CPHLDE		;SEE IF DONE
	INX	H		;POINT TO NEXT LOC.
	JC	WRO		;NOT DONE
TRST:	LHLD	STRT		;GET BEGINNING ADDR
RCW:	MOV	A,M		;GET CHAR THERE
	CMP	C		;SAME AS PATTERN ?
	JZ	GOOD		;YES, O.K.
	CALL	OUTADR		;NO, BAD, PRINT ADDR
	MOV	A,C		;WHAT SHOULD HAVE BEEN THERE
	CALL	HXSPOT		;PRINT IT
	MOV	A,M		;GET WHAT WAS THERE
	CALL	HXDGOT		;PRINT IT
	CALL	NLCHK
GOOD:	INR	M		;INCREMENT DATA
	CALL	CPHLDE		;TEST FOR DONE
	INX	H		;INR POINTER
	JC	RCW		;NOT DONE
	INR	C		;DONE WITH PASS, INR PATTERN
	MOV	A,C		;SHOW NEW PATTERN
	STA	VIDBF
	JNZ	TRST		;NEXT PASS
;
;SO FAR, SO GOOD, START THE WORM TEST
;
	MVI	A,0C3H		;JUMP INST
;
	STA	038H		;RST 7
	LXI	H,WORM7
	SHLD	38H+1		;TRAP ADDR CALL
;
	LHLD	STRT		;GET STARTING ADDR (WORM DEST)
	LXI	D,3BH		;LOWEST ALLOWED LOCATION FOR WORM
	CALL	CPHLDE		;COMPARE
	JC	WORM1		;USE 3B AS STARTING ADDR
	XCHG			;IN D,E
WORM1:	LXI	H,WORM		;POINT TO WORM
	LXI	B,CPHLDE-WORM	;WORM LENGTH
	PUSH	D		;STARTING ADDR OF WORM
	DB	0EDH,0B0H	;Z80: LDIR
	LXI	B,WRMDTA	;B,C HAS WORM DATA ADDR
	POP	H		;RESTORE WORM ADDR
	INX	H		;START AT WORM + 1
	PCHL			;TO THE WORM !
;
;ROUTINE TO MOVE THE WORM
;
WORM2:	POP	D		;GET ADDR OF WORM+1
	MOV	H,D		;DUPLICATE
	MOV	L,E		;IN H,L
	DCX	H		;LAST ADDR OF WORM
	LXI	B,CPHLDE-WORM	;WORM LENGTH
	DB	0EDH,0B8H	;Z80: LDDR
	LXI	B,WRMDTA	;RESTORE BC
	XCHG			;RESTORE WORM ADDR TO H,L
	INX	H
	INX	H		;H,L = START OF WORM
	PCHL			;RETURN TO WORM
;
;REPORT WORM ADDR
;
WORM5:	INR	A		;CHECK FOR ERROR
	JNZ	ERRA		;ERROR TYPE A IF NOT 0FFH
	ORA	B		;SET FLAGS
	JNZ	ERRB		;ERROR TYPE B IF NOT ZERO
	POP	H		;RECOVER ADDR
	ORA	L		;ONLY WANT TO REPORT XX00 ADDRS.
WORM5A:	PUSH	PSW
	CZ	OUTADR		;ADDRESS OUTPUT
	POP	PSW
	CZ	NLCHK
	INX	H		;ADJUST ADDRESS
	INX	H		;
	INX	H		;
	PCHL			;RETURN
;
;TRAP - SYSTEM CRASHED
;
WORM7:	MVI	A,'T'		;TRAP INDICATOR
	CALL	CHROT
	CALL	PSPACE		;FOLLOWED BY SPACE
	POP	H		;RECOVER ADDR
	DCX	H		;ADJUST ADDR
	CALL	OUTADR		;ADDRESS OUTPUT
	CALL	NLCHK
	INX	H		;ADJUST ADDR
	PCHL			;RETURN
;
;WORM ERROR REPORTING
;
ERRA:	MVI	B,'A'		;TYPE A ERROR
	JMP	ERR		;FINISH UP
ERRB:	MVI	B,'B'		;TYPE B ERROR
ERR:	MOV	C,A		;SAVE ERROR DATA
	MVI	A,'#'		;ERROR SYMBOL
	CALL	CHROT		;PRINT IT
	MOV	A,B		;GET ERROR TYPE
	CALL	CHROT		;PRINT IT
	CALL	PSPACE		;PRINT SPACE
	MOV	A,C		;GET BAD DATA
	CALL	HXSPOT		;PRINT
	POP	H		;SET UP
	XRA	A		;MAKE ACC. ZERO
	JMP	WORM5A		;FINISH UP
;
;THE WORM ITSELF
;
WORM:	RST	7		;TRAP AT TAIL
	LDAX	B		;MOVE DATA TO A; START OF WORM
	PUSH	PSW		;SAVE DATA ON STACK
	MVI	A,0FFH		;GET 2ND BYTE TO TEST
	POP	B		;1ST BYTE NOW IN B
	CALL	WORM5		;REPORT ADDR
RESTART:RST	7		;TRAP
	RST	7		;TRAP
	RST	7		;TRAP
WRMDTA:	NOP			;REPORT ADDR RETURNS HERE
	CALL	WORM2		;MOVE WORM
;
;COMPARE H,L TO D,E
;
CPHLDE:	MOV	A,H
	SUB	D
	RNZ
	MOV	A,L
	SBB	E
	RET
;
;DIRECT I/O TO/FROM PORTS
;
INPUT:	DCR	B
OUTPT:	CALL	LFF3A
	MOV	A,B
	RLC
	RLC
	RLC
	XRI	08H
	ORI	0D3H
	MOV	D,L
	LXI	H,IORAM
	MOV	M,A
	PUSH	H
	INX	H
	MOV	M,D
	INX	H
	MVI	M,0C9H
	LXI	H,LFD87
	XTHL
	MOV	A,B
	ORA	A
	MOV	A,E
	PCHL
LFD87:	RNZ
	JMP	HXSPOT
;
;COMPARE TWO REGIONS OF MEMORY
;
MEMCP:	CALL	LFDD7
LFD96:	LDAX	D
	CMP	M
	INX	H
	INX	D
	JZ	LFDA5
	CALL	LFF8D
	XCHG
	CALL	LFF90
	XCHG
	CALL	CTLS
LFDA5:	DCX	B
	MOV	A,B
	ORA	C
	RZ
	JMP	LFD96
;
;SEARCH MEMORY FOR A 16-BIT VALUE
;
SERCH:	CALL	LFDD7
	PUSH	H
	LXI	H,0FFFFH
	CPI	0AH
	CNZ	GETADR
	XTHL
LFDBE:	MOV	A,M
	XTHL
	ANA	H
	CMP	D
	XTHL
	INX	H
	JNZ	LFDCF
	MOV	A,M
	XTHL
	ANA	L
	CMP	E
	XTHL
	CZ	LFF85
LFDCF:	DCX	B
	MOV	A,B
	ORA	C
	JNZ	LFDBE
	POP	B
	RET
;
LFDD7:	CALL	LFD20
	JMP	LFF4A
;
;MOVE MEMORY FROM START UNTIL END TO DESTINATION
;OPERATES FROM LOW MEMORY TO HIGH MEMORY
;
MOVEM:	CALL	LFDD7
	JMP	FILL1
;
;CHARACTER INPUT ROUTINE
;
GTCHR:	IN	PORTB
	ANI	20H
	JZ	GTCHR		;NOT READY
	IN	PORTB+3		;CLEAR INTERRUPT
	IN	PORTB+4		;GET DATA
	ANI	7FH
	CPI	'C'-40H
	JZ	WMSTRT
	CPI	0DH
	JZ	CRLF
	JMP	CHROT
;
;GET TWO 16-BIT VALUES IN D,E & H,L & SUBTRACT LEAVE ANS. IN B,C
;
LFD20:	CALL	LFF3A		;GET 1ST NO. IN H,L, 2ND NO. IN D,E
	PUSH	PSW		;SAVE LAST CHAR TYPED
	MOV	A,E
	SUB	L
	MOV	C,A
	MOV	A,D
	SBB	H
	MOV	B,A
	INX	B		;WANT INCLUSIVE RANGE
	POP	PSW
	RET
;
;OUTPUT H,L AS ASCII HEX FOLLOWED BY SPACE
;
OUTADR:	MOV	A,H		;MOV H TO A
	CALL	HXDGOT		;OUTPUT AS HEX ASCII
	MOV	A,L		;MOVE L TO A & FALL THROUGH
;
;PRINT ACC. CONTENTS IN ASCII HEX FOLLOWED BY SPACE
;
HXSPOT:	CALL	HXDGOT		;MOVE H TO A
PSPACE:	MVI	A,' '		;PRINT A SPACE
;
;CHARACTER OUTPUT ROUTINE - SAVES ALL REGS
;
CHROT:	PUSH	D
	PUSH	B
	PUSH	PSW
	MOV	C,A
	CALL	CRTOUT
	POP	PSW
	POP	B
	POP	D
	RET
;
;GET TWO ADDRESSES IN D,E AND H,L DELIMITED BY , OR SPACE
;IF ONLY ONE ADDR GIVEN, IT IS IN BOTH H,L & D,E
;ELSE, 1ST ADDR IN H,L, 2ND IN D,E
;
LFF3A:	CALL	GETADR
	MOV	D,H
	MOV	E,L
	CPI	0AH
	RZ
	CPI	','
	JZ	LFF4A
	CPI	' '
	JNZ	WMSTRT
LFF4A:	XCHG
	CALL	GETADR
	XCHG
	RET
;
;CARRIAGE RETURN/LINFEED ROUTINE
;
CRLF:	MVI	A,0DH
	CALL	CHROT
	MVI	A,0AH
	JMP	CHROT
;
;ROUTINE TO GET AN ADDR & CONVERT TO BINARY IN H,L
;
GETADR:	LXI	H,0
GETAD1:	CALL	GTCHR
	PUSH	PSW
	SUI	30H
	JC	GETAD2
	CPI	0AH
	JC	LFF6A
	SUI	7
	CPI	0AH
	JNC	LFF6A
GETAD2:	POP	PSW
	RET
;
;ADD A HEX DIGIT TO H,L & GET ANOTHER CHARACTER
;
LFF6A:	DAD	H
	DAD	H
	DAD	H
	DAD	H
	ADD	L
	MOV	L,A
	POP	PSW
	JMP	GETAD1
;
LFF8D:	CALL	CRLF
LFF90:	DCX	H
	CALL	OUTADR
	MOV	A,M
	CALL	HXSPOT
	INX	H
	RET
;
LFF85:	CALL	LFF8D
	MOV	A,M
;
HXDGOT:	PUSH	PSW
	RRC
	RRC
	RRC
	RRC
	CALL	LFFA7
	POP	PSW
;
LFFA7:	ANI	0FH
	ADI	90H
	DAA
	ACI	40H
	DAA
	JMP	CHROT
;
;SEND CR/LF AND SEE IF CNTL-S TYPED -- WAIT IF SO
;
NLCHK:	CALL	CRLF
;
;CONTROL-S WAIT ROUTINE
;
CTLS:	IN	PORTB		;READ PORT B STATUS
	ANI	20H		;MASK INT. PENDING BIT
	RZ
	CALL	GTCHR
	CPI	'S'-40H
	JNZ	WMSTRT
	JMP	GTCHR		;WAIT FOR ANY CHAR & RET
;
PMSG:	MOV	A,M
	ORA	A
	RZ
	MOV	C,A
	CALL	CRTOUT
	INX	H
	JMP	PMSG
;
;MONITOR COMMAND TABLE
;
LFFC0:	DB	'B'
	DW	BOOT
	DB	'D'
	DW	DUMP
	DB	'E'
	DW	ENTR
	DB	'F'
	DW	FILL
	DB	'G'
	DW	EXEC
	DB	'I'
	DW	INPUT
	DB	'M'
	DW	MOVEM
	DB	'O'
	DW	OUTPT
	DB	'R'
	DW	RESTART
	DB	'S'
	DW	SERCH
	DB	'T'
	DW	MEMCK
	DB	'V'
	DW	MEMCP
	DB	1BH		;1BH = ESCAPE
	DW	ESCAP
	DB	0		;END OF TABLE
	DB	'VIO'		;REQD FOR SOFTWARE COMPATABILITY
	END	MONITOR
