$PAGEWIDTH(120)
$NOINTVECTOR
$LARGE
$OPTIMIZE(2)
$NOPAGING


	/***************
	* 2 JULY 1987 *
	***************/

MONITOR:DO;		/* BEGINNING OF MODULE */

DECLARE
	INT$VECTOR(5)		POINTER;	/* INTERRUPT VECTOR */
DECLARE
	MONITOR$STACKPTR	WORD,
	MONITOR$STACKBASE	WORD;

DECLARE
	COPYRIGHT(*) BYTE DATA ('(C) 1987 STEVE Q7D5');

DECLARE
	BRK1$FLAG		BYTE,		/* TRUE IF BREAK SET */
	BRK1$SAVE		BYTE,		/* INST BREAK SAVE */
	CHAR			BYTE,		/* ONE CHAR LOOK AHEAD */
	CHECK$SUM		BYTE,		/* PAPER TAPE CHECK SUM */
	I				BYTE,		/* INDEX */
	IX				WORD,		/* WORD INDEX */
	END$OFF			WORD,		/* END OF OFFSET ADDRESS */
	WORD$MODE		BYTE,		/* WORD MODE FLAG */
	LAST$COMMAND	BYTE;		/* LAST COMMAND SAVE */

DECLARE
	TRUE			LITERALLY '0FFH',
	FALSE			LITERALLY '000H',
	BREAK$INST		LITERALLY '0CCH',	/* BREAKPOINT INST */
	STEP$TRAP		LITERALLY '0100H',	/* SS TRAP FLAG MASK */
	USER$INIT$SP	LITERALLY '800H',	/* USER STACK INITIAL */
	GO$COMMAND		LITERALLY '2',		/* GO COMMAND CODE */
	SS$COMMAND		LITERALLY '3',		/* SINGLE STEP COMMAND */
	MAX$DELAY		LITERALLY '30000';	/* DELAY FOR PAPER TAPE */

DECLARE
	KB$SIGNON(*)	 BYTE DATA (066H,086H,00H,00H,7DH,7FH,00H,00H),
	SIO$BREAK$MSG(*) BYTE DATA ('Brk ',0),
	SIO$DIV$ERR(*)	 BYTE DATA ('DIV Err ',0),
	ERR$MESS1(*)	 BYTE DATA ('W/R Err ',0),
	ERR$MESS2(*)	 BYTE DATA ('CSm Err ',0),
	SIO$SIGNON(*)	 BYTE DATA (0DH,0AH,'SDK-86 MONITOR, V1.4 JULY ''87',0),
	ASCII(*)		 BYTE DATA ('0123456789ABCDEF'),
	SIO$CMND(*)		 BYTE DATA ('SXGNMDIOFH');

DECLARE HELP$MESS(*) BYTE DATA
(13,10,10,
'S{W}addr,{hex,} Xreg{hex,|,}|<cr> G{addr}{,brkpnt}<cr> N{addr},',13,10,10,
'Mstart,end,to<cr>, D{W}start,end<cr> Fstart,end{,page}<cr> I{W}port,',13,10,
10,
'O{W}port,hex,',13,10,10,
'addr|start|to :== <{seg:}offset>',13,10,
'end|brkpnt :== <offset>',13,10,
'seg|offset|port|page :== <hex>|<hex>+<hex>|<hex>-<hex>',13,10,
'<hex> :== <hex no>|<reg>',13,10,
'<reg> :== AX|BX|CX|DX|SP|BP|DI|SI|CS|DS|ES|SS',13,10,
'<hex no> :== <hex digit>{<hex digit>}',13,10,
'<hex digit> :== 0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F',13,10,
'^S stop listing  ^Q restart listing  ^C terminate listing',13,10,
'W :== word  cr :== return  | :== Or   {} :== contents are optional',0);

/**********************************
*	I/O DELCLARATIONS SECTION *
***********************************/

DECLARE
	SIO$STAT$PORT	LITERALLY '0FFF2H',	/* USART STATUS */
	SIO$DATA$PORT	LITERALLY '0FFF0H',	/* USART DATA */
	SIO$8251$RESET	LITERALLY '00065H',	/* RESET COMMAND */
	SIO$8251$MODE	LITERALLY '000CFH',	/* ASYNC MODE */
	SIO$8251$CMND	LITERALLY '00025H',	/* DTR OFF */
	SIO$8251$DTR$ON	LITERALLY '00027H',	/* DTR ON	*/
	SIO$DSRDY	LITERALLY '00080H',	/* DATA SET RDRY */
	SIO$RXRDY	LITERALLY '00002H',	/* RXRDY */
	SIO$TXRDY	LITERALLY '00001H';	/* TXRDY */

DECLARE
	KB$STAT$PORT	LITERALLY '0FFEAH',	/* STATUS/COMMAND PORT */
	KB$DATA$PORT	LITERALLY '0FFE8H',	/* DATA PORT */
	KB$INIT$MODE	LITERALLY '00H',	/* 8 8-BIT, LEFT ENTRY,
						ENCODE, 2 KEY LOCKOUT */
	KB$INIT$SCAN	LITERALLY '39H';	/* 10 MS SCAN RATE */

DECLARE
	ASCR		LITERALLY '0DH',	/* CARRIAGE RETURN */
	ASLF		LITERALLY '0AH',	/* LINE FEED */
	ASBL		LITERALLY '20H';	/* BLANK SPACE */

/*********************************
*	MEMORY ARGUMENT SECTION	*
**********************************/

