;
; THIS IS A 256 BYTE PROM MONITOR FOR USE WITH THE ALTAIR
; 8800B TURNKEY MODULE. THIS MONITOR PROVIDES THE USER WITH
; THE FOLLOWING FUNCTIONS:
;
; 1) MEMORY EXAMINE AND CHANGE FUNCTION
;	YOU CAN EXAMINE AND CHANGE THE CONTENTS OF ANY
;	VALID MEMORY LOCATION
; 2) MEMORY DUMP FUNCTION
;	YOU CAN DUMP IN THE ALTAIR BINARY PUNCH FORMAT
;	BETWEEN ANY TWO VALID MEMORY LOCATIONS
; 3) JUMP TO FUNCTION
;	YOU CAN CAUSE THE MONITOR TO JUMP TO ANY
;	LOCATION AND START EXECUTING THE PROGRAMS THERE
;
; THE MONITOR CAN BE REENTERED FROM THE USERS PROGRAM
; SO THAT THE FEATURES OF THE MONITOR ARE ALWAYS AVAILABLE
; TO ANY USER PROGRAM.
;
; MITS TURNKEY MONITOR
; C.W. VERTREES		01/13/1977
; REVISED		01/17/1977
;			01/19/1977
;			01/20/1977
;
; Andrew Kessel
; Modified for MITS 88-PMC 2K PROM board - Mon88PMC
; "Mon88PMC" is a modified 1702A Turnkey Monitor ("TurMon") EPROM, to 
; be used in any Altair 8800 series computer.
;
; As with the TurMon, there are three commands:
;  M - Memory examine and change
;  D - Binary memory dump
;  J - Jump to user program and run - ie, run a user program at
; memory location 000000, or execute a bootstrap program 
; located in EPROM.
;
; The chip helps when troubleshooting your Altair. You can load and 
; execute programs from a key board, deposit and examine memory, etc. 
; It allows you to do everything the TurMon Turnkey Monitor PROM can 
; do, but unlike the TurMon chip that can only be installed in the 
; Altair 88-TKY Turnkey board, the Mon88PMC can also be installed in 
; the MITS 88-PMC 2K PROM board. Therefore, it will run in the Altair 
; 8800/8800A. The standard TurMon chip only works with the 8800b 
; because it only works with the 88-TKY Turnkey board*, and the TKY 
; board only works in the 8800b.
;
; In addition to your regular RAM installed beginning at address 
; 000000, you will need to have stack memory for the Mon88PMC installed 
; at 56K (160000/E000)** to 60K (170000/F000). There can be a gap 
; between the top of regular memory and the stack memory. Mon88PMC 
; executes from the same memory location as the TurMon (176400/FD00). 
; You'll have to be sure your PMC board is properly addressed (it 
; probably is already). You'll need the TurMon instruction manual, too.
;
; The TurMon chip's code was modified to move its stack to a new 
; location at 60K (170000/F000). There were only two changes to the 
; TurMon's code:
;
; Address 000012/000A TurMon data= 374/FC Mon88PMC data= 360/F0
; Address 000017/000F TurMon data= 056/2E Mon88PMC data= 043/23
;
; Steve Piette		06/19/2010
; Disassembled based on listing in MITS Turnkey PROM Monitor Manual Aug 1977
; Note the original author uses a trick to save a few bytes in the loops by 
; using throw away instructions that effectivly NOP the first instruction of
; the loop on the first pass by adding bytes in front to modify the instruction.
;
; The TURMON manual says the code only uses 8 bytes of the stack.
; The documentation says the code is at 176400/FD00
; and the top of stack at 175000/FA00 but the code puts it at 176000/FC00

ACIACR	EQU	10H		; SIO STATUS AND COMMAND REGISTER
ACIADR	EQU	ACIACR+1	; SIO DATA REGISTER
SIORST	EQU	03H		; ACIA MASTER RESET
SIOINIT	EQU	11H		; /16, 8 BITS, NO PARITY, 2 STOP, NO INTS

LEADCH	EQU	0DH		; PUNCH LEADER CHAR (MANUAL SAYS 302Q)
LEADCNT	EQU	3CH		; PUNCH LEADER COUNT
SOFB	EQU	3CH		; PUNCH "START OF BLOCK" CHAR

PROMPT	EQU	'#'		; COMMAND PROMPT (TURMON WAS '.')

STACK	EQU	0F000H		; ON TURNKEY BOARD IT WAS 0FC00H

TURMON	ORG	0FD00H

