;**************************************************************
;Disassembly of a monitor program found in two 1702A EPROMs
;in a MITS Turnkey board, in an iCom Attache
;
;Assembles with Digital Research's ASM assembler.
;
;Disassembled by Martin Eberhard 25 May 2018
;Assembly verified to match the orignal EPROMs
;
;Commands
; A <start> <end> Examines and displays memory (in ASCII) from
;   the provided hex start address to the provided hex end
;   address.
;
; C Continuously tests RAM from 0 through FB5Fh, reporting
;   errors on the console. Terminate with the ESCAPE key.
;
; D <address> Displays the contents of each memory address
;   starting with the provided hex address, giving you a chance
;   to modify it.
;   * a 2-digit hex value modifies memory
;   * <Return> leave it unmodified
;   * <Space> backs up one address
;   * <Escape> exits the command
;
; E <start> <end> Examines and displays memory (in hex) from
;   the provided hex start address to the provided hex end
;   address.
;
; F Boots from floppy disk. Requires the floppy disk boot ROM
;   to be installed at address FF00h (the normal address for
;   the Altair DBL or MDBL ROM).
;
; G <address> Calls the specified address, leaving a return
;   address to this monitpr's main loop on the stack.
;
; H Boots from hard disk. Requires the hard disk boot ROM to
;   be installed at address FC00h (the normal address for the
;   Altair HDBL ROM).
;
; P Turns on the print function for the next command. This
;   changes the promt from an asterisk to a period, and causes
;   the next command's output to be sent both to the console
;   and to the 88-2SIO's sencon serial port.
;
; R Restarts at address 0, after reading the sense switches
;   into register a.
;
; T <start> <end> Continuously tests RAM in the specified
;   address range, reporting errors on the console. Terminate
;   with the ESCAPE key.
;
;**************************************************************

;Fixed RAM Addresses

LSTRAM	equ	0FB5Fh		;Last RAM address to test
STACK	equ	0FBF0h		;stack in Turnkey RAM
PMODE	equ	0FBFFh		;Prompt: '*' = normal,  
				;..'.'=print to port B too
FBOOT	equ	0FF00h		;Boot from Altair floppy disk
HBOOT	equ	0FC00h		;Boot from Altair hard disk
MONITOR	equ	0FD00h		;This code's address

;Altair Sense Switch I/O Port

SENSES	equ	0FFh		;Altair sense switch port

;88-2SIO equates

ACIACRA	equ	10h		;SIO A Status & Command reg
ACIADRA	equ	ACIACRA+1	;SIO A Data reg
ACIACRB	equ	12h		;SIO B Status & Command reg
ACIADRB	equ	ACIACRB+1	;SIO B Data reg

RXRBF	equ	01h		;Receive buffer full
TXTBE	equ	02h		;Transmit buffer empty
SIORST	equ	03h		;ACIA master reset
SIOINIT	equ	11h		;/16, 8 bits, no parity, 
				;..2 stop bits, no interrupts

;ASCII equates

BELL	equ	07h		;Ring the bell
CR	equ	0Dh		;Carriage Return
LF	equ	0Ah		;Line feed
ESC	equ	1Bh		;Escape

;*****************
;Beginning of code
;*****************
	org	MONITOR

;Initialize both ACIA's on an 88-2SIO

	mvi	a,SIORST
	out	ACIACRA
	out	ACIACRB

	mvi	a,SIOINIT
	out	ACIACRA
	out	ACIACRB

;Disable port B output, get command

MAIN:	mvi	a,'*'		;Standard prompt
	sta	PMODE		;Print mode off

;Get command, print to port B too if PMODE = '.'

PMAIN:	lxi	sp,STACK	;restore up stack

	call	PCRLF		;Print CR, LF

	lda	PMODE		;Print prompt
	call	PRINTA