DECLARE
	MEMORY$ARG1$PTR	POINTER,	/* ARGUMENT 1 */
	ARG1 STRUCTURE (OFF WORD, SEG WORD)
		AT (@MEMORY$ARG1$PTR),
	MEMORY$ARG1 BASED MEMORY$ARG1$PTR BYTE,
	MEMORY$WORD$ARG1 BASED MEMORY$ARG1$PTR WORD,

	MEMORY$ARG3$PTR POINTER,		/* ARGUMENT 3 */
	ARG3 STRUCTURE (OFF WORD, SEG WORD)
		AT (@MEMORY$ARG3$PTR),
	MEMORY$ARG3 BASED MEMORY$ARG3$PTR BYTE,

	MEMORY$BRK1$PTR POINTER,		/* BREAKPOINT 1 */
	BRK1 STRUCTURE (OFF WORD, SEG WORD)
		AT (@MEMORY$BRK1$PTR),
	MEMORY$BRK1 BASED MEMORY$BRK1$PTR BYTE,

	MEMORY$CSIP$PTR POINTER,		/* CS & IP WORD */
	CSIP STRUCTURE (OFF WORD, SEG WORD)
		AT (@MEMORY$CSIP$PTR),
	MEMORY$CSIP BASED MEMORY$CSIP$PTR BYTE,

	MEMORY$USERSTACK$PTR POINTER,
	USERSTACK STRUCTURE (OFF WORD, SEG WORD)
		AT (@MEMORY$USERSTACK$PTR),
	MEMORY$USERSTACK BASED MEMORY$USERSTACK$PTR WORD;


			/**********************
			*	REGISTER SECTION  *
			**********************/

DECLARE
	REG(*)		BYTE DATA		/* REGISTER NAMES */

		('AXBXCXDXSPBPSIDICSDSSSESIPFL'),

	REG$INDEX	WORD,
	REG$SAV(14)	WORD,			/* USER'S SAVED REGISTERS */
	REG$ORD(*)	BYTE DATA

		(7,6,1,3,2,0,9,11,12,8,13),

	SP		LITERALLY 'REG$SAV( 4)',
	BP		LITERALLY 'REG$SAV( 5)',
	CS		LITERALLY 'REG$SAV( 8)',
	DS		LITERALLY 'REG$SAV( 9)',
	SS		LITERALLY 'REG$SAV(10)',
	ES		LITERALLY 'REG$SAV(11)',
	IP		LITERALLY 'REG$SAV(12)',
	FL		LITERALLY 'REG$SAV(13)';



/*********************************************************************


			COMMON PROCEDURES

ABSTRACT

THIS MODULE CONTAINS THOSE LOWER LEVEL PROCEDURES CALLED BY HIGHER
LEVEL ROUTINES.

MODULE ORGANIZATION

THIS MODULE IS DIVIDED INTO 4 SECTIONS AS FOLLOWS:

1.	BASIC I/O SECTION
	SIO$CHAR$RDY		INPUT CHARACTER READY
	SIO$CHECK$CONTROL$CHAR	CHECK FOR CONTROL CHARACTER
	SIO$OUT$CHAR		OUTPUT CHARACTER
	SIO$GET$CHAR		INPUT A CHARACTER
	SIO$OUT$BYTE		OUTPUT A BYTE IN HEX
	SIO$OUT$WORD		OUTPUT A WORD IN HEX
	SIO$OUT$BLANK		OUTPUT A SINGLE BLANK
	SIO$OUT$STRING		OUTPUT A STRING
	SIO$OUT$HEADER		OUTPUT A PAPER TAPE HEADER

2.	UTILITY ROUTINES SECTION

	SIO$VAL$HEX		TEST FOR VALID HEX CHAR
	SIO$HEX			CONVERT TO HEX FROM ASCII
	SIO$VALID$REG$FIRST	TEST FOR VALID REGISTER FIRST CHAR
	SIO$VALID$REG		TEST FOR VALID REGISTER NAME
	SIO$CRLF		OUTPUTS A CR AND LF
	SIO$TEST$WORD$MODE	TEST FOR A 'W' IN COMMAND
	SIO$SCAN$BLANK		SCANS FOR OPTIONAL BLANK

3.	ARGUMENT EXPRESSION EVALUATION SECTION

	SIO$GET$WORD		GET AN WORD EXPRESSION
	SIO$GET$ADDR		GET AN ADDRESS EXPRESSION
	SIO$UPDATE$IP		OPTIONAL UPDATE OF CS:IP

4.	PAPER TAPE READ SECTION

	SIO$READ$CHAR		READ CHAR FROM TTY READER
	SIO$READ$BYTE		READ A BYTE
	SIO$READ$WORD		READ A WORD

5.	INTERRUPT AND RESTORE/EXECUTE ROUTINES

	SAVE$REGISTERS		SAVES USER REGISTERS
	RESTORES$EXECUTE	RESTORE MACHINE STATE AND EXEC
	INTERRUPT1$ENTRY	INTERRUPT ROUTINE FOR SINGLE STEP
	INTERRUPT3$ENTRY	INTERRUPT ROUTINE FOR GO
	INIT$INT$VECTOR		INITIALIZES INTERRUPT VECTORS
*/


/************************
* BASIC I/O SECTION	*
*************************/

SIO$CHAR$RDY:
	/*TESTS FOR INPUT CHARACTER PENDING BY READING THE STATUS
	PORT AND MASKING WITH SIO$RXRDY (READ DATA READY).
	RETURNS TRUE IF NOT EMPTY (CHAR PENDING) AND FALSE IF NO CHAR
	PENDING*/

PROCEDURE BYTE;
	IF (INPUT(SIO$STAT$PORT) AND SIO$RXRDY)=0 THEN RETURN FALSE;
	RETURN TRUE;
END;

SIO$CHECK$CONTROL$CHAR:

	/*THIS ROUTINE CHECKS IF A CONTROL CHARACTER HAS BEEN INPUT TO
	THE SERIAL PORT.	AFTER A CONTROL-S IT WAITS FOR A CONTROL-Q
	BEFORE RETURNING TO THE CALLER.	A CONTROL-C CAUSES A JUMP TO
	THE ERROR ROUTINE.*/

