;
;CP/M MACRO ASSEM 2.0	#001	MOSS 2.2 MONITOR
;
	TITLE	'MOSS 2.2 MONITOR'
	PAGE	68
	MACLIB	Z80
;
.8080
;
; MOSS MONITOR (VERSION 2.2)
;
; 20 JUNE 1980
; ALL RIGHTS RESERVED BY ROBERT B. MASON
;
; OCR PDF Documents 09-20-2014 by Larry Kraemer
; Modify Z80.LIB to add needed Macro's by Garry Kraemer
; Debugging & Corrections by Garry Kraemer - Senior Software Engineer
;
MOSS	ORG	0F000H
ROM	EQU	0F000H	;ROM START ADDRESS
WSVEC	EQU	0	;VECTOR FOR WARM RESTART
NBKPTS	EQU	2	;NUMBER OF BREAKPOINTS
CTRLS	EQU	13H	;ASCII DC3
CR	EQU	0DH	;ASCII CARRIAGE RETURN
LF	EQU	0AH	;ASCII LINE FEED
FMFD	EQU	0CH	;ASCII FORM FEED
BELL	EQU	7	;ASCII CNTRL CHAR TO RING THE BELL
IOBYTE	EQU	3	;ADDRESS OF I/O CONTROL BYTE
SDATA	EQU	20H	;SERIAL DATA PORT BASE ADDRESS
SINTEN	EQU	SDATA+1	;SERIAL INTERRUPT ENABLE REGISTER
SIDENT	EQU	SDATA+2	;SERIAL INTERRUPT IDENTIFICATION REGIS
SLCTRL	EQU	SDATA+3	;SERIAL LINE CONTROL REGISTER
SMDMCT	EQU	SDATA+4	;SERIAL MODEM CONTROL REGISTER
SLSTAT	EQU	SDATA+5	;SERIAL LINE STATUS REGISTER
SMDMST	EQU	SDATA+6	;SERIAL MODEM STATUS REGISTER
;
;
SPSV	EQU	6	;STACK POINTER SAVE LOCATION
;
;
; REGISTER STORAGE DISPLACEMENTS FROM
; NORMAL SYSTEM STACK LOCATION.
;
ALOC	EQU	15H
BLOC	EQU	13H
CLOC	EQU	12H
DLOC	EQU	11H
ELOC	EQU	10H
FLOC	EQU	14H
HLOC	EQU	31H
LLOC	EQU	30H
PLOC	EQU	34H
SLOC	EQU	17H
TLOC	EQU	35H
TLOCX	EQU	25H
LLOCX	EQU	20H
APLOC	EQU	9
BPLOC	EQU	11
CPLOC	EQU	10
DPLOC	EQU	13
EPLOC	EQU	12
FPLOC	EQU	8
HPLOC	EQU	15
LPLOC	EQU	14
XLOC	EQU	7
YLOC	EQU	5
RLOC	EQU	2
ILOC	EQU	3
;
; JUMP TARGETS FOR BASIC INPUT/OUTPUT
;
CBOOT:	JMP	INIT	;COLD START
CONIN:	JMP	CI	;CONS0LE INPUT
READER:	JMP	RI	;READER INPUT
CONOUT:	JMP	CO	;CONSOLE OUTPUT
PUNCH:	JMP	PO	;PUNCH OUTPUT
LIST:	JMP	LO	;LIST OUTPUT
CONST:	JMP	CSTS	;CONSOLE STATUS
	JMP	IOCHK	;PUT IOBYTE INTO (A)
	JMP	IOSET	;(C) HAS A NEW IOBYTE
	JMP	MEMCK	;MEMORY LIMIT CHECK
	JMP	RTS	;IODEF- DEFINE USER I/O ENTRY POINTS
	JMP	RTS	;SPCL- I/O CONTROL
	JMP	REST	;BREAKPOINT ENTRY POINT
;
; TBL CONTAINS THE ADDRESSES OF THE ACTION ROUTINES
; THE EXECUTIVE USES IT TO LOOK UP THE DESIRED ADDRESS.
TBL:	DW	ASGN
	DW	QPRT
	DW	QPRT
	DW	DISP
	DW	EOF
	DW	FILL
	DW	GOTO
	DW	HEXN
	DW	INPT
	DW	QPRT
	DW	QPRT
	DW	LEADER
	DW	MOVE
	DW	QPRT
	DW	OUPT
	DW	QPRT
	DW	QUERY
	DW	READ
	DW	SUBS
	DW	MTEST
	DW	QPRT
	DW	COMP
	DW	WRITE
	DW	XMNE
	DW	I8250
	DW	BYE
;
; THE COLD INITIALIZATION CODE
;
INIT:	DI		;DISABLE INTERRUPTS
	LXI	SP,3FH	;USE STACK TO INITIALIZE RESTARTS
	LXI	H,JMP*256	; WITH RESTART ERROR VECTORS 
	LXI	D,RSTER
	MVI	B,16	;16 TIMES (64 BYTES)
INIT1:	PUSH	D
	PUSH	H
	DJNZ	INIT1
;
	LXI	SP,FAKE-2 	;SET UP TEMPORARY STACK
	MVI	A,0	  	;SKIP THE NEXT INST
	ORG	$-1		;SAVE A BYTE HERE
;
; MEMSIZ CALCULATES THE TOP OF CONTIGUOUS RAM. IT SEARCHES
; FROM THE BOTTOM UP UNTIL A NON-RAM LOCATION IS
; FOUND. IT THEN TAKES OFF FOR MONITOR WORK SPACE
; NEEDS AND RETURNS THE VALUE IN (H,L).
MEMSIZ:	PUSH	B		;MOITOR START LOCATION
	LXI	B,ROM
	LXI	H,-1		;START OF MEMORY ADDRESS SPACE
MEMSZ1:	INR	H
	MOV	A,M
	CMA
	MOV	M,A
	CMP	M
	CMA
	MOV	M,A
	JRNZ	MEMSZ2
;
	MOV	A,H	;SEE IF ON MONITOR BORDER
	CMP	B
	JRNZ	MEMSZ1
;
MEMSZ2:	DCR	H	;TAKE OFF WORKSPACE
	LXI	B,EXIT-ENDX-3*NBKPTS+1
	DAD	B
	POP	B	;(B,C) IS UNPREDICTABLE DURING INIT
	RET
;
; ROUTINE MEMCHK FINDS THE CURRENT TOP OF CONTIGUOUS MEMORY
; (LESS THE MONITOR WORKSPACE) AND RETURNS THE VALUE.
;
MEMCK:	PUSH	H	;SAVE (H,L)
	CALL	MEMSIZ	;GET THE RAM SIZE
	MOV	A,L
	SUI	60	;TAKE OFF WORK SPACE
	JRNC	MEMCK0
;
	DCR	H
MEMCK0:	MOV	B,H
	POP	H
	RET
;
FAKE:	DW	FAKE+2
	SPHL
	LXI	D,EXIT
	XCHG
	LXI	B,ENDX-EXIT
	LDIR
;
	LXI	B,3*NBKPTS
	PUSH	D
	POP	H
	DCX	H
	LDIR
;
	LXI	H,-24
	DAD	SP
	PUSH	H
	INX	H 	;ADJUST USER STACK LOCATION
	INX	H
	SHLD	SPSV 	;SAVE THE STACK INITIAL VALUE
	MVI	D,10 	;INITIALIZE REGISTER STORAGE AREA
INIT2:	PUSH	B
	DCR	D 	;LOOP CONTROL
	JRNZ	INIT2
;
; INSERT I/O INIT CODE HERE
	CALL	RTS
	CALL	I8250 	;INITIALIZE THE 8250
	CALL	RTS
	LXI	H,LOGMSG ;LOG ONTO THE SYSTEM
	CALL	PRTWD
	JMPR	WINIT	;GO TO MONITOR EXECUTIVE
;
; ROUTINE EXF READS ONE PARAMETER. IT EXPECTS THE FIRST
; CHARACTER OF THE PARAMETER TO BE IN THE A REGISTER
; ON ENTRY.
EXF:	MVI	B,1	;SET UP FOR ONE PARAMETER
	LXI	H,0
	JMPR	EX1	;FIRST CHARACTER IN A ALREADY
;
; ROUTINE EXPR READS PARAMETERS FROM THE CONSOLE
; AND DEVELOPS A 16 BIT HEXADECIMAL FOR EACH ONE.
; THE NUMBER OF PARAMETERS WANTED IS IN THE B REG
; ON ENTRY. A CARRIAGE RETURN WILL TERMINATE THE
; ENTRY SEQUENCE. A BLANK OR A COMMA WILL END THE
; CURRENT PARAMETER ENTRY. EACH PARAMETER ONLY
; TAKES THE LAST 4 DIGITS TYPED IN; ANY EXCESS IS
; DISCARDED. A NON-HEX DIGIT WILL TERMINATE THE
; ENTRY SEQUENCE AND CAUSE A WARM BOOT OF THE MON.
;
AS3:	DJNZ	AS2	;PART OF THE ASSIGN CODE
;
EX3:	JRNZ	QPRT	;NON-ZERO IS ERROR
;
EXPR1:	DCR	B	;MORE PARAMETERS?
	RZ 		;NO, RETURN