;Get a command from the user and execute it

	call	GETCON		;Get a command

	push	psw		;Print a space
	call	PSPACE
	pop	psw

	cpi	'A'		;Examine ASCII command?
	cz	EXMCMD

	cpi	'G'		;Go command?
	jz	GOCMD

	cpi	'E'		;Examine hex command?
	cz	EXMCMD

	cpi	'D'		;Display & modify command?
	cz	DMCMD

	cpi	'T'		;Test RAM?
	cz	TSTCMD

	cpi	'C'		;Test all RAM?
	cz	TARCMD

	cpi	'F'		;Boot from floppy?
	jz	FBOOT		;(Requires boot PROM)

	cpi	'H'		;Boot from hard disk?
	jz	HBOOT		;(Requires hard disk boot PROM)

	cpi	'R'		;Restart command?
	jz	RSTCMD

	cpi	'P'		;Print mode?
	jnz	MAIN		;n: bogus command

	mvi	a,'.'		;Print mode on
	sta	PMODE

	jmp	PMAIN		;Go get another command

;***Command Routine****************************
; R: Restart at 0, after reading sense switches
;Note: This will misbehave if the Turnkey
;has the PROM-disable mod.
;**********************************************
RSTCMD:	in	SENSES		;Read sense switches
	jmp	0

;***Subroutine************************
;Get a 4-digit hex value from the user
;On Exit:
;  de = binary value of hex input
;  hl = previous de value
;Trashes psw,c
;*************************************
GETHX4:	lxi	h,0		;Initial value
	mvi	c,4		;4 digits

GH4LUP:	call	GETCON		;Get chr, check for abort

;Fall into GETHEX

;***Subroutine*******************
;Get Hex Input
;On Entry:
;  c = number of digits to get
;  hl = initial value (0)
;On Exit:
;  de = binary value of hex input
;  hl = previous de value
;Trashes psw,c
;******************************** 
GETHEX:	dad	h		;shift digits over
	dad	h
	dad	h
	dad	h

	sui	30h		;convert ASCII to hex
	cpi	0ah		;0-9?
	jc	GH4GAP

	sui	7		;Skip gap

GH4GAP:	add	l
	mov	l,a

	dcr	c		;More digits?
	jnz	GH4LUP		;y: go get 'em

	xchg			;result to de, old de to hl

;***Subroutine******
;Print a space
;On Exit:
;   a = ' '
;*******************
PSPACE:	mvi	a,' '

;Fall into PRINTA

;***Subroutine*******************************
;Print a on console, and optionally on port B
;On Entry:
;  a=character to print
;  PMODE = '.' if port B is enabled
;All registers preserved
;********************************************
PRINTA:	push	psw

PWAIT:	in	ACIACRA		;Wait for transmitter A
	ani	TXTBE
	jz	PWAIT

	pop	psw
	out	ACIADRA

	push	psw
	lda	PMODE		;Print mode on?
	cpi	'.'
	jz	BPRINT		;y: go print too

	pop	psw
	ret

;Print to port B

BPRINT:	in	ACIACRB		;Wait for transmitter B
	ani	TXTBE
	jz	BPRINT

	pop	psw
	out	ACIADRB
	ret


;***Subroutine*******************
;Print Carriage Return, Line Feed
;Trashes a
;********************************
PCRLF:	mvi	a,CR
	call	PRINTA
	mvi	a,LF
	jmp	PRINTA

;***Subroutine***************************
;Get a console character, abort if ESC
;On Exit:
;  a = console character, parity stripped
;Rude abort to MAIN if ESC
;****************************************
GETCON:	in	ACIACRA		;Wait for console chr
	ani	RXRBF
	jz	GETCON

	in	ACIADRA
	ani	7Fh		;strip parity

	cpi	ESC		;Abort?
	jz	MAIN

	jmp	PRINTA		;n: echo and return


;***Command Routine*************************************
; C: Test all of RAM comntinuously until user types ESC
;On Entry:
;  bc = random number generator seed (ununitialized)
;Exits (when ESC is typed) to MAIN, canceling print mode
;*******************************************************
TARCMD:	lxi	h,0		;start at address 0
	lxi	d,LSTRAM	;last RAM address
	jmp	TSTRAM

;***Command Routine*************************************
; T: Test RAM comntinuously until user types ESC
;On Entry:
;  bc is just random (seed)
;Exits (when ESC is typed) to MAIN, canceling print mode
;*******************************************************
TSTCMD:	call	GETHX4		;Get de=start address
	call	GETHX4		;hl=start address,
				;..get de=end address

;Fall into TSTRAM