MON:	MVI	A,SIORST	; RESET ACIA
	OUT	ACIACR		; AND INITIALIZE
	MVI	A,SIOINIT	; /16, 8 BITS, NO PARITY, 2 STOP, NO INTS
	OUT	ACIACR

ENTER:	LXI	SP,STACK	; LOAD STACK POINTER
	CALL	CRLF		; FORMAT OUTPUT
	MVI	A,PROMPT	; HELLO MONITOR
	CALL	OUTCHK
	CALL	INCH		; WHAT TO DO

	CPI	'M'
	JZ	MEM		; DO MEMORY EXAMINE

	CPI	'D'
	CZ	DMP		; DO A MEMORY DUMP

	CPI	'J'
	JNZ	ENTER		; NOT A VALID CMD

	CALL	OCTL6		; DO A JUMP, GET ADDRESS
	PCHL			; LOAD PC AND GO

; THIS CONTROL STRUCTURE HANDLES THE MEMORY
; EXAMINE AND CHANGE FUNCTION
;
MEM:	CALL	OCTL6		; GET ADDRESS

	DB	3EH		; "MVI A," SKIP NEXT (BOMB A)
CONT:	INX	H		; INCREMENT ADDRESS
	CALL	CRLF		; NEW LINE

	MOV	D,H		; STORE ADDRESS IN D/E
	MOV	E,L
	CALL	PRINT6		; PRINT ADDRESS

	LDAX	D		; LOAD DATA
	MOV	H,A
	CALL	PRINT3		; PRINT DATA BYTE

	CALL	OCTL3		; GET NEW DATA

	XCHG			; RESTORE ADDRESS
	JC	CONT		; NO NEW DATA
	MOV	M,A		; STORE DATA
	CMP	M		; COMPAREDEPOSIT
	JZ	CONT		; OK DO NEXT

ERR:	MVI	A,'?'		; FLAG BAD DEPOSIT
	CALL	OUTCHK		; PRINT '?'

	JMP	ENTER		; RETURN TO MONITOR
; ERROR CONDITIONS RETURN TO MONITOR VIA "ERR"

; THIS CONTROL STRUCTURE RUNS THE MEMORY DUMP FUNCTION
;
; DUMP BLOCK FORMAT (ONLY DO BEGIN AND PROGRAM RECORDS)
; BYTE	CONTENTS		DESCRIPTION
;
; BEGIN/NAME RECORD
;   1	125Q			BEGIN SYNC
; 2-4	NAME			PROGRAM NAME
; 5-N	COMMENTS		PROGRAM VERSION AND DATA, ETC.
; N+1	015Q			TERMINATES PROGRAM NAME RECORD
;
; PROGRAM LOAD RECORD
;   1	074Q			LOAD SYNC BYTE
;   2	0-377Q			BLOCK LENGTH
;   3	L.S. BYTE		LSB OF LOAD ADDRESS
;   3	M.S. BYTE		MSB OF LOAD ADDRESS
; 5-N	DATA BYTES
; N+1	CHECKSUM		8 BIT ADD OF ALL BYTES 3 TO N
;
; END-OF-FILE RECORD
;   1	170Q			PAPER TAPE/AUDIO CASSETTE EOF
; 2-3				EXECUTION START ADDRESS
;
DMP:	CALL	OCTL6		; GET START

	XCHG			; STORE IN D/E
	CNC	SPACE

	CALL	OCTL6		; GET END

	MVI	A,LEADCH	; LOAD LEADER CHAR
X1:	MVI	B,LEADCNT	; LOAD LEADER CNTR

X2:	CALL	OUTCHK		; PUNCH LEADER

	DCR	B		; DECREMENT COUNT
	JNZ	X2

	CMP	B		; DONE WITH LEADER, NOW
	MOV	A,B		; A = 0
	JNZ	X1		; PUNCH NULLS

	MOV	A,L		; SUB START FROM END
	SUB	E
	MOV	L,A
	MOV	A,H
	SBB	D
	MOV	H,A		; HL CONTAINS TOT BYTES
	INX	H		; INCREMENT TOT BYTES

BLOCK:	DCR	B		; B=FFH
	MOV	A,H
	ORA	A		; MORE THAN ONE BLOCK?
	JNZ	NOTLST		; NOT LAST BLOCK

	MOV	B,L		; LAST BLOCK LENGTH

NOTLST:	MVI	A,SOFB		; A=3CH
	CALL	OUTCHK		; PUNCH "START OF BLOCK"

	MOV	A,B		; B=BYTE CNTR
	CALL	OUTCHK		; PUNCH BYTE COUNT

	MVI	C,0		; CLEAR CHECKSUM