EXPR:	LXI 	H,0 	;INITIALIZE PARAMETER
EX0:	CALL 	ECHO 	;GET NEXT NUMBER
EX1:	MOV	C,A	;SAVE CHAR FOR LATER USE
	CALL 	NIBBLE 
	JRC 	EX2 	;NOT A NUMBER, JUMP
;
	DAD	H	;MULTIPLY BY 16
	DAD	H
	DAD	H
	DAD	H
	ORA	L	;ADD ON NEW DIGIT
	MOV	L,A
	JMPR	EX0	;GO GET NEXT DIGIT
;
EX2:	XTHL		;PUT UNDER RETURN ADDRESS ON STACK
	PUSH	H	;RESTORE RETURN ADDRESS
	MOV	A,C	;REGET THE LAST CHARACTER
	CALL	P2C	;TEST FOR DELIMITER
	JRNC	EX3	;JUMP IF NOT CARRIAGE RETURN
;
	DJNZ	QPRT	;CARRET WITH MORE PARAM MEANS ERROR
;
	RET
;
; MAIN	ACTION ROUTINES
;
; LOGICAL ASSIGNMENT OF PERIPHERALS
;
;THIS ROUTINE CONTROLS THE ASSIGNMENT OF PHYSICAL
;PERIPHERALS TO THE FOUR LOGICAL DEVICE TYPES. IT
;ALTERS IOBYTE (MEMORY LOCATION 0003) TO MATCH THE
;CURRENT ASSIGNMENT. THE FOUR LOGICAL DEVICES ARE
;CONSOLE, READER LIST, AND PUNCH. IN ALL CASES,
;THE TTY DEVICE IS SET UP AS THE DEFAULT DEVICE.
;
ASGN:	CALL	ECHO	;GET THE LOGICAL DEVICE DESIRED
	LXI	H,ALT	;START OF CONVERSION TABLE
	LXI	D,APT-ALT 	;DISTANCE BETWEEN LOGICAL CHOICE
	MVI	B,4	;NUMBER OF LOGICAL CHOICES
AS0:	CMP	M	;IS THS ONE IT?
	JRZ	AS1	;YES, JUMP
;
	DAD	D	;NO, GO TO NEXT LOGICAL ENTRY
	DJNZ	AS0	
;
QPRT:	LXI	H,QMSG	;GET ADDRESS OF QUESTION MARK MSG
	CALL	PRTWA	;PRNT IT
; THE WARM START CODE
WINIT:	LHLD	SPSV	;RESET THE STACK
	SPHL
WINITA:	LXI	H,WINIT ;RESET RETURN AND WARM START VECTOR
	PUSH	H
	SHLD	WSVEC+1
	MVI	A,0C3H
	STA	WSVEC
	CALL	CRLF	;START A NEW LINE
	CALL	DECHO	;GET THE COMMAND
	SUI	'A'	;GET RID OF ASCII ZONE
	JRC	QPRT	;BAD COMMAND
;
	CPI	'Z'-'A'+1 	;CHECK UPPER LIMIT
	JRNC	QPRT	;BAD COM AND
;
	ADD	A	;DOUBLE IT FOR TABLE OFFSET
	MOV	E,A	;SET UP FOR DOUBLE ADD
	MVI	D,0
	MVI	B,2	;SET UP FOR TWO PARAMETERS
	LXI	H,TBL	;GET ACTION ROUTINE ADDRESS
	DAD	D
	MOV	A,M	;LOAD H,L INDIRECT
	INX	H
	MOV 	H,M
	MOV 	L,A
	PCHL		;GO TO ACTION ROUTINE
;
; FILL ACTION ROUTINE
;
;THIS ROUTINE FILLS A BLOCK OF MEMORY WITH A USER-
;DETERMINED CONSTANT. IT EXPECTS THREE PARAMETERS
;TO BE ENTERED IN THE FOLLOWING ORDER:
;
;START ADDRESS
;FINISH ADDRESS
;FILL VALUE
;
FILL:	CALL	EXPR3	;GET THREE PARAMETERS
FIO:	MOV	M,C	;PUT DOWN THE FILL VALUE
	CALL	HILO	;INCREMENT AND CHECK THE POINTER
	JRNC	FIO	;NOT DONE YET, JUMP
	POP	D	;RESTORE STACK POINTER IN CASE
	JMPR	WINIT	; STACK WAS OVERWRITTEN
;
AS1:	MOV	D,B	;SAVE THE COUNTER RESIDUE
	MVI	B,4	;LOOP CONTROL
	CALL	DECHO	;GET THE NEW ASSIGNMENT
AS2:	INX	H	;INCREMENT POINTER
	CMP	M	;SEE IF THIS IS IT
	JRNZ	AS3
;
	MOV	L,B	;SAVE THE RESIDUE TO FORM ASGT
	DCR	L	;ADJUST VALUE
	MOV	B,D	;REGET THE LOGICAL RESIDUE
	MVI	H,3	;SET UP THE IOBYTE MASK
	DCR	B	;ADJUST THIS ONE ALSO
	JRZ	AS5	;NO SHFT NEEDED
;
AS4:	DAD	H	;SHIFT THE MASKS INTO POSITION
	DAD	H
	DJNZ	AS4	;NOT DONE YET, JUMP
;
AS5:	LDA	IOBYTE
	ORA	H	;MASK THE DESIRED ASSIGNMENT IN
	XRA	H	;LOGICAL ASGT BITS NOW OFF
	ORA	L	;PUT IN NEW VALUE
	MOV	C,A
IOSET:	MOV	A,C
	STA	IOBYTE	;SAVE NEW ASSIGNMENTS
	RET
IOCHK:	LDA	IOBYTE
	RET
;
ALT:	DB	'L'	;LOGICAL LIST DEVICE TABLE
	DB	'2'	;USER DEVICE #2
	DB	'1'	;USER DEVICE #1
	DB	'L'	;LIST TO HIGH SPEED PRINTER
	DB	'T'	;LIST TO TTY
APT:	DB	'P'	;LOGICAL PUNCH DEVICE TABLE
	DB	'2'	;USER DEVICE #2
	DB	'1'	;USER DEVICE #1
	DB	'P'	;PUNCH TO HIGH SPEED PUNCH
	DB	'T'	;PUNCH TO TTY
ART:	DB	'R'	;LOGICAL READER DEVICE TABLE
	DB	'2'	;USER DEVICE #2
	DB	'1'	;USER DEVICE #1
	DB	'P'	;READER TO HIGH SPEED READER 
	DB	'T'	;READER TO TTY
ACT:	DB	'C'	;LOGICAL CONSOLE DEVICE TABLE
	DB	'1'	;USER DEVICE #1
	DB	'B'	;CONSOLE TO BATCH (PRINTER OR PTR)
	DB	'C'	;CONSOLE TO CRT
	DB	'T'	;CONSOLE TO TTY
;
; THE BYE ROUTINE IS USED TO PREVENT UNAUTHORIZED USAGE
; 	OF THE SYSTEM. THE SYSTEM LOCKS UP AND WILL NOT
; 	RESPOND TO ANYTHING OTHER THAN TWO ASCII BELL
; 	CHARACTERS. WHEN IT SEES THEM CONSECUTIVELY
; 	CONTROL IS RETURNED TO THE MONITOR WITHOUT ALTERING
; 	ANYTHING.
;
BYE:	MVI	B,2	;SET UP FOR TWO CHARACTERS
BYE1:	CALL	CONI	;GO READ THE CONSOLE
	CPI	BELL	;SEE IF AN ASCII BELL
	JRNZ	BYE	;NO, START OVER AGAIN
;
	CALL	ECH1	;ECHO THE BELL
	DJNZ	BYE1	;NOT YET, GET NEXT ONE
;
	RET		;RETURN TO MONITOR
;
; COMPARE ROUTINE
;
;THIS ROUTINE COMPARES TWO BLOCKS OF MEMORY AGAINST EACH
;	OTHER. IF A DIFFERENCE IN THE RELATIVE ADDRESS
;	CONTENTS IS DETECTED THE ADDRESS OF THE FIRST
;	BLOCK IS DISPLAYED ALONG WITH ITS CONTENTS AND
;	THE CONTENTS OF THE OTHER BLOCK'S SAME RELATIVE
;	ADDRESS.
COMP:	CALL	EXPR3	;GO GET THREE PARAMETERS
CMPA:	LDAX	B	;GET SOURCE 2 DATA
	PUSH	B	;SAVE SOURCE 2 POINTER
	MOV	B,M	;READ SOURCE 1 DATA
	CMP	B	;COMPARE DATA
	JRZ	CMPB	;JUMP IF OK
;
	PUSH	PSW	;SAVE SOURCE 2 DATA
	CALL	LADRB	;WRITE THE ADDRESS
	MOV	A,B	;GET SOURCE 1 DATA
	CALL	DASH1	;FORMAT
	POP	PSW	;REGET SOURCE 2 DATA
	CALL	HEX1	;OUTPUT IT