;***Subroutine******************************************
;Test memory continuously until user types ESC
;On Entry:
;  bc is just random (seed)
;  de = last address
;  hl = first address
;Exits (when ESC is typed) to MAIN, canceling print mode
;*******************************************************
TSTRAM:	mvi	a,'$'		;Indicate test in progress
	call	PRINTA

;Fill RAM with random numbers, using (uninitialized)
;bc as the random number generator's seed

	call	RANDOM		;Calculate next random number

	push	b		;Random seed
	push	h		;memory pointer
	push	d		;end address

TSTWRT:	call	RANDOM		;Get next random number
	mov	m,b		;..and write it to RAM

	call	NXTADR		;Next address, check for end
	jnz	TSTWRT		;not done: keep going

	pop	d		;end address
	pop	h		;memory pointer		
	pop	b		;random seed

;Use the same random number seed to compare against
;what's in RAM, and report any differences

	push	h
	push	d

TSTRD:	call	CONIN		;Abort from user?

	call	RANDOM		;re-get next random number
	mov	a,m
	cmp	b
	cnz	PHLBA		;Print address, b, a in hex

	call	NXTADR		;Next address, check for end
	jnz	TSTRD		;Not done: keep going

	pop	d
	pop	h

	jmp	TSTRAM		;Test again, bc is new seed

;***Subroutine**********************
;Calculate next pseudo-random number
;On Entry:
;  bc = previous random seed
;On exit: 
;  a = b = next random number
;  bc = updated random seed
;***********************************
RANDOM:	mov	a,b
	ani	0B4h
	ana	a
	jpe	SBCL

	stc

;Shift bc left one, shifting carry in

SBCL:	mov	a,c
	ral
	mov	c,a
	mov	a,b
	ral
	mov	b,a
	ret

;***Subroutine****************
;Print hl as 4 hex digits on a
;new line, followed by a space
;On Entry:
;  hl = 16-bit binary value
;Trashes psw
;*****************************
PHLHEX:	call	PCRLF		;Print CR, LF

	mov	a,h		;High byte
	call	PAHEX

	mov	a,l		;Low byte
	call	PAHEX

	call	PSPACE		;Print a space
	ret

;***Subroutine*********************
;Print 4-hex digit address followed
;by two 2-hex digit values
;On Entry:
;  b = 1st value
;  a = 2nd value
;  hl = address
;Trashes psw
;**********************************
PHLBA:	push	psw
	call	PHLHEX		;Print hl=address in hex

	mov	a,b		;Print 1st value
	call	PAHEX

	call	PSPACE		;Print a space

	pop	psw

;Fall into PAHEX to print 2nd value in a

;***Subroutine**********
;Print a as 2 hex digits
;On Entry:
;  a = binary value
;Trashes psw
;***********************
PAHEX:	push	psw
	call	PHHNIB		;Print high nibble
	pop	psw
	jmp	PLHNIB		;Print low nibble

;***Subroutine**********
;Print High Hex Nibble
;On Entry:
;  a<7:4> = binary value
;***********************
PHHNIB:	rar			;shift nibble down
	rar
	rar
	rar

;Fall into PLHNIB

;***Subroutine**********
;Print Low Hex Nibble
;On Entry:
;  a<3:0> = binary value
;***********************
PLHNIB:	ani	0fh		;Mask nibble
	adi	'0'		;Convert to ASCII
	cpi	'9'+1		;A=F?
	jc	PRINTA		;N: print it

	adi	7		;y: make it A-F
	jmp	PRINTA		;..and print it


;***Command Routine******************
; A: Examine Memory, report in ASCII
; E: Examine Memory, report in hex
;On Entry:
;  a = command (A or E)
;On Exit:
;  a=0 (which will cancel print mode)
;Also exits (when ESC is typed) to
;MAIN, canceling print mode
;************************************
EXMCMD:	mov	b,a		;Remember command

	call	GETHX4		;Get de=start address
	call	GETHX4		;hl=start address,
				;..get de=end address

;Start a new row on screen

EXMROW:	mvi	c,10h		;Bytes per line on screen
	call	PHLHEX		;Print address in hex,
				;..and then a space

;Next byte on the row

EXMBYT:	mov	a,b		;Hex or ASCII?
	cpi	'A'

	mov	a,m		;Get memory data
	jz	EASCII		;Go print it in ASCII

	call	PAHEX		;Print value in hex
	call	PSPACE		;Print a space