; "PUNCH" PROGRAM RECORDS
	MOV	A,E		; PUNCH LOAD ADDR
	CALL	OUTCHK		; L.S. BYTE
	MOV	A,D
	CALL	OUTCHK		; M.S. BYTE

DATA:	LDAX	D		; GET DATA BYTE
	CALL	OUTCHK		; PUNCH IT
	INX	D		; INCREMENT ADDR
	DCX	H		; TOTBYTES=TOTBYTES-1
	DCR	B		; DONE W/BLOCK?
	JNZ	DATA		; NO

	MOV	A,C		; YES, PUNCH CHECKSUM
	CALL	OUTCHK

	MOV	A,H		; DONE W/ALL BYTES?
	ORA	L
	JNZ	BLOCK		; NO, PUNCH NEXT BLOCK

CRLF:	MVI	A,0DH		; DO A CRLF AND RETURN TO MONITOR
	CALL	OUTCHK
	MVI	A,0AH
	JMP	OUTCHK
; RETURN TO MONITOR THROUGH OUTCHK

; THIS SUBROUTINE BUILDS 3/6 OCTAL DIGITS IN H AND L
;
; SPECIAL RETURN PROVIDED BY A "SPACE", CARRY BIT SET.
; ONLY VALID OCTAL OR "SPACE" ACCEPTED. ALL OTHERS FLAGGED AND
; CONTROL RETURNS TO MONITOR
;
OCTL6:	DB	6		; LOAD B WITH A 6, SKIP NEXT
OCTL3:	DB	6		; LOAD B WITH 3
	DB	3
	LXI	H,0		; CLEAR H/L FOR LESS THAN 6 DIG RET

AGN:	CALL	INCH		; GET CHARACTER
	MOV	C,A		; STORE IN C
	CPI	' '		; COMPARE TO "SPACE"

	STC			; SET THE CARRY
	RZ			; RETURN IF "SPACE"
	ANI	0B8H		; TEST FOR VALID OCTAL
	XRI	30H
	JNZ	ERR		; BAD, FLAG & RET TO MON

	MOV	A,C		; RESTORE CHAR
	ANI	7		; STRIP OFF ASCII

	DAD	H		; SHIFT H/L LEFT 3 BITS
	DAD	H
	DAD	H
	ADD	L
	MOV	L,A		; PUT OCTAL IN H
	DCR	B		; DONE?
	JNZ	AGN		; NO, DO AGAIN

	RET			; YES, NORM RETURN

; THIS SUBROUTINE PRINTS 3 OCTAL DIGITS FROM H
; OR 6 OCTAL DIGITS FROM H AND L
;
; DIGITS ARE FOLLOWED BY A SPACE
;
PRINT6:	MVI	B,6		; LOAD CNTR W/6

	XRA	A		; CLEAR A
	JMP	NEXT1		; SHIFT ONE BIT

PRINT3:	MVI	B,3		; LOAD CNTR W/3
	DB	0E6H		; SKIP NEXT, SHIFT 2 BITS
NEXT3:	DAD	H		; SHIFT H/L LEFT 3 INTO A
	RAL
	DAD	H
	RAL
NEXT1:	DAD	H
	RAL
	ANI	7		; STRIP OFF OCTAL

	ORI	30H		; ADD ASCII
	CALL	OUTCHK		; PRINT IT

	DCR	B		; DONE?
	JNZ	NEXT3		; NO, SHIFT NEXT THREE

SPACE:	MVI	A,' '		; YES, PRINT SPACE
	JMP	OUTCHK		; AND RETURN
; RETURN TO CALLING PROGRAM THROUGH OUTCHK

; THIS SUBROUTINE WILL INPUT A CHARACTER, STRIP
; PARITY AND AUTOMATICLY ECHO THE CHARACTER.
; IT WILL ALSO OUTPUT A CHARACTER WITH CHECKSUM CALCULATIONS.
;
INCH:	IN	ACIACR		; READ STATUS
	RRC			; ROTATE ACIA RDRF BIT INTO CARRY
	JNC	INCH		; NOT READY

	IN	ACIADR		; READ CHARACTER
	ANI	7FH		; STRIP PARITY

OUTCHK:	PUSH	PSW		; SAVE CHARACTER
	ADD	C		; ADD IN CHECKSUM
	MOV	C,A		; UPDATE CHECKSUM

LOOP:	IN	ACIACR		; READ STATUS
	RRC
	RRC			; ROTATE ACIA TDRE BIT INTO CARRY
	JNC	LOOP		; READY?

	POP	PSW		; YES, GET CHAR
	OUT	ACIADR		; PRINT CHARACTER

	RET			; FROM WHENCE YE CAME