CMPB:	POP	B
	CALL	HILOXB	;INCREMENT SOURCE I POINTER AND SEE IF
	JMPR	CMPA	;JUMP IF NOT DONE YET
;
;
; DISPLAY ACTION ROUTINE
; 	THIS ROUTINE DISPLAYS A BLOCK OF MEMORY ON THE
; 	CURRENT CONSOLE DEVICE (CONSOLE DUMP). THE USER
; 	MUST SPECIFY THE START AND FINISH ADDRESSES.
; 	THE DISPLAY IS ORGANIZED TO DISPLAY UP TO 16 BYTES
; 	PER DISPLAY LTNE WITH ALL COLUMNS ALIGNED SO
; 	EACH COLUMN HAS THE SAME LAST HEX DIGIT IN ITS ADDRESS
;
DISP:	CALL	EXLF	;GO GET BLOCK LIMITS
DIS1:	CALL	LADRB	;DISPLAY THE START ADDRESS
	MOV	A,L	;SEE IF ON 16 BYTE BOUNDARY
	CALL	TRPLSP	;SKIP OVER TO RIGHT COLUMN
	PUSH	H	;SAVE (H,L)
DIS2:	MOV	A,M	;GET THE CONTENTS
	CALL	HEX1	;OUTPUT IT
	CALL	HILO	;INCREMENT CHECK POINTER
	JRC	DIS7	;DONE IF CARRY SET
	CALL	BLK	;MAKE COLUMNS
	MOV	A,L	;READY FOR NEW LINE?
	ANI	0FH
	JRNZ	DIS2
;
DIS3:	POP	H	;REGET LINE START ADDRESS
	MOV	A,L	;SKIP OVER TO RIGHT SPACE
	ANI	0FH
	CALL	TRPL2
DIS4:	MOV	A,M	;GET MEMORY VALUE
	ANI	7FH	;STRIP OFF PARITY BIT
	MOV	C,A	;SET UP FOR OUTPUT
	CPI	' '	;SEE IF PRINTABLE IN ASCII
	JRC	DIS5	;JUMP IF' SO
;
	CPI	7EH
	JRC	DIS6
;
DIS5:	MVI	C,'.'	;ELSE, PRINT A DOT
DIS6:	CALL	CONOUT
	CALL	HILOX	;INCREMENT (H,L) AND SEE IF DONE
	MOV	A,L	;NOT DONE, READY FOR NEW LINE?
	ANI	0FH
	JRNZ	DIS4	;JUMP IF NOT
;
	JMPR	DIS1	;DO THE NEXT LINE
;
DIS7:	SUB	E	;SKIP OVER TO START ASCII PRINTOUT
	CALL	TRPLSP
	JMPR	DIS3	;GO PRINT THE ASCII
;
;
TRPLSP:	ANI	0FH	;ISOLATE THE LOW FOUR BITS
	MOV	B,A	;PREPARE TO SPACE OVER TO RIGHT COLUMN
	ADD	A	;TRIPLE THE COUNT
	ADD	B
TRPL2:	MOV	B,A	;PUT BACK INTO B
	INR	B	;ADJUST COUNTER
TRPL1:	CALL	BLK	;DO THE SPACING
	DJNZ	TRPL1	;NO, DO ANOTHER COLUMN
;
	RET
;
; GO TO ACTION ROUTINE
;
; GOTO COMMAND TRANSFERS CONTROL TO A SPECIFIED ADDRESS.
;   IT ALLOWS THE SELECTIVE SETTING OF UP TO TWO BREAKPOINTS
;   AS WELL AS ALLOWING ANY CONSOLE INPUT TO BREAKPOINT
;   THE RUN, AS LONG AS INTERRUPT 1 IS ACTIVE.
;
GOTO:	CALL	PCHK	;SEE IF OLD ADDRESS WANTED
	JRC	GO3	; YES, JUMP
	JRZ	GO0	; YES, BUT SET SOME BREAKPOINTS
	CALL	EXF	;GET NEW GOTO ADDRESS
	POP	D
	LXI	H,PLOC	;PUT ADDRESS IN PC LOCATION
	DAD	SP
	MOV	M,D	;LOW BYTE
	DCX	H
	MOV	M,E	;HIGH BYTE
	MOV	A,C
	CPI	CR	;SEE IF A CR WAS LAST ENTERED
	JRZ	GO3	
;
GO0:	MVI	B,NBKPTS
	LXI	H,TLOC	;POINT TO TRAP STORAGE
	DAD	SP
GO1:	PUSH	B	;SAVE NUMBER OF BREAKPOINTS
	PUSH	H	;SAVE STORAGE POINTER
	MVI	B,2	;SET UP TO GET A TRAP ADDRESS
	CALL	EXPR1	;GET A TRAP ADDRESS
	POP	D	;GET THE TRAP ADDRESS INTO (D,E)
	POP	H	;REGET THE STORAGE ADDRESS
	MOV	A,D	;INSURE THE TRAP ADDRESS ISN'T ZERO
	ORA	E
	JRZ	GO2	;JUMP IF SO
;
	MOV	M,E	;SAVE THE BREAKPOINT ADDRESS
	INX	H
	MOV	M,D
	INX	H
	LDAX	D 	;SAVE THE INSTRUCTION FROM THE BP ADDR
	MOV	M,A
	INX	H
	MVI	A,RST OR 8 ;INSERT THE BREAKPOINT
	STAX	D
GO2:	MOV	A,C	;REGET THE DELIMITER TO SEE
	CPI	CR	; IF WE ARE DONE SETTING BREAKPOINTS
	POP	B 	; UNLOAD THE STACK FIRST
	JRZ	GO3	;YES, JUMP
;
	DJNZ	GO1	;JUMP IF NOT AT BP LIMIT
;
GO3:	CALL	CRLF
	POP	H	;GET RID OF STACK JUNK
	LXI	H,RS9
	PUSH	H
	LXI	H,REST	
	SHLD	9	;SET BREAKPOINT JUMP VECTOR ADDRESS
	LXI	H,24	;FIND REGISTER SET ROUTINE ADDRESS
	DAD	SP
	POP	D	;ADJUST THE STACK
	PCHL		;GO TO THE DESIRED PLACE
;
; GENERAL PURPOSE INPUT/OUTPUT ROUTINES		
;
;THESE ROUTINES ALLOW BYTE BY BYTE INPUT OR OUTPUT FROM
;       THE CURRENT CONSOLE DEVICE. THEY ARE INVOKED BY
;       THE MONITOR "I" OR "O" COMMAND.
INPT:	CALL	EXPR1	;GET INPUT PORT NUMBER
	POP	B	;GET PORT # INTO C REGISTER
	INP	E	;READ VALUE INTO E REGISTER
;
	JMPR	BITS2	;GO DO A BINARY PRINT OF THE VALUE
;
OUPT:	CALL	EXPR	;GET THE ADDRESS AND DATA FOR OUTPUT
	POP	D	;DATA VALUE INTO E
	POP	B	;PORT INTO C
	OUTP	E	;DO THE OUTPUT
;
	RET
;
;  MOVE	ROUTINE
;	THIS ROUTINE EXPECTS THREE PARAMETERS, ENTERED IN THE
;	SOURCE FIRST BYTE ADDRESS
;	SOURCE LAST BYTE ADDRESS
;	DESTINATION FIRST BYTE ADDRESS
;
MOVE:	CALL	EXPR3	;GET THREE PARAMETERS
MOV1:	MOV	A,M	;GET NEXT BYTE
	STAX	B	;MOVE IT
	CALL	HILOXB	;GO INCREMENT CHECK SOURCE POINTER
	JMPR	MOV1	;NOT THERE YET, GO DO IT AGAIN
;
;
; SUBSTITUTE ACTION ROUTINE
;
; THIS ROUTINE ALLOWS THE USER TO INSPECT ANY MEMORY LOCATION
;	AND ALTER THE CONTENTS TF DESIRED AND IF THE ADDRESS
;	IS IN RAM. THE CONTENtS MAY BE LEFT UNALTERED
;	BY ENTERING A SPACE, COMMA OR A CARRIAGE RETURN. IF
;	A CARRIAGE RETURN IS ENTER~D THE ROUTINE IS TERMINATE
;	IF A SPACE OR COMMA IS ENTER~D, THE ROUTINE
;	PROCEEDS TO THE NEXT LOCATION AND PRESENTS THE USER
;	WITH AN OPPORTUNITY TO ALTER IT.
;
SUBS:	CALL	EXPR1	;GO GET ONE PARAMETER
	POP	H	;GET THE START ADDRESS
SUB1:	MOV	A,M	;GET THE CONTENTS OF THE ADDRESS
	CALL	DASH1	;DISPLAY IT ON CONSOLE AND A DASH
	CALL	PCHK	;GET CHECK CHARACTER
	RC		;DONE IF CARRIAGE RETURN
	JRZ	SUB2	;NO CHANGE IF BLANK OR
