;*******************************************************************
;
; DISCTEST - Disk test utility
;    Tests write/read operation of an Altair 8" floppy disk by
;    writing a pattern to disk, reading back that pattern, then
;    displaying the data on a terminal connected to an 88-SIO
;    serial port (not 2SIO).
;
;    PROM Origin: FD00h
;
;    This PROM is part of the test suite used by MITS bench 
;    technician Robert Lopez. These PROMs were used at MITS
;    to test and debug boards for the Altair 8800 computer.
;
;    OPERATION:
;
;    1)	Turn on drive zero and insert a floppy.
;
;    2) Examine the PROM address (FD00h) and depress RUN. The RAM
;       resident part of the program is copied to RAM at address
;       zero and the PROM suspends in endless loop.
;
;    3) Stop the computer, examine the RAM entry address (0070h),
;	then set the sense switches (A15-A8) to 70h. This tells
;       the program to write the pattern 70h down to 00h on the
;       sector. This data, in turn, generates a usable ASCII
;	pattern on the terminal when displayed.
;   
;    4) Depress RUN. Drive zero is restored to track 0 and the
;       program then enters its test loop:
;
;	a) Write sector zero of the current track starting with
;	   the value on the sense switches, then decrementing by
;	   one for each subsequent byte written.
;
;	b) Read back sector zero and send the 1st 98 bytes read
;	   through an SIO port at 0/1 (not 2SIO).
;
;	c) Step in one track and repeat from (a). After testing
;	   track 75 (not 76), restore to track zero and repeat.
;
;    The value on the sense switches is the number of bytes that
;    will be written, and the start value of the 1st byte after
;    the sync byte. The display step always displays 98 bytes,
;    including the sync byte, so at least 97 (61h) bytes should be
;    set on the sense switches so that no displayed bytes are
;    random. The maximum count should be about 136 (88h). If the
;    sense switches are set to zero, the test stops because the
;    program hangs waiting for more data requests than the
;    controller will issue for a sector.
;
;    Disassembled 1/23/2016 by Mike Douglas from a 1702a PROM
;    provided by Tom Sanderson.
;
;*******************************************************************

; I/O port addresses

SENSESW	equ	0FFH		;front panel sense switch address

DRVSEL	equ	8		;drive select port (out)
DRVSTAT	equ	8		;drive status port (in)
DRVCTL	equ	9		;drive control port (out)
DRVSEC	equ	9		;drive sector status regsiter (in)
DRVDATA	equ	10		;drive data in/out

SIOCSR	equ	0		;SIO control/status register
SIODR	equ	1		;SIO data register

; FDC status bit masks

HEADMSK	equ	004h		;mask to get head load bit alone
MOVEMSK	equ	002h		;mask to get MVHEAD bit alone
TRK0MSK	equ	040h		;mask to get TRACK0 bit alone
ENWDMSK	equ	001h		;enter new write data mask
NRDAMSK	equ	080h		;new read data available mask

; FDC command bit masks