;Next address, unless we are done

EXMNXT:	call	NXTADR		;Next address, check for end
	rz			;Done? (a=0 if so)

	dcr	c		;Same row?
	jz	EXMROW

	call	CONIN		;Opportunity to abort
	jmp	EXMBYT

;Display memory contents as ASCII

EASCII:	ani	60h		;Printable character?
	jnz	EASCI2		;y: then print it

	call	PSPACE		;n: Print a space
	jmp	EXMNXT

;Print printable ASCII chr

EASCI2:	mov	a,m		;re-get chr

	ani	7fh		;Srip parity
	call	PRINTA		;on screen

	jmp	EXMNXT

;***Command Routine*************************************
; D: Display and Modify memory command
;Exits (when ESC is typed) to MAIN, canceling print mode
;*******************************************************
DMCMD:	call	GETHX4		;Get de=address
	xchg			;address to hl

DMNEWL:	call	PCRLF		;Print CR, LF
	call	PHLHEX		;Print hl=address in hex

DMNEXT:	mov	a,m		;Get current memory data
	call	PAHEX		;Print it as hex

	mvi	a,'-'		;spacer
	call	PRINTA

	call	GETCON		;Get chr, check for abort
	cpi	ESC		;(stupid) check again for abort
	rz

	cpi	' '		;Back up in RAM?
	jz	BACKUP		;y: go back up

;CR leaves memory unmodified

	cpi	CR		;Leave unmodified?
	jnz	DMMOD		;n: go modify memory

	call	NXTADR		;next address (could have just inx hl...)
	call	PHLHEX		;Print hl in hex
	jmp	DMNEXT

;Modify this memory address, and make sure RAM write worked

DMMOD:	xchg			;save hl to de
	lxi	h,0		;initial value
	mvi	c,2		;2 digits to get
	call	GETHEX		;go get de= 2 hex, restore hl

	mov	m,e		;Write new value to memory

	mov	a,m		;Check to see if it wrote ok
	cmp	e
	mov	b,e		;remember what we wrote

	jz	DMNXTA

;---------------------------------------------------
;Memory write failure: report failure on the console
;b=what we tried to write
;a=what we read
;---------------------------------------------------
	call	PHLBA		;Print error address, write
				;..value, readback value

	mvi	a,BELL		;ring the bell
	call	PRINTA
	call	PSPACE		;Print a space

	mvi	a,'?'		;flag error
	call	PRINTA

	jmp	PMAIN		;and abort,
				;..leaving print mode alone

;--------------------
;Next memory location
;--------------------
DMNXTA:	inx	h		;Next memory address
	jmp	DMNEXT

;***Subroutine************************
;Next address, check for end
;On Exit:
;  hl incremented
;  a=0 and Z set if hl was equal to de
;*************************************
NXTADR:	mov	a,e		;compare de to hl
	sub	l		;low bytes match?
	jnz	NXTA1		;n: Z is cleared

	mov	a,d
	sbb	h		;Z set if both bytes match

NXTA1:	inx	h		;next address
	ret

;-----------------------
;Back up in RAM
;On Exit:
;  hl and de decremented
;-----------------------
BACKUP:	dcx	h		;decrement memory pointer
	dcx	d		;(Completely unecessary)
	jmp	DMNEWL		;back to D command

;***Subroutine***********************************
;Get character from the console
;Ignore spaces, abort to MAIN (which cancels mode
;mode) if ESC. Assume the console is so slow that
;we never need to check its status.
;On Exit:
;  a = console chr
;************************************************
CONIN:	in	ACIACRA		;Flush status
	in	ACIADRA		;get console chr
	ani	7fh		;strip parity
	cpi	' '
	jz	CONIN		;Ignore spaces

	cpi	ESC		;Abort?
	jz	MAIN		;Y: then do so

	ret

;***Command Routine********************
; G: Go Command: Get address from user,
;and "call" requested address
;**************************************
GOCMD:	call	GETHX4		;Get exec address
	xchg
	lxi	d,PMAIN		;create return address
	call	PCRLF		;Print CR, LF
	push	d		;return address ont stack
	pchl			;go to requested address

;*********************
;Unused PROM locations
;*********************
	db	0FFh
	db	0FFh
	db	0FFh
	db	0FFh

	end