;
	CPI	LF	;SEE IF PREVIOUS BYTE WANTED
	JRZ	SUB3	;YES, DO IT
;
	PUSH	H	;SAVE MEMORY POINTER
	CALL	EXF	;GO GET REST OF NEW VALUE
	POP	D	;NEW VALUE TO E REGISTER
	POP	H	;RESTORE MEMORY POINTER
	MOV	M,E	;PUT DOWN NEW VALUE
	MOV	A,C	;GET THE DELIMITER
	CPI	CR	;SEE IF DONE (CARRIAGE RETURN)
	RZ		;YES, RETURN TO MONITOR
SUB2:	INX	H	;NO INCREMENT MEMORY PONTER
	INX	H	;ALLOW A FALL THROUGH ON THE NEXT INST
SUB3:	DCX	H	;ADJUST (H,L) AS APPROPRIATE
	MOV	A,L	;GET LO ADDRESS BYTE
	ANI	7	;SEE IF ON A BOUNDARY
	CZ	LADRB	;CALL IF ON THE BOUNDARY
	JMPR	SUB1	;GO DO THE NEXT LOCATION
;
;
; MTEST ROUTINE TESTS A SPECIFIED BLOCK OF MEMORY TO
;   SEE IF ANY HARD DATA BIT FAILURES EXIST. IT IS
;   NOT AN EXHAUSTIVE TEST BUT JUST A QUICK INDICATION
;   OF THE MEMORY'S OPERATVENESS.
;
MTEST:	CALL	EXLF
MTEST1:	MOV	A,M	;READ A BYTE
	PUSH	PSW	;SAVE IT
	CMA		;COMPLEMENT IT
	MOV	M,A	;WRITE IT
	XRA	M	;RESULT SHOULD BE ZERO
	CNZ	BITS	;LOG ERROR IF NOT
MTEST2:	POP	PSW
	MOV	M,A	;RESTORE ORIGINAL BYTE
	CALL	HILOX	;PONT TO NEXT AND SEE IF DONE
	JMPR	MTEST1 ;NO, CONTINUE
;
BITS:	PUSH	D	;SAVE (D,E)
	MOV	E,A	;SAVE ERROR PATTERN IN E
	CALL	LADRB	;FIRST PRINT THE ADDRESS
BITS2:	MVI	B,8	;LOOP CONTROL FOR 8 BITS
BITS1:	MOV	A,E	;GET NEXT BIT
	RLC		; INTO CARRY
	MOV	E,A	;SAVE REST
	MVI	A,'0'/2	;BUILD ASCII 1 OR 0
	RAL		; CARRY DETERMINES WHICH
	MOV	C,A	;NOW, OUTPUT IT
	CALL	CONOUT
	DJNZ	BITS1	;DO IT AGAIN
;
	POP	D
	RET
;
; EXAMINE REGISTERS COMMAND INSPECTS THE VALUES OF THE
;   THE REGISTERS STORED BY THE LAST ENCOUNTERED BREAKPOINT.
;   THE VALUES MAY BE MODIFIED IF DESIRED.
;
XAA:	INX	H	;SKIP OVER TO NEXT ENTRY
	INX	H
XA:	INR	M	;SEE IF AT END OF TABLE
	RZ		;COULDN'T FIND MATCH, QUIT
	JP	XAB	;SORT OUT BIT 7 OF TABLE
	ORI	80H	;SET IT ON TEST VALUE
	JMPR	XAC
;
XAB:	ANI	7FH	;RESET BIT 7
XAC:	DCR	M	;TO BE PULLED OUT IN ROM
	CMP	M	;SEE IF THIS IS IT
	JRNZ	XAA	;NO, GO TRY AGAIN
;
	CALL	BLK	;YES PREPARE TO SHOW CURRENT VALUE
	CALL	PRTVAL	;GO PRINT THE VALUE
	CALL	DASH	;PROMPT A NEW VALUE
	CALL	PCHK	;GET THE INPUT
	RC		;DONE IF CARRIAGE RETURN
	JRZ	XF	;JUMP IF NO CHANGE DESIRED
;
	PUSH	H	;TO BE CHANGED, SAVE POINTER
	CALL	EXF	;GET THE NEW VALUE
	POP	H	; INTO (H,L)
	MOV	A,L	;GET THE N~W LOW BYTE
	INX	D	;ADJUST POINTER
	STAX	D	;PUT IT DOWN
	XTHL		;RECOVER THE TABLE POINTER
	MOV	A,M	;GET THE ATTRIBUTES
	XTHL		;SET THE STACK STRAIGHT
	RLC		;SEE IF 8 BIT REGISTER
	JRNC	XE	;JUMP IF SO
;
	INX	D	;REGISTER PAIR, DO OTHER 8 BITS
	MOV	A,H
	STAX	D
XE:	POP	H	;RESTORE THE TABLE POINTER
XF:	MOV	A,C	;SEE IF IT WAS A CR
	CPI	CR
	RZ		;DONE IF SO
XMNE:	LXI	H,ACTBL	;GET ADDRESS OF REGISTER LOOK-UP TABLE
XMNE1:	CALL	PCHK	;FIND OUT WHAT ACTION IS WANTED
	JRC	XG	;SHOW ALL IF CARRIAGE RETURN