STEPIN	equ	001h		;drive command - step in
STEPOUT	equ	002h		;drive command - step out
HLOAD	equ	004h		;drive command - load head (8")
HUNLOAD	equ	008h		;drive command - unload head
WRITE	equ	080h		;drive command - start write

; Misc equates

RUNLOC	equ	0		;run location in RAM
READBUF	equ	019Eh		;read buffer location
NUMTRK	equ	77		;number of tracks

	org	0FD00h
;--------------------------------------------------------------------
;  Move the executable from PROM to RAM starting at address zero.
;  Once moved, die in a loop so the operator can reset the machine
;  and run from address 0070h.
;--------------------------------------------------------------------
	lxi	h,RAMCODE	;hl->executable in PROM
	lxi	d,RUNLOC	;de->destination in RAM
	lxi	b,255		;bc=number of bytes to move

moveLp:	mov	a,m		;copy from PROM to RAM
	stax	d
	inx	h
	inx	d
	dcx	b
	mov	a,b
	ora	c
	jnz	moveLp

	nop			;spacer
die:	jmp	die		;idle here after copy
	nop			;spacer

;--------------------------------------------------------------------
;  The disk test program starts here. It is copied to RAM at
;  address zero for execution. The program is started by
;  running from location "start" at 0070h.
;--------------------------------------------------------------------
RAMCODE	equ	$		;start of code to put in RAM
OFFSET	equ	RAMCODE-RUNLOC	;label offset when running in RAM

; wrSect - write sector zero on the current track with the
;   number of bytes specified on the sense switches. 

wrSect:	mvi	a,0		;select drive 0
	out	DRVSEL

	mvi	a,HLOAD		;load head
	out	DRVCTL

rdSens:	in	SENSESW		;# bytes to write from switches 
	mov	c,a		;c=number of bytes to write
	mvi	d,0FFh		;d=sync byte
	mvi	b,ENWDMSK	;b=mask for "enter new data" flag

wtSec:	in	DRVSEC		;read sector position register
	cpi	0C0h		;wait for sector true and sector 0
	jnz	wtSec-OFFSET

	mvi	a,WRITE		;issue write command
	out	DRVCTL

; write sync byte

wrSync:	in	DRVSTAT		;wait for enter data true
	ana	b
	jnz	wrSync-OFFSET

	mov	a,d		;write sync byte from d
	out	DRVDATA

; write number of bytes specified on sense switches. The byte
;    values written are the decrementing number of bytes left.

wrData:	in	DRVSTAT		;wait for enter data true
	ana	b
	jnz	wrData-OFFSET

	mov	a,c		;write value of byte counter
	out	DRVDATA

	dcr	c		;decrement number of bytes left
	jnz	wrData-OFFSET

; write a trailing zero

wrZero:	in	DRVSTAT		;wait for enter data true
	ana	b
	jnz	wrZero-OFFSET

	xra	a		;write a zero byte
	out	DRVDATA

;--------------------------------------------------------------------
;  Read 98 bytes of the data just written into READBUF and send
;  out an SIO port at 0/1.
;--------------------------------------------------------------------
	lxi	h,READBUF	;hl->read data buffer
	mvi	b,NRDAMSK	;b=new read data bit mask

	nop			;spacer
	nop

rdSec:	in	DRVSEC		;wait for sector true on sector 0
	cpi	0C0h
	jnz	rdSec-OFFSET

rdLoop:	in	DRVSTAT		;wait for read data bit
	ana	b
	jnz	rdLoop-OFFSET

	in	DRVDATA		;read data byte
	mov	m,a		;store in memory
	inr	l		;until lsb of address=0 (98 bytes)
	jnz	rdLoop-OFFSET

	mov	a,d		;a=FF
	out	DRVSEL		;deselect the drive

; display data read through SIO at 0/1

	lxi	h,READBUF	;hl->read data buffer

wtSio:	in	SIOCSR		;wait for OK to transmit
	rlc
	jc	wtSio-OFFSET

	mov	a,m		;a=next byte from read buffer
	out	SIODR		;transmit it

	inr	l		;move to next byte in buffer
	jnz	wtSio-OFFSET

	mvi	a,0		;re-select drive zero
	out	DRVSEL

	jmp	doStep-OFFSET	;step to next track

	rst	7		;spacer/patch bytes
	rst	7

;--------------------------------------------------------------------
;  start - jump here in RAM to start program execution. Seeks to
;     track zero then enters the write/read/display loop.
;--------------------------------------------------------------------
start:	mvi	a,0		;select drive 0
	out	DRVSEL

home:	mvi	e,0		;e=current track

seek0:	in	DRVSTAT		;wait until OK to move head
	ani	MOVEMSK
	jnz	seek0-OFFSET

	mvi	a,STEPOUT	;step out
	out	DRVCTL

	in	DRVSTAT		;track zero reached?
	ani	TRK0MSK
	jnz	seek0-OFFSET

	nop			;spacer
	mvi	e,0		;e=current track (redundant)
	nop			;spacer

reStart	jmp	wrSect-OFFSET	;go write on this track

; doStep - step in then repeat test. If max track reached, restore
;    to track zero and continue. Note this code skips the last
;    track (76).

doStep:	in	DRVSTAT		;wait until OK to move head
	ani	MOVEMSK
	jnz	doStep-OFFSET

	mvi	a,STEPIN	;step in
	out	DRVCTL

	inr	e		;e=current track
	mvi	a,NUMTRK-1	;at max track?
	cmp	e
	jnz	reStart-OFFSET	;run test again on new track

	jmp	home-OFFSET	;home to track 0 and start over

	end