PROCEDURE;
	CHAR = INPUT(SIO$DATA$PORT) AND 07FH;
	IF CHAR=13H THEN		/* CONTROL-S ? */
	DO WHILE CHAR<>11H;	 /* CONTROL-Q	*/
	 IF SIO$CHAR$RDY THEN
		DO;
		CHAR = INPUT(SIO$DATA$PORT) AND 07FH;
		IF CHAR=03H THEN GOTO ERROR;
		END;
	END;
	ELSE IF CHAR = 03H THEN GOTO ERROR;
END SIO$CHECK$CONTROL$CHAR;


SIO$OUT$CHAR:

	/* THIS ROUTINE OUTPUTS THE INPUT PARAMETER TO THE USART OUTPUT
	PORT WHEN USART IS READY FOR OUTPUT 8XMIT BUFFER EMPTY). */

	PROCEDURE(C);
	DECLARE C BYTE;
	IF SIO$CHAR$RDY THEN CALL SIO$CHECK$CONTROL$CHAR;
	DO WHILE (INPUT(SIO$STAT$PORT) AND SIO$TXRDY)=0;END;
	OUTPUT(SIO$DATA$PORT) = C;
	END;


SIO$GET$CHAR:

	/* THIS ROUTINE INPUTS A CHARACTER FROM THE INPUT PORT AND
	RETURNS WITH IT IN THE GLOBAL 'CHAR'.	THE CHARACTER IS ECHOED
	TO THE OUTPUT PORT IF PRINTABLE. */

	PROCEDURE;
	DO WHILE (INPUT(SIO$STAT$PORT) AND SIO$RXRDY)=0;END;
	CHAR = INPUT(SIO$DATA$PORT) AND 07FH;
	IF CHAR>=ASBL THEN CALL SIO$OUT$CHAR(CHAR);
	END;

SIO$OUT$BYTE:

	/* THIS ROUTINE OUTPUTS THE SINGLE INPUT PARAMETER TO THE
	USART IN ASCII HEXADECIMAL FORMAT. */

	PROCEDURE(B);
	DECLARE B BYTE;
	CALL SIO$OUT$CHAR(ASCII(SHR(B,4) AND 0FH));
	CALL SIO$OUT$CHAR(ASCII(B AND 0FH));
	CHECK$SUM = CHECK$SUM - B;
	END;


SIO$OUT$WORD:

	/* THIS ROUTINE OUTPUTS THE INPUT PARAMETER AS 4 ASCII
	HEXADECIMAL CHARACTERS TO THE USART OUTPUT PORT. */

	PROCEDURE(W);
	DECLARE W WORD;
	CALL SIO$OUT$BYTE(HIGH(W));
	CALL SIO$OUT$BYTE(LOW(W));
	END;


SIO$OUT$BLANK:

	/* THIS ROUTINE OUTPUTS ONE BLANK. */

	PROCEDURE;
	CALL SIO$OUT$CHAR(ASBL);
	END;


SIO$OUT$STRING:

	/* OUTPUTS A STRING POINTED TO BY THE FIRST PARM. */

	PROCEDURE(PTR);
	DECLARE PTR POINTER, STR BASED PTR (1) BYTE;
	IX = 0;
	DO WHILE STR(IX)<>0;
	 CALL SIO$OUT$CHAR(STR(IX));
	 IX = IX + 1;
	END;
	END;



SIO$8251$SETTLING$DELAY:

	/* THIS DELAY ROUTINE ALLOWS THE USART SUFFICIENT TIME TO
	SETTLE AFTER WRITE OPERATIONS. */

	PROCEDURE;
	I = SHR(0FFH,0FFH);
	END;


/*****************************
* UTILITY ROUTINES SECTION	*
*****************************/

SIO$VALID$HEX:

	/* THIS ROUTINE TESTS IF THE INPUT PARM IS A VALID ASCII
	HEX DIGIT AND RETURNS TRUE AS THE VALUE OF THE PROCEDURE
	IF SO AND FALSE IF NOT. */

	PROCEDURE (H) BYTE;
	DECLARE H BYTE;
	DO I=0 TO LAST(ASCII);
	 IF H=ASCII(I) THEN RETURN TRUE;
	END;
	RETURN FALSE;
	END;


SIO$HEX:

	/* THIS ROUTINE CONVERTS THE INPUT PARM FROM ASCII TO ITS
	BINARY EQUIVALENT AND RETURNS IT AS THE VALUE OF THE PROCEDURE.
	 NO CHECK IS MADE FOR INPUT VALIDITY. */

	PROCEDURE(C) WORD;
	DECLARE C BYTE;
	IF C<='9' THEN RETURN DOUBLE(C-30H);
	ELSE RETURN DOUBLE(C-37H);
	END;


SIO$VALID$REG$FIRST:

	/* THIS ROUTINE CHECKS IF 'CHAR' IS A VALID FIRST LETTER OF A
	REGISTER NAME AND RETURNS TRUE AS THE VALUE OF THE PROCEDURE IF SO. */

	PROCEDURE BYTE;
	DO I=0 TO 26 BY 2;
	 IF CHAR=REG(I) THEN RETURN TRUE;
	END;
	RETURN FALSE;
	END;


SIO$VALID$REG:

	/* THIS ROUTINE CHECKS IF THE TWO INPUT PARMS TAKEN TOGETHER
	FORM A VALID REGISTER NAME.	IT SEARCHES THE REGISTER TABLE AND
	IF A MATCH IS FOUND, THE GLOBAL 'REG$INDEX' IS SET TO THE INDEX
	OF THE VALID REGISTER AND THE PROCEDURE RETURNS TRUE.	IF NO MATCH
	THE PROCEDURE RETURNS FALSE AND REG$INDEX IS UNDEFINED. */

	PROCEDURE (C1,C2) BYTE;
	DECLARE (C1,C2) BYTE;
	DO REG$INDEX=0 TO 13;
	 IF C1=REG(REG$INDEX*2) AND C2=REG(REG$INDEX*2+1) THEN RETURN TRUE;
	END;
	RETURN FALSE;
	END;