;
	JRZ	XMNE1	;IGNORE BLANKS OR COMMAS
	CPI	''''	;SEE IF PRIMES WANTED
	JRNZ	XA	;NO, MUST BE SINGLE REGISTER
;
	LXI	H,PRMTB	;YES, SET TABLE ADDRESS
	JMPR	XMNE1	; AND FIND OUT WHICH ONE
;
XG:	MOV	A,M
	MOV	C,A
	INR	A	;SEE IF AT END OF TABLE
	RZ		;DONE IF SO
	CM	CRLF	;START A NEW LINE IF BIT 7 IS SET
	CALL	CONOUT
	CALL	DASH	;PROMPT FOR A NEW VALUE
	CALL	PRTVAL	;GO PRINT THE VALUE
	CALL	BLK	;FORMATTER
	INX	H	;POINT TO NEXT ENTRY
	JMPR	XG	;DO THE NEXT VALUE
;
PRTVAL: INX	H	;POINT TO NEXT ENTRY
	MOV	A,M	;GET OFFSET AND ATTRIBUTES BYTE
	ANI	3FH	;ISOLATE THE OFFSET
	ADI	2	;ALLOW FOR RETURN ADDRESS
	XCHG		;RE-SWAP POINTERS
	MOV	L,A	;BUILD THE ADDRESS OF THE REQ CONTENTS
	MVI	H,0
	DAD	SP
	XCHG		;RE-SWAP THE POINTERS
	MOV	A,M	;NOW FIND OUT ATTRIBUTES
	MVI	B,1	;SET UP FOR SINGLE REG VALUE
	RLC
	JRNC	PV1	;JUMP IF SINGLE REGISTER VALUE WANTED
;
	INR	B	;SET UP FOR REGISTER PAIR
	RLC
	JRNC	PV1	;JUMP IF REGISTER PAIR IS NEXT
;
	PUSH	H	;SPECIAL CASE FOR MEMORY REGISTER
	LDAX	D	;BUILD ADDRESS IN (H,L)
	MOV	H,A
	DCX	D
	LDAX	D
	MOV	L,A
	MOV	A,M	;GET THE MEMORY VALUE
	POP	H	;RESTORE (H,L)
	DJNZ	PV2	;ALWAYS JUMP
;
PV1:	LDAX	D	;GET THE REGISTER CONTENTS
PV2:	CALL	HEX1	;OUTPUT THE VALUE
	DCX	D	;ADJUST THE MEMORY POINTER
	DJNZ	PV1
;
	RET
;
ACTBL:	DB	80H+'A',ALOC
	DB	'B',BLOC
	DB	'C',CLOC
	DB	'D',DLOC
	DB	'E',ELOC
	DB	'F',FLOC
	DB	'H',HLOC
	DB	'L',LLOC
	DB	80H+'M',HLOC+0C0H
	DB	'P',PLOC+80H
	DB	'S',SLOC+80H
	DB	'I',ILOC
;
; REST OF Z 80 REGISTER OFFSETS
;
PRMTB:	DB	80H+'A',APLOC
	DB	'B',BPLOC
	DB	'C',CPLOC
	DB	'D',DPLOC
	DB	'E',EPLOC
	DB	'F',FPLOC
	DB	'H',HPLOC
	DB	'L',LPLOC
	DB	80H+'M',HPLOC+0C0H
	DB	'X',XLOC+80H
	DB	'Y',YLOC+80H
	DB	'R',RLOC
	DB	0FFH
;
; GENERAL PURPOSE ROUTINES
;
;
; ROUTINE CONV CONVERTS THE LOW ORDER NIBBLE OF THE
;       ACCUMULATOR TO ITS ASCII EQUIVALENT. IT
;       PUTS THE RESULT INTO C FOR LATER OUTPUT.
;
CONV:	ANI	0FH	;STRIP OFF BITS 4 7
	ADI	90H	;PUT ON THE ASCII ZONE
	DAA
	ACI 	40H
	DAA
	MOV	C,A	;PUT IN OUTPUT PASS REGISTER
	RET
;
; ROUTINE ECHO READS A BYTE FROM A HALF DUPLEX CONSOLE
;	DEVICE THEN ECHOES THE CHARACTER BACK TO THE
;	CONS0LE.
;
DECHO:	CALL	DASH	;PRINT A DASH
ECHO:	CALL	CONI	;CONSOLE READ, WRITE ROUTINE
ECH1:	PUSH	B	;  SAVE (B,C)
	MOV	C,A	;  PASS CHARACTER IN C REGISTER
	CALL	CONOUT	;  OUTPUT IT
	MOV	A,C	;  PUT CHARACTER BACK INTO A
	POP	B	;  RESTORE (B,C)
	RET
;
; ROUTINE EXPR3 GETS THREE PARAMETERS, DOES A CR LF AND
;     THEN LOADS (B,C), (D,E), AND (H,L) WITH THE PARAMETERS.
EXPR3:	INR	B	;2 IS ALREADY IN THE B REGISTER
	CALL	EXPR	;GET THE PARAMETERS
	POP	B	;PUT PARAMETERS INTO REGISTERS
	POP	D
	JMP	CRLFA	;GO DO THE CARRIAGE RETURN SEQUENCE
;
; ROUTINE HILO INCREMENTS (H,L) IT THEN CHECKS FOR (AND
;	DISALLOWS) A WRAP AROUND SITUATION. IF IT OCCURS,
;	THE CARRY BIT WILL BE SET ON RETURN. IF NO WRAP- 
;	AROUND OCCURRED (H,L)IS COMPARED TO (D,E) AND
;	THE FLAG BITS SET ACCORDINGLY.
HILO:	INX	H	;INCREMENT (H,L)
	MOV	A,H	;TEST IF ZERO
	ORA	L	;  IN (H,L)
	STC		;SET CARRY FOR (H,L)=0
	RZ		;RETURN TF (H,L)=0
	MOV	A,E	;COMPARE(H,L) TO (D,E)
	SUB	L
	MOV	A,D
	SBB	H
	RET		;RETURN WITH FLAGS SET
;
; ROUTINE HILOX INCREMENTS (H,L), COMPARES IT TO (D,E) AND
;   IF EQUAL RETURNS CONTROL TO THE MONITOR EXECUThVE.
;   OTHERWISE, CONTROL RETURNS TO THE CALLING ROUTINE.
;
HILOD:	POP	D	;GET RID OF RETURN ADDRESS
	RET		;RETURN TO MONITOR
HILOXB:	INX	B	;INCREMENT (B,C)
HILOX:	CALL	HILO	;INC AND CHECK (H,L)
	JRC	HILOD	;DONE IF CARRY SET
;
	CALL	CONST	;SEE IF CONSOLE BREAK PENDING
	ORA	A
	RZ		;NONE RETURN TO CONTINUE
	CALL	CONI	;SEE IF WAIT OR BREAK
	CPI	CTRLS
	JRNZ	HILOD	;JUMP IF BREAK
;
	JMP	CONI	;GO WAIT FOR NEXT CHARACTER
;
; ROUTINE NIBBLE CONVERTS THE ASCII CHARACTERS 0-9 AND
;	A-F TO THEIR EQUIVALENT HEXADECIMAL VALUE. IF
;	THE CHARACTER IS NOT IN RANGE, THE CARRY BIT IS SET TO
;	FLAG THE ERROR.
NIBBLE:	SUI	'0'	;ASCII TO HEX CONVERSION
	RC		;  DONE IF OUT OF RANGE
	CPI	'G'-'0' ;CHECK UPPER END
	CMC		;  TOGGLE THE CARRY BIT
	RC		;  DONE IF OUT OF RANGE
	CPI	'9'-'0'+1  ;SEE IF NUMERIC
	CMC		;  TOGGLE THE CARRY BIT
	RNC		;  DONE IF SO
	SUI	'A'-'9'-1	;SUBTRACT THE ALPHA BIAS
	CPI	10	;	SET CARRY FOR INVALID CHAR
	RET
;
; ROUTINE PCHK READS A CHARACTER FROM THE CONSOLE, THEN
;	CHECKS IT FOR A DELIMITER. IF IT IS NOT
;	A DELIMTTER A NON ZERO CONDITION IS RETURNED.
;	IF IT IS A DELIMITER, A ZERO CONDITION IS RETURNED.
;	FURTHER TF THE DELIMITER IS A CARRIAGE RETURN
;	THE CARRY BIT IS SET. A BLANK OR A COMMA RESET'S
;	THE CARRY BIT.
;
PCHK:	CALL	ECHO	;GET, TEST FOR DELIMITER
P2C:	CPI	' '	;  BLANK?
	RZ		;  YES, DONE
	CPI	','	;  NO COMMA?
	RZ		;  YES, DONE
	CPI	CR	;  NO CARRIAGE RETURN?
	STC		;  SHOW IT IN CARRY BIT
	RZ		;  DONE IF CR
	CMC		; CLEAR CARRY FOR NO DELIMITER
	RET
;
; ROUTINE REST TRAPS ALL OF THE REGISTER CONTENTS WHENEVER A
;	RESTART 1 INSTRUCTION IS EXECUTED. THE TRAPPED CONTEN
;	ARE STORED IN THE SYSTEM STACK AREA FOR LATER ACCESS AND
;	USE BY THE GOTO AND THE EXAMINE REGISTERS COMMANDS.
;
; INSERT INTERRUPT DISABLER SOFTWARE AT START OF REST:
REST:	PUSH	H	;SAVE ALL THE REGISTERS
	PUSH	D
	PUSH	B
	PUSH	PSW
	CALL	MEMSIZ	;GET THE MONITOR'S STACK LOCATION
	XCHG
	LXI	H,10	;GO UP 10 BYTES IN THE STACK
	DAD	SP	; TO SKIP OVER TEMP REGISTER SAVE
	MVI	B,4	;PICK OFF THE REGISTER VALUES
	XCHG
RS1:	DCX	H
	MOV	M,D	;SAVE IN WORK AREA
	DCX	H
	MOV	M,E
	POP	D
	DJNZ	RS1
;
	POP	B	;GET THE BREAKPOINT LOCATION
	DCX	B
	SPHL		;SET THE MONITOR STACK
	LXI	H,TLOCX	;SET UP TO RESTORE BREAKPOINTS
	DAD	SP
	PUSH	D
	MVI	D,NBKPTS ;LOOP CONTROL F0R N BREAKPOINTS
RS2:	MOV	A,M
	SUB	C	;SEE IF A SOFTWARE TRAP
	INX	H
	MOV	A,M
	SBB	B	;MAYBE, TRY REST OF ADDRESS
	JRZ	RS5	;FOUND ONE, JUMP TO RESET IT
;
RS3:	INX	H	;NOT FOUND, TRY NEXT ONE
	INX	H
	DCR	D
	JRNZ	RS2
;
RS4:	INX	B	;NONE FOUND
RS5:	LXI	H,LLOCX
	POP	D
	DAD	SP
	MOV	M,E	;STORE USER (H,L)
	INX	H
	MOV	M,D
	PUSH	B	;SAVE (B,C)
	MVI	C,'*'	;TYPE THE BREAK INDICATION
	CALL	CONOUT
	POP	D	;REGET THE BREAKPOINT LOCATION
	MVI	A,RS9/256
	CMP	D	;SEE IF A RET BREAKPOINT
	JRZ	RS6	
;
	INX	H
	INX	H
	MOV	M,E	;RESTORE USER PROGRAM COUNTER
	INX	H
	MOV	M,D
	XCHG		;PRINT THE BREAKPOINT LOCATION
	CALL	LADR 
RS6:	LXI	H,TLOCX
	DAD	SP
	LXI	B,NBKPTS*256
RS7:	MOV	E,M	;RESTORE BREAKPOINTED LOCATIONS
	MOV	M,C	;RESET SYSTEM BP SAVE AREA
	INX	H
	MOV	D,M
	MOV	M,C
	INX	H
	MOV	A,E
	ORA	D
	JRZ	RS8	;DO NOTHING IF ZERO
;
	MOV	A,M
	STAX	D
RS8:	INX	H	;SAME THING FOR OTHER
	DJNZ	RS7	; BREAKPOINT
;
	EXAF		;NOW SAVE THE Z-80 UNIQUES
	EXX
;
	PUSH	H
	PUSH	D
	PUSH	B
	PUSH	PSW
	PUSHIX
;
	PUSHIY
;
	LDAI
;
	MOV	B,A
	LDAR
;
	MOV	C,A
	PUSH	B
	JMP	WINITA	;RETURN TO MONITOR
RS9:	PUSH	H	;RET BREAKPOINT ENCOUNTERED, ADJUST TH
	RST	1	;DO THE BREAKPOINT
EXIT:	POP	B
	MOV	A,C
	STAR	
;
	MOV	A,B
	STAI
;
	POPIX
;
	POPIY
;
	POP	PSW
	POP	B
	POP	D
	POP	H
	EXAF
;
	EXX
;
	POP	D
	POP	B
	POP	PSW
	POP	H
	SPHL
	DB	0	;PLACE FOR EI
	LXI	H,0
	JMP	0
ENDX:	EQU	$
;
;ERROR HANDLERS
;
;	THREE TYPES OF ERRORS ARE DETECTED:	A RESTART
;	ERROR AN I/O ASSIGNMENT ERROR AND CERTAIN PROGRAM
;	ERRORS (DETERMINED BY THE PARTCULAR ROUTINE WHERE
;	THE ERROR CONDITION WAS ENCOUNTERED.) EACH CAUSES
;	A UNIQUE MESSAGE TO BE PRINTED, THEN DOES A WARM
;	INITIALIZATION OF THE MONITOR. THE I/O ERROR
;	CAUSES THE I/O ASSIGNMENTS TO BE RESET TO DEFAULT ASSI
IOER:	XRA	A	;SET IOBYTE TO DEFAULT VALUE
	STA	IOBYTE
	LXI	H,IOMSG	;GET ADDRESS OF I/O ERROR MSG
	JMP	COMERR	;GO PROCESS IT
IOMSG: 	DB	'I/O ER','R'+80H
;
; BYTE ROUTINE READS TWO ASCII CHARACTERS FROM THE
;	CURRENT PAPER TAPE READER AND ASSEMBLES THEM INTO TWO
;	HEXADECIMAL BYTES OF DATA. IT UPDATES A CHECKSUM
;	ACCUMULATED IN REGISTER D.
BYTE:	CALL	BYT	;GET NEXT BYTE
	ORA	B	;COMBINE THEM
	MOV	B,A
	ADD	D	;UPDATE CHECKSUM
	MOV	D,A	
	MOV	A,B	;RESTORE BYTE
	RET
;
PEOL:	MVI	C,CR
	CALL	PO
	MVI	C,LF
	JMP	PO	;GO PUNCH THE OUTPUT
;
; RIX ROUTINE READS ONE CHARACTER FROM THE CURRENT
;	PAPER TAPE READER AND STRIPS OFF THE PARITY BIT.
RIX:	CALL	RI
	ANI	7FH
	RET
QMSG:	DB	'???','?'+80H
LOGMSG:	DB	'MOSS VERS 2.2'
	DB	CR,LF+80H
;
; INITIALIZATION CODE FOR THE 8250 ASYNCHRONOUS COMMUNICATION
;  ELEMENT. THIS CODE WILL INITIALIZE THE BAUD RATE OF THE
;  8250, AS WELL AS THE WORD FORMAT. 8 DATA BTTS I STOP BIT
;  AND NO PARITY ARE SELECTED. EITHER 2 OR 3 CARIAGE RETURNS
;  MUST BE ENTERED TO ESTABLISH THE CORRECT BAUD RATE.
I8250:	MVI	A,0FH	;SET UP THE 8250
	OUT	SMDMCT
	LXI	D,40H	;SET UP TO TIME THE START BIT
	MOV	H,D
	MOV	L,D	;ZEROES TO (H,L)
I8250A:	IN	SMDMST	;WAIT FOR START BIT
	ANA	E
	JRZ	I8250A
;
I8250B:	IN	SMDMST	;NOW, TIME THE START BIT DURATION
	INX	H
	ANA	E
	ANA	E
	JNZ	I8250B
	PUSH	H	;SAVE COUNT IN CASE OF 4 MHZ
	DAD	H	;PREPARE THE 2 MHZ DIVISOR
	MOV	E,H	;SET UP THE FUDGE FACTOR
	DAD	D	;APPLY THE FUDGE FACTOR
	DAD	D
	PUSH	H	;SAVE FOR LATER USE
	DAD	H	;WAIT FOR 8 BIT TIMES
	DAD	H
I8250C:	IN	SDATA	;WASTE SOME TIME
	DCX	H
	MOV	A,L
	ORA	H
	JNZ	I8250C
	POP	H	;REGET 2 MHZ DIVISOR
I8250D:	MVI	A,83H	;SET DIVISOR REGISTER ACCESS
	OUT	SLCTRL
	MOV	A,L	;SET THE DIVISOR
	OUT	SDATA
	MOV	A,H
	OUT	SINTEN
	MVI	A,3	;SET DATA REGISTER ACCESS
	OUT	SLCTRL
	XRA	A	;DISABLE INTERRUPTS
	OUT	SINTEN
	OUT	SLSTAT	;AND RESET ERROR FLAGS
	CALL	TTYIN	;GET A CHARACTER
	ANI	7FH	;STRIP OFF ANY PARITY BIT
	CPI	0DH	;SEE IF IT IS A CARRIAGE RETURN
	POP	H	;SET THE STACK STRAIGHT
	RZ		;DONE IF CARRIAGE RETURN RECEIVED
	MOV	E,L	;ELSE, MUST BE 4 MHZ SYSTEM
	MOV	D,H	; SO, COUNT=COUNT*5/4
	CALL	DIV2
	CALL	DIV2
	DAD	D
	PUSH	H
	JMPR	I8250D	;GO SET THE NEW DIVISOR
;
;
;
DIV2:	ORA	A	;CLEAR THE CARRY BIT
	MOV	A,H	;DO A 16 BIT RIGHT SHIFT
	RAR
	MOV	H,A
	MOV	A,L
	RAR
	MOV	L,A
	RET
;
; EOF ROUTINE PUNCHES AN END OF FILE RECORD (INTEL HEX
;   FORMAT) ONTO THE CURRENTLY ASSIGNED PAPER TAPE PUNCH
;   DEVICE. AN ENTRY POINT ADDRESS FOR THE FILE WILL ALSO
;   BE PUNCHED, IF SPECIFIED.
;
EOF:	CALL	EXLF	;GET JUMP ADDRESS
	PUSH	D	;SAVE THE # OF TRAILER NULLS
EOFA:	CALL	PSOR	;PUNCH START OF RECORD
	XRA	A	;ZERO OUT THE CHECKSUM
	MOV	D,A
	CALL	PBADR	;OUTPUT THE RECORD LENGTH AND EP
	MVI	A,1	;PUNCH RECORD TYPE = 1
	CALL	PBYTE
	XRA	A
	SUB	D	;OUTPUT THE CHECKSUM
	CALL	PBYTE
	JMPR	LEO	;GO DO THE TRAILER
;
;
; LEADER ROUTINE 'PUNCHES' SIX INCHES (OR AS SPECIFIED)
;   OF LEADER ON THE PAPER TAPE PUNCH. NULLS ARE PUNCHED
;   TO FORM THE LEADER (OR TRAILER).
;
LEADER:	CALL	EXPR1	;SEE IF SOME OTHER LENGTH WANTED
LEO:	POP	B	;GET THE VALUE
	MOV	A,B
	ORA	C	;TEST FOR DEFAULT SELECT
	MOV	B,C	;MOVE NEW VALUE IN JUST IN CASE
	MVI	C,0	;GET A NULL CHARACTER
	JRNZ	LE1	;JUMP IF NEW VALUE WANTED
;
	MVI	B,60	;DEFAULT SET 60 NULLS
LE1:	CALL	PUNCH	;PUNCH ONE NULL
	DJNZ	LE1	;KEEP GOING TIL DONE
;
	RET
;
; QUERY ROUTINE WILL TELL THE OPERATOR WHAT HIS CURRENT LOGICAL
;	PHYSICAL PERIPHERAL DEVICE ASSIGNMENTS ARE. NO PARAME
;	(OTHER THAN A CARRIAGE RETURN) ARE REQUIRED ON ENTRY.
QUERY:	LDA	IOBYTE	;GET THE ASSIGNMENT CONTROL BYTE
	MVI	B,4	;SET UP FOR FOUR LOGICAL DEVICES
	LXI	H,ACT	;ADDRESS OF CONVERSION TABLE
	LXI	D,ALT-APT ;NEGATIVE OFFSET FOR LOGICAL TABLE
QUE1:	PUSH	PSW
	CALL	BLK	;FORMAT THE PRINT OUT
	MOV	C,M	;GET THE CURRENT LOGICAL DEVICE CODE
	CALL	CONOUT	;OUTPUT IT
	CALL	DASH	;OUTPUT A DASH
	POP	PSW	;REGET THE CONTROL BYTE
	PUSH	PSW	;RESAVE IT
	PUSH	H	;SAVE THE TABLE POINTER
QUE2:	INX	H	;ADJUST POINTER TO CURRENT PHYSICAL DE
	INR	A
	ANI	3	;BITS 0 AND 1 ARE 0 WHEN ON CURRENT AS
	JRNZ	QUE2	;NOT THERE YET, TRY AGAIN
;
	MOV	C,M	;FOUND IT, NOW PRINT IT
	CALL	CONOUT	
	POP	H
	POP	PSW	;GO TO NEXT LOGICAL DEVICE
	RAR		;ADJUST THE IOBYTE
	RAR		
	DAD	D	;ADJUST THE TABLE POINTER
	DJNZ	QUE1	;GO DO NEXT LOGICAL DEVICE
;
	RET		;RETURN TO MONITOR
;
; READ ROUTINE READS AN INTEL HEX FORMAT PAPER TAPE FROM
;   THE CURRENT PAPER TAPE READER. IF A NON ZERO ADDRESS
;   IS SPECIFIED IN THE END OF FILE RECORD CONTROL WILL
;   BE TRANSFERRED TO THAT ADDRESS. OTHERWISE, CONTROL
;   WILL REVERT TO THE EXECUTIVE.
;
READ:	CALL	EXPR1	;GET OFFSET BIAS
REDO:	POP	H	; INTO (H,L)
	PUSH	H	;SAVE THE BIAS
RED1:	CALL	RIX	;READ A BYTE
	SBI	':'	;LOOK FOR START OF RECORD
	JRNZ	RED1	;JUMP TO KEEP LOOKING
;
	MOV	D,A	;INITIALIZE CHECKSUM
	CALL	BYTE	;GET RECORD LENGTH
	JRZ	RED3	;JUMP IF EOF RECORD
;
	MOV	E,A	;ELSE, ASSUME DATA RECORD
	CALL	BYTE	;GET LOAD ADDRESS HIGH BYTE
	PUSH	PSW	;SAVE IT
	CALL	BYTE	;GET LOAD ADDRESS LOW BYTE
	POP	B	;BUILD ADDRESS IN (B,C)
	MOV	C,A	
	DAD	B	;ADD ON THE BIAS
	CALL	BYTE	;SKIP OVER RECORD TYPE
RED2:	CALL	BYTE	;GET A DATA BYTE
	MOV	M,A	;PUT IT INTO MEMORY
	CMA		;D0 A QUICK CHECK
	XRA	M	; RESULT SHOULD BE ZERO
	CNZ	BITS	;IF ERROR PRINT ADDRESS AND DATA
	INX	H	;INCREMENT MEMORY POINTER
	DCR	E	;RECORD LENGTH FOR LOOP CONTROL
	JRNZ	RED2	;DO REST OF THE RECORD
;
	CALL	BYTE	;GET THE CHECKSUM
	JNZ	QPRT	;ABORT IF ERROR
	JMPR	REDO	;GO DO NEXT RECORD
;
RED3:	CALL	BYTE	;EOF RECORD GET ENTRY POINT
	MOV	H,A	;HIGH BYTE TO (H)
	CALL	BYTE	;GET THE LOW BYTE
	MOV	L,A
	ORA	H	;SEE IF IT IS ZERO
	POP	D	;RESTORE THE STACK
	RZ		;RETURN TO MONITOR IF EP=0
	PCHL		;ELSE, GO TO THE ENTRY POINT
;
; WRITE ROUTINE IS USED TO PUNCH AN INTEL HEX FORMAT
;   PAPER TAPE ON THE CURRENT ASSIGNED PUNCH UNIT.
;
WRITE:	CALL	EXPR3	;GET 3 PARAMETERS, DO CRLF
	XRA	A	;SEE IF RECORD LENGTH CHANGE
	MOV	B,A	;SET HIGH BYTE TO ZERO
	ORA	C	;NOW SEE IF CHANGE WANTED
	JRNZ	WRI1	;YES, JUMP AND SET IT UP
;
	MVI	C,16	;NO, DEFAULT TO 16 BYTES/RECORD
WRI1:	PUSH	H	;SAVE MEMORY POINTER
	DAD	B	;ADD THE RECORD LENGTH
	ORA	A	;CLEAR THE CARRY BIT
	DSBC	D	;SEE IF FULL RECORD REMAINS
;
	POP	H	;RESTORE (H,L)
	JRC	WRI2	;GO DO A FULL RECORD
	PUSH	D	;SAVE LAST BYTE ADDRESS
	XCHG		;SWAP (D,E) AND(H,L)
	ORA	A	;RESET THE CARRY BIT
	DSBC	D	;FIND # OF BYTE REMAINING
;
	INX	H	;ADJUST TO INCLUDE LAST BYTE
	XTHL		;SWAP TOP OF STACK
	XCHG		;SET (D,E) (H,L) TO NORMAL
	POP	B	;NEW RECORD LENGTH TO (B,C)
	RC		;DONE IF ZERO LENGTH RECORD
WRI2:	PUSH	B	;SAVE LOOP COUNT
	PUSH	D	
	MOV	D,B	;ZERO THE CHECKSUM
	MOV	B,C	;MOVE LOOP CONTROL TO B
	CALL	PSOR	;PUNCH START OF RECORD
	MOV	A,B	;GET RECORD LENGTH
	CALL	PBADR	;PUNCH IT
	XRA	A	;PUNCH RECORD TYPE '0'
	CALL	PBYTE
WRI3:	MOV	A,M	;GET NEXT DATA BYTE
	INX	H	;BUMP THE POINTER
	CALL	PBYTE	;PUNCH THE DATA
	DJNZ	WRI3	;DO REST OF RECORD
;
	XRA	A	;NOW, DO THE CHECKSUM
	SUB	D	
	CALL	PBYTE	;PUNCH IT
	POP	D	;RESTORE THE REGISTERS
	POP	B
	JMPR	WRI1	;GO DO NEXT RECORD
;
PSOR:	CALL	PEOL
	MVI	C,':'
	JMP	PO
;
; HEXN ROUTINE
;
;THIS ROUTINE ADDS AND SUBTRACTS TWO HEXADECIMAL 16 BIT
;	UNSIGNED NUMBERS AND DISPLAYS THE RESULTS ON THE
;	CONSOLE.
HEXN:	CALL	EXLF	;GET THE TWO NUMBERS
	PUSH	H	;SAVE IT FOR THE SUBTRACT
	DAD	D	;ADD THEM
	CALL	LADRB	;OUTPUT THEM
	POP	H	;REGET THE FIRST NUMBER
	ORA	A	;CLEAR THE CARRY BIT
	DSBC	D	;DO THE SUBTRACT
;
	JMPR	LADR	;GO OUTPUT THE RESULT
;
;
; ROUTINE LADR PRINTS THE CONTENTS OF (H,L) ON THE
;	CURRENT CONSOLE EITHER AT THE SART OF A NEW
;	LINE (EP = LADRA) OH AT THE CURRENT LOCATION (EP =
;	LADR).
LADRA:	CALL	CRLF	;START A NEW LINE
LADR:	MOV	A,H	;GET HIGH TWO DIGITS
	CALL	HEX1	;PRNT THEM
	MOV	A,L	;GET LOW TWO DIGITS
HEX1:	PUSH	PSW	;SAVE THE LOW DIGIT
	RRC		;PUT HIGH NIBBLE INTO BITS 0-3
	RRC
	RRC
	RRC
	CALL	HEX2	;GO PRINT SINGLE DIGIT
	POP	PSW	;REGET THE LOW DIGIT
HEX2:	CALL	CONV	;GO INSERT ASCII ZONE
	JMPR	CO	;DO THE CHARACTER OUTPUT
;
;
; ROUTINE DASH TYPES A DASH ON THE CURRENT CONSOLE DEVICE.
;
DASH1:	CALL	HEX1	;FIRST, PRINT ACCUM AS TWO HEX DIGITS
DASH:	MVI	C,'-'	;GET AN ASCII DASH
	JMPR	CO	;GO TYPE IT
;
;
;  IOBYTE HANDLERS
;
	ORG	MOSS + 05FBH
LADRB:	CALL	LADRA	;OUTPUT (H,L) AS 4 ASCII DIGITS
BLK:	MVI	C,' '	;OUTPUT A BLANK
CO:	LDA	IOBYTE
	ANI	3	;ISOLATE CONSOLE ASGT
	JZ	TTYOUT	;TTY DEVICE ACTIVE
	CPI	2
	JM	CRTOUT	;CRT ACTIVE
	JNZ	CUSO1	;USER CONSOLE 1 ACTIVE
LO:	LDA	IOBYTE
	ANI	0C0H	;ISOLATE LIST ASGT
	JZ	TTYOUT	;TTY DEVICE ACTIVE
	CPI	80H
	JM	CRTOUT	;CRT ACTIVE
	JZ	LPRT	;LINE PRINTER ACTIVE
	JMP	LUSE1	;USER PRINTER 1 ACTIVE
;
CSTS:	LDA	IOBYTE
	ANI	3	;ISOLATE CONSOLE ASGT
	JZ	TTST	;TTY ACTIVE
	CPI	2
	JM	CRTST	;CRT ACTIVE
	JNZ	CUST1	;USER CONSOLE 1 ACTIVE
;
BATST:	LDA	IOBYTE
	ANI	0CH	;ISOLATE BATCH ASGT
	JZ	TTST	;TTY ACTIVE
	CPI	8
	JM	PTRST	;PAPER TAPE READER ACTIVE
	JZ	RUST1	;USER READER 1 ACTIVE
	JMP	RUST2	;USER READER 2 ACTIVE
;
CI:	LDA	IOBYTE
	ANI	3	;ISOLATE CONSOLE ASGT
	JZ	TTYIN	;TTY DEVICE ACTIVE
	CPI	2
	JM	CRTIN	;CRT ACTIVE
	JNZ	CUSI1	;USER CONSOLE 1 ACTIVE
;
RI:	LDA	IOBYTE
	ANI	0CH	;ISOLATE BATCH ASGT
	JZ	TTYRDR	;TTY ACTIVE
	CPI	8
	JM	PTRIN	;PAPER TAPE READER ACTIVE
	JZ	RUSI1	;USER READER I ACTIVE
	JMP	RUSI2	;USER READER 2 ACTIVE
;
LSTAT:	LDA	IOBYTE
	ANI	0C0H	;ISOLATE THE LIST DEVICE ASSIGNMENT
	JZ	TTOST 
	CPI	80H
	JM	CRTOST
	JZ	LPRST
	JMP	LUST1
;
PO:	LDA	IOBYTE
	ANI	30H	;ISOLATE PUNCH ASGT
	JZ	TTPNCH	;TTY ACTIVE
	CPI	20H
	JM	HSP	;HIGH SPEED PUNCH ACTIVE
	JZ	PUSO1	;USER PUNCH 1 ACTIVE
	JMP	PUSO2	;USER PUNCH 2 ACTIVE
;
; ROUTINE CONI READS THE CONSOLE AND STRIPS OFF THE ASCII
;	PARITY BIT.
;
CONI:	CALL	CI	;GET THE NEXT CHARACTER
	ANI	7FH	;STRIP OFF THE PARITY BIT
RTS:	RET
;
; ROUTINE PRTWD PRINTS AN ASCII STRING ONTO THE CONSOLE.
;	THE STRING MUST BE TERMINATED BY BIT 7 SET IN THE
;	LAST CHARACTER OF THE STRING. THE STRING WILL START
;	A NEW LINE (EP = PRTWD) OR CONTINUE ON THE SAME
;	LINE (EP = PRTWA)
PRTWD:	CALL	CRLF	;START A NEW LINE
PRTWA:	PUSH	B	;SAVE (B,C)
PRTA:	MOV	C,M	;GET NEXT CHARACTER FROM MEMORY
	CALL	CO	;OUTPUT IT
	INX	H	;INCREMENT MEMORY POINTER
	MOV	A,C
	RLC		;TEST FOR BIT 7 DELIMITER
	JRNC	PRTA	;NO DELIMITER, GO DO NEXT CHARACTER
;
PRTB:	POP	B	;RESTORE (B,C)
	RET
;
; ROUTINE EXLF READS TWO PARAMETERS, PUTS THEM INTO THE
;	D,E AND H,L REGISTERS, THEN DOES A CARRIAGE RETURN,
;	LINE FEED SEQUENCE.
;
EXLF:	CALL	EXPR	;GO GET TWO PARAMETERS
	POP 	D
	POP 	H
;
; ROUTINE CRLF GENERATES A CARRIAGE RETURN, LNE FEED
;	SEQUENCE ON THE CURRENT CONSOLE TO START A NEW LINE
;	IT INCLUDES TRHEE NULL CHARACTERS FOR TTY TYPE
;	DEVICES FOR THE HEAD MOVEMENT TIME.
;
CRLF:	PUSH	H	;SAVE THE CONTENTS OF (H,L)
CRLFA:	LXI	H,CRMSG	;ADDRESS OF CR,LF MESSAGE
	CALL	PRTWA	; OUTPUT IT
	POP	H	;RESTORE (H,L)
	RET
;
RSTER:	LXI	H,RSTMSG  ;GET ADDRESS OF RESTART ERROR MSG
COMERR: CALL	PRTWD	;PRINT IT ON NEW LINE
	JMP	WSVEC	;GO TO WARM BOOT
RSTMSG: DB	'RST ER','R'+80H
CRMSG:	DB	CR,LF,0,80H
;
; I/O DRIVERS FOR THE 8250 ASYNC COMM ELEMENT
;
TTST:	IN	SLSTAT	;GET 8250 LINE STATUS
	ANI	1	;SEE IF RECEIVE DATA AVAILABLE
	RZ		;RETURN IF NOT
	ADI	0FEH	;FLAG THAT DATA IS AVAILABLE
	RET
;
TTYIN:	IN	SLSTAT	;GET 8250 LINE STATUS
	RAR		;MOVE RX DATA READY BIT INTO CARRY
	JRNC	TTYIN	;LOOP UNTIL DATA IS IN
;
	IN	SDATA	;READ THE DATA
	RET
;
TTOST:	IN	SLSTAT	;GET 8250 LINE STATUS
	ANI	20H	;ISOLATE TX BUFFER EMPTY BIT
	RZ		;RETURN IF NOT EMPTY
	ADI	0BFH	;FLAG THE EMPTY STATE
	RET
;
TTYOUT: IN	SLSTAT	;GET 8250 LINE STATUS
	ANI	20H	;ISOLATE THRE BIT
	JRZ	TTYOUT	;WAIT UNTIL ONE OF THE REGISTERS EMPTI
;
	MOV	A,C	;MOVE THE DATA OVER			
	OUT	SDATA	;OUTPUT THE DATA
	RET
;
; EQUATES FOR ADDITIONAL CONSOLE DEVICES
CRTIN	EQU	IOER
CRTOUT	EQU	IOER
CRTST	EQU	IOER
CRTOST	EQU	IOER	;UNASSIGNED CRT OUTPUT STATUS
CUSI1	EQU	IOER	;UNASSIGNED USER CONSOLE (INPUT)
CUSO1	EQU	IOER	;UNASSIGNED USER CONSOLE (OUTPUT)
CUST1	EQU	IOER
;
; EQUATES FOR ADDITIONAL PAPER TAPE PUNCH DEVICES
TTPNCH	EQU	TTYOUT	;UNASSIGNED TELETYPE PUNCH
HSP	EQU	IOER	;UNASSIGNED HIGH SPEED PUNCH
HSPST	EQU	IOER	;UNASSIGNED HJGH SPEED PUNCH STATUS
PUSO1	EQU	IOER	;UNASSIGNED USER PUNCH 1
PUSO2	EQU	IOER	;UNASSIGNED USER PUNCH 2
;
; EQUATES FOR ADDITIONAL LIST DEVICES
LPRT	EQU	IOER	;UNASSIGNED LINE PRINTER
LPRST	EQU	IOER	;UNASSIGNED PRINTER STATUS
LUSE1	EQU	IOER	;LIST DEVICE 1
LUST1	EQU	IOER	;LIST DEVICE 1 STATUS
;
; EQUATES FOR ADDITIONAL PAPER TAPE READER DEVICES
TTYRDR	EQU	TTYIN	;UNASSIGNED TELETYPE PAPER TAPE READER
PTRIN	EQU	IOER	;UNASSIGNED HIGH SPEED PAPER TAPE READ
PTRST	EQU	IOER	;UNASSIGNED HS PTR STATUS
RUSI1	EQU	IOER	;UNASSIGNED PAPER TAPE READER 1
RUST1	EQU	IOER	;UNASSIGNED PAPER TAPE READER 1 (STATUS)
RUSI2	EQU	IOER	;UNASSIGNED PAPER TAPE READER 2
RUST2	EQU	IOER	;UNASSIGNED PAPER TAPE READER 2 (STATUS)
;
BYT:	CALL	RIBBLE	;READ AND CONVERT ONE CHARACTER
	RLC		;SHIFT INTO HIGH NIBBLE
	RLC		
	RLC
	RLC
	MOV	B,A	;SAVE IN B TEMPORARILY
RIBBLE:	CALL	RIX	;READ A CHARACTER
	JMP	NIBBLE	;GO CONVERT TO HEX DIGIT
;
; PADR ROUTINE PUNCHES (H,L) AS FOUR ASCII CHARACTERS.
;   IT IS USED TO PUT THE ADDRESS INTO AN INTEL HEX
;   FORMAT RECORD.
;
PBADR:	CALL	PBYTE
PADR:	MOV	A,H
	CALL	PBYTE
	MOV	A,L
;
; PBYTE ROUTINE PUNCHES (A) AS TWO ASCII CHARACTERS ON
;   THE CURRENT PUNCH DEVICE.
;
PBYTE:	PUSH	PSW	;SAVE THE BYTE
	RRC		;DO HIGH NIBBLE FIRST	
	RRC		
	RRC
	RRC
	CALL	CONV	;CONVERT TO ASCII
	CALL	PUNCH	;PUNCH IT
	POP	PSW	;GET LOW NIBBLE
	PUSH	PSW	;RESAVE F0R CHECKSUM
	CALL	CONV	;CONVERT TO ASCII
	CALL	PUNCH	;PUNCH IT
	POP	PSW
	ADD	D	;UPDATE CHECKSUM
	MOV	D,A
	RET
;
	END