SIO$CRLF:

	/* THIS ROUTINE OUTPUTS A CR AND LF TO THE OUTPUT PORT. */

	PROCEDURE;
	CALL SIO$OUT$CHAR(ASCR);
	CALL SIO$OUT$CHAR(ASLF);
	END;


SIO$TEST$WORD$MODE:

	/* THIS PROCEDURE TESTS FOR A 'W' FOLLOWING THE COMMAND AND IF
	SO SETS THE FLAG 'WORD$MODE TO TRUE OR FALSE OTHERWISE.
	SCANS OFF OPTIONAL BLANK FOLLOWING COMMAND. */

	PROCEDURE;
	WORD$MODE = FALSE;
	CALL SIO$GET$CHAR;
	IF CHAR='W' THEN
	 DO;
	 WORD$MODE = TRUE;
	 CALL SIO$GET$CHAR;
	 END;
	IF CHAR=ASBL THEN
	 CALL SIO$GET$CHAR;
	END;


SIO$SCAN$BLANK:

	/* THIS ROUTINE IS CALLED AFTER A COMMAND LETTER TO SCAN OFF
	THE OPTIONAL BLANK. */

	PROCEDURE;
	CALL SIO$GET$CHAR;
	IF CHAR=ASBL THEN
	 CALL SIO$GET$CHAR;
	END;


/*********************************************
*	ARGUMENT EXPRESSION EVALUATOR SECTION	*
*********************************************/


SIO$GET$WORD:

	/* THIS ROUTINE READS CHARS FROM THE INPUT PORT AND EVALUATES
	AN EXPRESSION CONSISTING OF '+-' AND OPERATIONS OF HEX NUMBERS
	AND REGISTER NAMES. */

	PROCEDURE WORD;
	DECLARE (SAVE,W) WORD, (OPER,T) BYTE;
	OPER = '+';
	W = 0;
	DO WHILE TRUE;
	 T = CHAR;
	 SAVE = 0;
	 IF SIO$VALID$REG$FIRST THEN
		DO;
		CALL SIO$GET$CHAR;
		IF SIO$VALID$REG(T,CHAR) THEN
		DO;
		SAVE = REG$SAV(REG$INDEX);
		CALL SIO$GET$CHAR;
		GOTO EVAL;
		END;
		ELSE
		SAVE = SIO$HEX(T);
		END;
	IF NOT(SIO$VALID$HEX(T)) THEN GOTO ERROR;
	DO WHILE SIO$VALID$HEX(CHAR);
		SAVE = SHL(SAVE,4) + SIO$HEX(CHAR);
		CALL SIO$GET$CHAR;
		END;
EVAL:	IF OPER='+' THEN
		W = W + SAVE;
		ELSE
		W = W - SAVE;
		IF CHAR=ASCR OR CHAR=':' OR CHAR=',' THEN
		RETURN W;
		IF CHAR='+' OR CHAR='-' THEN
		OPER = CHAR;
		ELSE
		GOTO ERROR;
		CALL SIO$GET$CHAR;
	 END;
	END;

SIO$GET$ADDR:

	/* THIS ROUTINE ACCEPTS A VALID ADDRESS EXPRESSION CONSISTING
	OF AN OPTIONAL <SEG>: AND AN DISPLACEMENT. */

	PROCEDURE(PTR,DEFAULT$BASE);
	DECLARE PTR POINTER, DEFAULT$BASE WORD,
	 ARG BASED PTR STRUCTURE (OFF WORD, SEG WORD);
	ARG.SEG = DEFAULT$BASE;
	ARG.OFF = SIO$GET$WORD;
	IF CHAR=':' THEN
	 DO;
	 CALL SIO$GET$CHAR;
	 ARG.SEG = ARG.OFF;
	 ARG.OFF = SIO$GET$WORD;
	 IF CHAR=':' THEN GOTO ERROR;
	 END;
	END;

SIO$UPDATE$IP:
	/* THIS PROCEDURE IS CALLED BY SINGLE STEP AND TO TO OUTPUT THE
	CURRENT IP AND INSTRUCTION BYTE AND OPEN THE IP FOR INPUT. */
	PROCEDURE;
	CALL SIO$OUT$BLANK;
	CALL SIO$OUT$WORD(IP);
	CSIP.SEG = CS;
	CSIP.OFF = IP;
	CALL SIO$OUT$CHAR('-');
	CALL SIO$OUT$BLANK;
	CALL SIO$OUT$BYTE(MEMORY$CSIP);
	CALL SIO$OUT$BLANK;
	CALL SIO$GET$CHAR;
	IF CHAR<>',' AND CHAR<>ASCR THEN CALL SIO$GET$ADDR(@CSIP,CS);
	END;


	/*********************************************
	*	INTERRUPT AND RESTORE/EXECUTE SECTION	*
	*********************************************/

SAVE$REGISTERS:

	/* THIS ROUTINE IS USED TO SAVE THE STACKED USER'S REGISTERS
	IN THE MONITOR'S SAVE AREA. */

	PROCEDURE;
	BP = MEMORY$USERSTACK;
	USERSTACK.OFF = USERSTACK.OFF + 4;
	DO I=0 TO 10;			/* POP REGISTERS OFF OF STACK */
	 REG$SAV(REG$ORD(I)) = MEMORY$USERSTACK;
	 USERSTACK.OFF = USERSTACK.OFF + 2;
	END;
	 SS = USERSTACK.SEG;
	 SP = USERSTACK.OFF;
	END;

RESTORE$EXECUTE:
	/* THIS PROCEDURE RESTORES THE STATE OF THE USER MACHINES AND
	PASSES CONTROL BACK TO THE USER PROGRAM.	IT CONTAINS A
	MACHINE LANGUAGE SUBROUTINE TO PERFORM THE POPPING OF THE
	USER REGISTERS AND TO EXECUTE AN 'IRET' TO TRANSFER CONTROL
	TO THE USER'S PROGRAM. */
	PROCEDURE;
	DECLARE RESTORE$EXECUTE$CODE(*) BYTE DATA
	 (08BH,0ECH,		/* MOV BP,SP		*/
		08BH,046H,002H,	/* MOV AX,/BP/.PARM2	*/
		08BH,05EH,004H,	/* MOV BX,/BP/.PARM1	*/
		08EH,0D0H,		/* MOV SS,AX	*/
		08BH,0E3H,		/* MOV SP,BX	*/
		05DH,			/* POP BP		*/
		05FH,			/* POP DI		*/
		05EH,			/* POP SI		*/
		05BH,			/* POP BX		*/
		05AH,			/* POP DX		*/
		059H,			/* POP CX		*/
		058H,			/* POP AX		*/
		01FH,			/* POP DS		*/
		007H,			/* POP ES		*/
		0CFH),			/* IRET			*/
	 RESTORE$EXECUTE$CODE$PTR WORD DATA (.RESTORE$EXECUTE$CODE);

	USERSTACK.SEG = SS;
	USERSTACK.OFF = SP;
	DO I=0 TO 10;		/* PUSH USER'S REGISTERS ONTO HIS STACK */
	 USERSTACK.OFF = USERSTACK.OFF - 2;
	 MEMORY$USERSTACK = REG$SAV(REG$ORD(10-I));
	END;
	USERSTACK.OFF = USERSTACK.OFF - 2;
	MEMORY$USERSTACK = BP;
	CALL RESTORE$EXECUTE$CODE$PTR(USERSTACK.OFF,USERSTACK.SEG);
	END;

INTERRUPT1$ENTRY:
	/* THIS PROCEDURE IS CALLED WHEN THE CPU IS INTERRUPTED BY
	EXECUTING AN INSTRUCTION WITH THE TRAP BIT SET (SINGLE STEP). */

	PROCEDURE INTERRUPT 1;
	USERSTACK.OFF = STACKPTR;	/* CHANGE TO MONITOR'S STACK */
	USERSTACK.SEG = STACKBASE;
	STACKPTR = MONITOR$STACKPTR;
	STACKBASE = MONITOR$STACKBASE;
	CALL SAVE$REGISTERS;
	FL = FL AND (NOT STEP$TRAP);	/* CLEAR STEP FLAG */
	IF LAST$COMMAND<>SS$COMMAND THEN	/* CONTINUE IF NOT SS */
	CALL RESTORE$EXECUTE;
	CALL SIO$CRLF;
	CALL SIO$UPDATE$IP;
	IF CHAR=',' THEN
		DO;
		IP = CSIP.OFF;
		CS = CSIP.SEG;
		FL = FL OR STEP$TRAP;	 /* SET STEP FLAG */
		CALL RESTORE$EXECUTE;
		END;
	IF CHAR<>ASCR THEN GOTO ERROR;
	GOTO NEXT$COMMAND;
	END;

INTERRUPT3$ENTRY:
	/* THIS PROCEDURE IS CALLED WHEN THE CPU EXECUTES A 'INT 3'
	INSTRUCTION.	THE MONITOR INSERTS THIS (OCCH) FOR A
	BREAKPOINT.	ALSO AN EXTERNAL INTERRUPT OR A USER SOFTWARE
	INTERRUPT MAY CAUSE THIS PROCEDURE TO CALLED. */

	PROCEDURE INTERRUPT 3;
	USERSTACK.OFF = STACKPTR;
	USERSTACK.SEG = STACKBASE;
	STACKPTR = MONITOR$STACKPTR;
	STACKBASE = MONITOR$STACKBASE;
	CALL SAVE$REGISTERS;
	CALL SIO$CRLF;
	GOTO AFTER$INTERRUPT;
	END;


INTERRUPT0$ENTRY:
	/* THIS PROCEDURE IS CALLED WHEN THE CPU EXECUTES A 'INT 0'
	INSTRUCTION. ALSO AN EXTERNAL INTERRUPT OR A USER SOFTWARE
	INTERRUPT MAY CAUSE THIS PROCEDURE TO CALLED. */

	PROCEDURE INTERRUPT 0;
	USERSTACK.OFF = STACKPTR;
	USERSTACK.SEG = STACKBASE;
	STACKPTR = MONITOR$STACKPTR;
	STACKBASE = MONITOR$STACKBASE;
	CALL SAVE$REGISTERS;
	CALL SIO$CRLF;
	CALL SIO$OUT$STRING(@SIO$DIV$ERR);
	GOTO DISP$CS$IP;
	END;



INIT$INT$VECTOR:

	/* THIS ROUTINE INITIALIZES AN INTERRUPT VECTOR AS FOLLOWS: THE
	OFFSET FROM THE ADDRESS OF 'INT$ROUTINE' CORRECTED BY THE
	APPROPRIATE NUMBER OF BYTES FOR THE INTERRUPT PLM PROLOGUE.
	THE SEGMENT FROM THE CURRENT CS REGISTER IS DETERMINED BY
	A MACHINE LANGUAGE CODED SUBROUTINE. */

	PROCEDURE(INT$VECTOR$PTR,INT$ROUTINE$OFFSET);

	DECLARE INT$VECTOR$PTR POINTER, INT$ROUTINE$OFFSET WORD,
	 VECTOR BASED INT$VECTOR$PTR STRUCTURE (OFF WORD, SEG WORD),
	 CORRECTION LITERALLY '19H',	/* OFFSET FOR PROLOGUE */
	 INIT$INT$VECTOR$CODE(*) BYTE DATA
		(055H,				/* PUSH BP		*/
		08BH,0ECH,			/* MOV BP,SP		*/
		08CH,0C8H,			/* MOV AX,CS		*/
		0C4H,05EH,004H,		/* LES BX,/BP/.PARM1	*/
		026H,089H,007H,		/* MOV ES:W/BX/,AX	*/
		05DH,				/* POP BP		*/
		0C2H,004H,000H),	/* RET 4		*/
	INIT$INT$VECTOR$CODE$PTR WORD DATA (.INIT$INT$VECTOR$CODE);

	CALL INIT$INT$VECTOR$CODE$PTR(@VECTOR.SEG);	/*SEGMENT PORTION*/
	VECTOR.OFF = INTROUTINE$OFFSET - CORRECTION;	/*OFFSET PORTION */
END;

/********************************************************************

	COMMAND MODULE
	==============

ABSTRACT

THIS MODULE CONTAINS ALL THE COMMANDS IMPLEMEMTED AS INDIVIDULA PROCEDURES
AND CALLED FROM THE OUTER BLOCK OF THE COMMAND DISPATCH LOOP.

MODULE ORGANISATION

THIS MODULE CONTAINS THE FOLLOWING SECTIONS:

1. COMMAND SECTION
	SIO$GO			GO
	SIO$SINGLE$STEP	SINGLE STEP
	SIO$EXAM$MEM	EXAMINE MEMORY
	SIO$EXAM$REG	EXAMINE REGISTER
	SIO$MOVE		MOVE
	SIO$DISPLAY		DISPLAY BYTES
	SIO$INPUT		INPUT PORT
	SIO$OUTPUT		OUTPUT PORT
	SIO$WRITE		WRITE DATA RECORD
	SIO$READ		READ DATA RECORD
	FCMD$8086		CONVERT DATA IN MEMORY TO PSUEDO ASM86 SOURCE

2. COMMAND DISPATCH ( OUTER BLOCK, MAIN PROGRAM LOOP )
	NEXT$COMMAND		DISPATCH
	ERROR			ERROR ROUTINE

*/

/*********************
*   COMMAND SECTION  *
**********************/


$INCLUDE (:F0:DIS86B.SRC)


SIO$GO:

/* IMPLEMENTS THE 'GO' COMMAND. THE USER MAY SPECIFY A NEW IP:CS AND AN
OPTIONAL BREAKPOINT. */

PROCEDURE;

	CALL SIO$UPDATE$IP;
	IF CHAR=',' THEN		/* BREAK POINT */
		DO;
		CALL SIO$GET$CHAR;
		CALL SIO$GET$ADDR(@BRK1,CSIP.SEG);
		IF CHAR<>ASCR THEN	GOTO ERROR;
		BRK1$SAVE = MEMORY$BRK1;
		MEMORY$BRK1 = BREAK$INST;
		IF MEMORY$BRK1<>BREAK$INST THEN GOTO ERROR;
		BRK1$FLAG = TRUE;
		END;

	ELSE				/* NO BREAK POINT */
		IF CHAR<>ASCR THEN GOTO ERROR;
		CALL SIO$CRLF;
		IP = CSIP.OFF;
		CS = CSIP.SEG;
		FL =FL AND ( NOT STEP$TRAP );	/* CLEAR IF SET */
		CALL RESTORE$EXECUTE;
	END;

SIO$SINGLE$STEP:

	/* IMPLEMENTS THE SINGLE STEP COMMAND. DISPLAYS IP AND THE
	CURRENT INSTRUCTION BYTE. OPENS CS:IP FOR INPUT. DEPRESSING
	COMMA CAUSES THE MONITOR TO SINGLE STEP THE INSTRUCTION, AND
	PERIOD TERMINATES THE COMMAND. */

PROCEDURE;
	CALL SIO$UPDATE$IP;
	IF CHAR<>',' THEN GOTO ERROR;
	IP = CSIP.OFF;
	CS = CSIP.SEG;
	FL = FL OR STEP$TRAP;
	CALL RESTORE$EXECUTE;
END;

SIO$EXAM$MEM:

	/* IMPLEMENTS THE EXAMINE MEMORY COMMAND */

PROCEDURE;

	DECLARE W WORD;
	CALL SIO$TEST$WORD$MODE;
	CALL SIO$GET$ADDR(@ARG1,CS);
	IF CHAR<>',' THEN GOTO ERROR;
	DO WHILE TRUE;
		CALL SIO$OUT$BLANK;
		IF WORD$MODE THEN
			CALL SIO$OUT$WORD(MEMORY$WORD$ARG1);
		ELSE
			CALL SIO$OUT$BYTE(MEMORY$ARG1);
		CALL SIO$OUT$CHAR('-');
		CALL SIO$OUT$BLANK;
		CALL SIO$GET$CHAR;
		IF CHAR=ASCR THEN RETURN;
		IF CHAR<>',' THEN 
			DO;
			W = SIO$GET$WORD;
			IF (CHAR <> ',') AND (CHAR <> ASCR) THEN GOTO ERROR;
			IF WORD$MODE THEN
				DO;
				MEMORY$WORD$ARG1 = W;
				IF MEMORY$WORD$ARG1 <> W THEN GOTO ERROR;
				END;
			ELSE
				DO;
				MEMORY$ARG1 = LOW(W);
				IF MEMORY$ARG1 <> LOW(W) THEN GOTO ERROR;
				END;
			END;
		IF CHAR=ASCR THEN RETURN;
		IF WORD$MODE THEN
			ARG1.OFF = ARG1.OFF + 2;
		ELSE
			ARG1.OFF = ARG1.OFF + 1;
		CALL SIO$CRLF;
		CALL SIO$OUT$WORD(ARG1.OFF);
	END;
END;

SIO$EXAM$REG:

	/* IMPLEMENTS THE EXAMINE REGISTER COMMAND.	SCANS FOR A VALID
	REGISTER NAME AND DISPLAYS THE VALUE OF THAT REGISTER WHICH IS
	OPTIONALLY OPENED FOR INPUT. COMMA INCREMENTS TO THE NEXT REGISTER
	UNLESS IT IS 'FL' WHICH TERMINATES AS DOES CR */

PROCEDURE;
	DECLARE (T,I) BYTE, SAVE WORD;
	CALL SIO$SCAN$BLANK;
	IF CHAR=ASCR THEN
		DO;
		CALL SIO$CRLF;
		DO I=0 TO 13;
			CALL SIO$OUT$BLANK;
			CALL SIO$OUT$CHAR(REG(I*2));
			CALL SIO$OUT$CHAR(REG(I*2+1));
			CALL SIO$OUT$CHAR('=');
			CALL SIO$OUT$WORD(REG$SAV(I));
			IF I=6 THEN CALL SIO$CRLF;
		END;
		RETURN;
		END;
	IF NOT(SIO$VALID$REG$FIRST) THEN GOTO ERROR;
	T = CHAR;
	CALL SIO$GET$CHAR;
	IF NOT(SIO$VALID$REG(T,CHAR)) THEN GOTO ERROR;
	I = REG$INDEX;
	DO WHILE TRUE;
		CALL SIO$OUT$CHAR('=');
		CALL SIO$OUT$WORD(REG$SAV(I));
		CALL SIO$OUT$CHAR('=');
		CALL SIO$OUT$BLANK;
		CALL SIO$GET$CHAR;
		IF CHAR<>',' AND CHAR<>ASCR THEN
			DO;
			SAVE = SIO$GET$WORD;
			IF (CHAR <> ',') AND (CHAR <> ASCR) THEN GOTO ERROR;
			REG$SAV(I) = SAVE;
			END;
		IF CHAR=ASCR OR I=13 THEN RETURN;
		I = I + 1;
		CALL SIO$CRLF;
		CALL SIO$OUT$CHAR(REG(I*2));
		CALL SIO$OUT$CHAR(REG(I*2+1));
	END;
END;

SIO$MOVE:

	/* IMPLEMENTS THE MOVE COMMAND. ACCEPTS 3 ARGUMENTS AND MOVES THE
	BLOCK OF MEMORY SPECIFIED BY ARG1-ARG2 TO ARG3. ARG2<ARG1 OR THERE
	IS A DIFFERENCE WHEN THE BYTE READ BACK, THEN ERROR. */

	PROCEDURE;
		CALL SIO$SCAN$BLANK;
		CALL SIO$GET$ADDR(@ARG1,CS);		/* FIRST ARGUMENT */
		IF CHAR<>',' THEN GOTO ERROR;
		CALL SIO$GET$CHAR;
		END$OFF = SIO$GET$WORD;		/* SECOND ARGUMENT */
		IF END$OFF<ARG1.OFF THEN GOTO ERROR;
		IF CHAR<>',' THEN GOTO ERROR;
		CALL SIO$GET$CHAR;
		CALL SIO$GET$ADDR(@ARG3,CS);	/* THIRD ARGUMENT */
		IF CHAR<>ASCR THEN GOTO ERROR;
		CALL SIO$CRLF;
	LOOP:
		MEMORY$ARG3 = MEMORY$ARG1;
		IF MEMORY$ARG3<>MEMORY$ARG1 THEN GOTO ERROR;
		IF ARG1.OFF = END$OFF THEN RETURN;
		ARG1.OFF = ARG1.OFF + 1;
		ARG3.OFF = ARG3.OFF + 1;
		GOTO LOOP;
	END;

SIO$DISPLAY:

	/* IMPLEMENTS THE DISPLAY BYTE COMMAND. IF CALLED WITH 1 PARM THEN
	OUTPUTS A SINGLE BYTE.	IF CALLED WITH 2 PARAMS THEN OUTPUTS THE RANGE
	BETWEEN THE TWO ADDRESSES.	IF OFFSET<BEGIN THEN OUTPUTS ONLY A SINGLE
	BYTE. */

	PROCEDURE;
		DECLARE T BYTE;
		CALL SIO$TEST$WORD$MODE;
		CALL SIO$GET$ADDR(@ARG1,CS);
		IF CHAR=ASCR THEN
			END$OFF = ARG1.OFF;
		ELSE
			DO;
			IF CHAR<>',' THEN GOTO ERROR;
			CALL SIO$GET$CHAR;
			END$OFF = SIO$GET$WORD;
			IF END$OFF < ARG1.OFF THEN GOTO ERROR;
			IF CHAR<>ASCR THEN GOTO ERROR;
			END;
NEWLINE:
	CALL SIO$CRLF;
	CALL SIO$OUT$WORD(ARG1.OFF);
LOOP:	CALL SIO$OUT$BLANK;
	IF WORD$MODE THEN
		DO;
		CALL SIO$OUT$WORD(MEMORY$WORD$ARG1);
		IF ARG1.OFF = END$OFF THEN RETURN;
		ARG1.OFF = ARG1.OFF + 1;
		END;
	ELSE
		CALL SIO$OUT$BYTE(MEMORY$ARG1);
	IF ARG1.OFF>=END$OFF THEN RETURN;
	ARG1.OFF = ARG1.OFF + 1;
	T = ARG1.OFF AND 000FH;
	IF T=0 OR (WORD$MODE AND T=1) THEN GOTO NEWLINE;
	GOTO LOOP;
	END;


SIO$INPUT:

	/* THIS ROUTINE IMPLEMENTS THE 'INPUT' COMMAND. USER SPECIFIES
	A PORT AND THE DATUM OF THE PORT IS DISPLAYED. */

	PROCEDURE;
		DECLARE PORT WORD;
		CALL SIO$TEST$WORD$MODE;
		PORT = SIO$GET$WORD;
	LOOP:
		IF CHAR<>',' THEN GOTO ERROR;
		CALL SIO$CRLF;
		IF WORD$MODE THEN
			CALL SIO$OUT$WORD(INWORD(PORT));
		ELSE
			CALL SIO$OUT$BYTE(INPUT(PORT));
		CALL SIO$GET$CHAR;
	IF CHAR=ASCR THEN RETURN;
	GOTO LOOP;
	END;


SIO$OUTPUT:

	/* THIS ROUTINE IMPLEMENTS THE 'OUTPUT' COMMAND. THE USER SUPPLIED
	DATUM IS OUTPUT TO THE SPECIFIED PORT. */

	PROCEDURE;
		DECLARE (DATUM, PORT) WORD;
		CALL SIO$TEST$WORD$MODE;
		PORT = SIO$GET$WORD;
		IF CHAR<>',' THEN GOTO ERROR;
		CALL SIO$GET$CHAR;
	LOOP:
		DATUM = SIO$GET$WORD;
		IF CHAR=':' THEN GOTO ERROR;
		IF WORD$MODE THEN
			OUTWORD(PORT) = DATUM;
		ELSE
			OUTPUT(PORT) = LOW(DATUM);
		IF CHAR=',' THEN
			DO;
			CALL SIO$CRLF;
			CALL SIO$OUT$CHAR('-');
			CALL SIO$OUT$BLANK;
			CALL SIO$GET$CHAR;
			IF CHAR<> ASCR THEN GOTO LOOP;
			END;
		END;




/*************************************
*	COMMAND DISPATCH MAIN PROGRAM LOOP*
*************************************/

	DISABLE;
	OUTPUT(KB$STAT$PORT) = KB$INIT$MODE;
	OUTPUT(KB$STAT$PORT) = KB$INIT$SCAN;
	OUTPUT(KB$STAT$PORT) = 90H;	/* OUTPUT '8086' SIGNON BACKWARDS */
	DO I=0 TO 7;
	OUTPUT(KB$DATA$PORT) = KB$SIGNON(I);
	END;

/* THIS STRANGE SEQUENCE OF CODE IS USED TO GUARANTEE CORRECT USART
	INITIALIZATION AFTER EITHER: 1) HARDWARE RESET (EXPECTING MODE)
	2) SOFTWARE RESTART (EXPECTING COMMAND). */

	OUTPUT(SIO$STAT$PORT) = SIO$8251$RESET;
	CALL SIO$8251$SETTLING$DELAY;
	OUTPUT(SIO$STAT$PORT) = 25H;
	CALL SIO$8251$SETTLING$DELAY;
	OUTPUT(SIO$STAT$PORT) = SIO$8251$RESET;
	CALL SIO$8251$SETTLING$DELAY;
	OUTPUT(SIO$STAT$PORT) = SIO$8251$MODE;
	CALL SIO$8251$SETTLING$DELAY;
	OUTPUT(SIO$STAT$PORT) = SIO$8251$CMND;
	CALL SIO$8251$SETTLING$DELAY;
	CALL SIO$OUT$STRING(@SIO$SIGNON);

	/* INITIALIZE USER'S REGISTERS */
	CS,SS,DS,ES,FL,IP = 0;
	SP = USER$INIT$SP;

	/* INITIALIZE INTERRUPT VECTORS */
	CALL INIT$INT$VECTOR(@INT$VECTOR(0),.INTERRUPT0$ENTRY);
	CALL INIT$INT$VECTOR(@INT$VECTOR(1),.INTERRUPT1$ENTRY);
	CALL INIT$INT$VECTOR(@INT$VECTOR(2),.INTERRUPT3$ENTRY);
	CALL INIT$INT$VECTOR(@INT$VECTOR(3),.INTERRUPT3$ENTRY);

	BRK1$FLAG = FALSE;
	MONITOR$STACKPTR = STACKPTR;
	MONITOR$STACKBASE = STACKBASE;

NEXT$COMMAND:
	/* THIS IS THE PERPETUAL COMMAND LOOP WHICH DISPATCHES TO EACH
	COMMAND WHICH IS A SEPARATED PROCEDURE. */

	CALL SIO$CRLF;
	CALL SIO$OUT$CHAR(0);
	CALL SIO$OUT$CHAR('.');
	CALL SIO$GET$CHAR;
	DO I=0 TO LAST(SIO$CMND);
	 IF CHAR=SIO$CMND(I) THEN GOTO DISPATCH;
	END;
	GOTO ERROR;
DISPATCH:
	LAST$COMMAND = I;
	DO CASE I;
	CALL SIO$EXAM$MEM;
	CALL SIO$EXAM$REG;
	CALL SIO$GO;
	CALL SIO$SINGLE$STEP;
	CALL SIO$MOVE;
	CALL SIO$DISPLAY;
	CALL SIO$INPUT;
	CALL SIO$OUTPUT;
	CALL FCMD$8086;
	CALL SIO$OUT$STRING(@HELP$MESS);
	END;
	GOTO NEXT$COMMAND;

ERROR:
	/* THIS ROUTINE HANDLES ALL ERRORS DETECTED BY THE MONITOR AND 
	WILL OUTPUT THE ERROR PROMPT TO THE OUTPUT PORT. */

	CALL SIO$OUT$CHAR('#');
	GOTO NEXT$COMMAND;

AFTER$INTERRUPT:
	/* THIS ROUTINE IS CALLED AFTER AN INTERRUPT TO DISPLAY THE CS:IP
	AND RESTORE THE BREAKPOINTED INSTRUCTION. */

	IF BRK1$FLAG THEN
	DO;
	MEMORY$BRK1 = BRK1$SAVE;
	BRK1$FLAG = FALSE;
	IF ((IP-1) AND 000FH)=(BRK1.OFF AND 000FH) AND
		(SHR(IP-1,4)+CS)=(SHR(BRK1.OFF,4)+BRK1.SEG) THEN
		DO;
		IP = IP - 1;
		CALL SIO$OUT$STRING(@SIO$BREAK$MSG);
		END;
	END;

DISP$CS$IP:
	CALL SIO$OUT$CHAR('@');
	CALL SIO$OUT$WORD(CS);
	CALL SIO$OUT$CHAR(':');
	CALL SIO$OUT$WORD(IP);
	GOTO NEXT$COMMAND;

	END MONITOR;	/* END OF MODULE */
EOF
