; ----------------------------------------------------------------------
TITLE   Micro Decision ROM Source (Rev 1.3)
;	Reconstruction version 1.0-20030917
;       For MD2 Rev. 1 Motherboards.
;       This version compiles exactly to the ROM 1.3 code.
;       Assembly code reverse-engineered by Scott G. Taylor 2003
;       using the Morrow MD3 Rom 3.1 code as a guide.
;       Compiler used was Microsoft M80.COM.
;       Linker used was the Public Domain ld80 (L80 compat) linker.
;
;	Please forward commentary, clarifications and adjustments to
;		morrow (@) smedley (.) mrynet (.) com
; ----------------------------------------------------------------------

; ----------------------------------------------------------------------
; Checksums for the ROM and RAM tests should be edited below
; ------------------------------

ROMCKSUM	EQU	0C5H	; Original MD2 1.3 ROM was 0C5H
RAMCKSUM	EQU	01FH	; Original MD2 1.3 ROM was 01FH

	.Z80
	ASEG

; ----------------------------------------------------------------------
; Binary origin
; ------------------------------
;

ROM	EQU	0

	ORG	ROM

; ----------------------------------------------------------------------
; System Equates
; ------------------------------
;

REV	EQU	13H
ROMSUB	EQU	'A'

ROMSIZ	EQU	0800H		; 2716 chip ROM - 2048 bytes (MD2)
;ROMSIZ	EQU	1000H		; 2732 chip ROM - 4096 bytes (MD3)
ROMEND	EQU	ROMSIZ-1	; Address of last byte in ROM (devel use)
RAMTSZ	EQU	03F8H		; Number of RAM bytes to test
ROMTSZ	EQU	ROMSIZ-3	; ROM bytes to test
				;  (3FDH for 2K ROM, 7FDH for 4K ROM)

CR      EQU     0DH             ; Carriage return
LF      EQU     0AH             ; Line feed
BELL    EQU     07H             ; Bell
MSGTAG	EQU	0AEH		; This tag is used in text message DBs
				;   for messages output during the ROM
				;   initialisation and cold boot process.
				;   I have no clue what its purpose is.

S1DATA  EQU     0FCH            ; Console port data
S1STAT  EQU     0FDH            ; Console port status
S2DATA  EQU     0FEH            ; Printer port data
S2STAT  EQU     0FFH            ; Printer port status

DISKBUF EQU     0FC00H          ; Start of disk sector buffer
MEMTOP  EQU     0FFFFH          ; Top of ram

ROMCTL  EQU     0F6H            ; ROM enable/disable port 

JPOPCOD	EQU	0C3H		; Opcode for JP for testing

WR      EQU     0               ; For building test pattern..
RD      EQU     40H             ; ..for memory diagnostics

BOOTBF  EQU     DISKBUF+200H

        ; offsets within the ramdatx area.
HSTDSK  EQU     0               ; host disk
HSTTRK  EQU     1               ; host track
HSTSEC  EQU     2               ; host sector
;
RETRY   EQU     4               ; retry count
; 
SECSIZ  EQU     12              ; two bytes which describe sector size
SECCNT  EQU     3               ; sector count
;
IOADD   EQU     10              ; address of actual i/o code
;
HSTBUF  EQU     5               ; pointer to data buffer
;         
ERFLAG  EQU     7               ; error flag
;               
STADD   EQU     14              ; 7 byte buffer for fdc status
;
PHYTRK  EQU     8
PHYHD   EQU     9

CMDCNT	EQU     21
FDCCMD	EQU     CMDCNT+1
CY	EQU     CMDCNT+3
HD	EQU     CMDCNT+4
R	EQU     CMDCNT+5
N	EQU     CMDCNT+6
EOT	EQU     CMDCNT+7
GPL	EQU     CMDCNT+8
DTL	EQU     CMDCNT+9
EOTOF	EQU	5

HMBT	EQU	0	; Home bit for command
SPCMD	EQU	3	; SPECIFY command
SDSTAT	EQU	4	; Sense Drive Status command
WRCMD	EQU	5	; Write command
RDCMD	EQU	6	; Read command
SKCMD	EQU	7	; Root of SEEK and HOME command
SICMD	EQU	8	; Sense Interrupt command

SEKBT	EQU	8	; Seek bit for command
STCNT	EQU	7	; Number of bytes returned by FDC

        ; error codes
WPERR	EQU	1	; Write protect code
SKERR	EQU	2	; Seek error code
NFERR	EQU	5	; Not found code
NTRDY	EQU	6	; Not ready code

        ; I/O ports are:
FDCSTAT EQU     0FAH
FDCDATA EQU     0FBH
MOTOR	EQU     0F7H
MOTOR2	EQU	0F8H	; ROM 1.3 MOTOR check (ROM 3.1 uses 0F7H)
MTRCHK	EQU     0F5H

PREC	EQU     3       ; precomp bit (MD2)
;PREC	EQU	2	; precomp bit (MD3 ROM 3.1)

MERR	EQU     040H    ; master error mask
EXEC	EQU     20H     ; mask for exec bit
RDRDY	EQU     0F0H    ; status for read byte
WRRDY	EQU     0B0H    ; status for write byte
 
PR40	EQU     19      ; precomp bound for 40 tracks
PR80	EQU     39      ; precomp bound for 80 tracks

;
DOUT	EQU	0000H	; MD Rom 1.3 uses 0000H timeouT
; DOUT	EQU	8000H 	; MD Rom 3.1 uses 8000H timeout
;
ASC0	EQU	'0'	; ASCII 0
ASCA	EQU	'A'	; ASCII A
ASCI	EQU	'I'	; ASCII I
ASCR	EQU	'R'	; ASCII R

MRQ	EQU	7
EXB	EQU	5

WRALL	EQU	0	; Write allocated
WRDIR	EQU	1	; Write directory
WRUAL	EQU	2	; Write unallocated

	; Bits within the DISKDEF byte
SEC0	EQU	6
DEN	EQU	6
DSB	EQU	2
DSM	EQU	2
VD	EQU	7
TK80	EQU	5
SIZMSG	EQU	18H
DENMSK	EQU	40H

	; Bits within the flag byte: DFLAG
HSTACT	EQU	0	; Host active flag
HSTWRT	EQU	1	; Host written flag
RSFLAG	EQU	2	; Read sector flag
READOP	EQU	3	; Read operation flag

	; Offsets withint the RAMDATY area.
SEKDSK	EQU	0	; Seek disk
SEKTRK	EQU	1	; Seek track
SEKSEC	EQU	2	; Seek sector

SEKHST	EQU	3	; SEKSEC converted to host

UNACNT	EQU	4	; Unalloc rec count
UNADSK	EQU	5	; Unalloc disk
UNATRK	EQU	6	; Unalloc track
UNASEC	EQU	7	; Unalloc sector
UNAMAX	EQU	8	; Sectors per allocated block
SECTRK	EQU	9	; Sectors per track

WRTYPE	EQU	10	; Write type
DFLAG	EQU	11	; Flag byte
TRSEC	EQU	12	; Temp storage

VMSGP	EQU	13	; Pointer to virtual drive message
VDRVP	EQU	15	; Pointer to virtual drive in message.

CDSK	EQU	17	; Current drive
PDSK	EQU	18	;
VDSK	EQU	19	; Current virtual drive

DMAADR	EQU	20	; DMA address

CONOUT	EQU	22	; Pointer to CONOUT address
CONIN	EQU	24	; Pointer to CONIN address

MTAB	EQU	26	; Pointer to MTAB
TROFF	EQU	7	; Offset to track in MTAB

MTRMSK	EQU	3	; Motor control bit mask
MTRMSK1	EQU	7
TRK0MSK	EQU	10H	; On-Track-Zero mask

; ----------------------------------------------------------------------
; Jump table
; ------------------------------
;

	JP	START		; Power-on Jump
	JP	MESG		; Print a message pointed to by DE
	JP	HOME		; Home a diskette head
	JP	RDHST		; Read physical sector
	JP	WRTHST		; Write physical sector
	JP	DISCIO		; Direct I/O read or write
	JP	RDIO		; I/O read loop
	JP	WRIO		; I/O write loop
	JP	BTER		; Print boot error message

	DW	RAMDATX		; Prointer to ROM version of DATX

START:	
	IM	0		; Set interrupt mode 0
	LD	SP,0H		; Assume that ram is OK
	LD	IY,ROMDATY	; Init IY
	CALL	IUSART		; Initialize UARTS
	LD	DE,SIGNON	; Point to sign-on message
	CALL	CLRMSG		; Print the signon, clearing the screen
	CALL	ROMTST		; ROM CRC check
	CALL	BUFCHK		; MOV CODE TO HIGH MEMORY
	CALL	RAMTST		; POST MOVE CRC
	CALL	DISKBUF		; CALL RELOC CODE
WARM:	
	LD	DE,BOOTMSG	; BOOTMSG
	CALL	MESG		; PRINT MESSAGE
PLOP1:	
	CALL	CINY		; GET CHAR FROM CRT
	CP	CR		; IS IT A CARRIAGE RETURN
	JR	NZ,PLOP1	; LOOP IF NOT
	LD	DE,CRLF		; CR LF MESSAGE
	CALL	MESG		; PRINT TO CRT
	JP	BOOTCPM		; READ IN CPM

MEMERR:	CALL	MESG
	LD	DE,RERR
	CALL	MESG
	HALT

IUSART:	LD	A,80H		; UART RESET command
	OUT	(S1STAT),A	; Reset UART 1
	OUT	(S2STAT),A	; Reset UART 2
	OUT	(S1STAT),A	; Reset UART 1 again (get around a bug)
	OUT	(S2STAT),A	; Reset UART 2 again (get around a bug)
	LD	A,40H		; UART RESET command 2
	OUT	(S1STAT),A	; Reset UART 1 -- sequence 2
	OUT	(S2STAT),A	; Reset UART 2 -- sequence 2
	LD	A,0CEH		; Command: 8 bits, no parity, x16, 2 stop
	OUT	(S1STAT),A	; Send to UART 1
	OUT	(S2STAT),A	; Send to UART 2
	LD	A,37H		; Command: TX, RX: ON, DTR, RTS: ON, ER reset
	OUT	(S1STAT),A	; Send to UART 1
	OUT	(S2STAT),A	; Send to UART 2
	RET

CRLF:	DB	CR,LF,0		; CR/LF message
COLN:	DB	':',0A0H,0	; A simple colon output message

RMCKSM: ROMCKSUM		; Checksum for the entire ROM

; THE FOLLOWING ***MUST*** RESIDE AT 07FH-080H for the interrupt
; return-address for the WAIT routine.
	
        DS (ROM+7FH-$),0FFH
WAITINT:DW SKINT        ;Interupt return address for WAIT routine.


; ----------------------------------------------------------------------
; Subroutine: ROMTST
; -------------------
;
; Function: This routine initializes the hl, and bc register
;       pairs and calls chksum. It is used to test the system  
;       integrity. If the test passed, then a message is printed
;       and the diagnostics continue. If the test failed, then
;       execution will be passed to the memerr routine.
;       Memerr will print a message indicating the problem found,
;       and then abort the boot process.
;

ROMTST:				; Perform a checksum on the ROM
	LD	HL,0		; Starting address in ROM
	LD	BC,ROMTSZ	; Number of bytes to check
	CALL	CHKSUM		; Compute checksum of ROM
	LD	DE,ROMMSG	; Point to ROMMSG message
	JR	NZ,MEMERR	; Print the message if the checksum was bad
	RET			; ROM OK, continue

; ----------------------------------------------------------------------
; Subroutine:   MEMERR
; ---------------------
;
; Function: When a bad memory location is detected during
;       the memory test, execution comes here. This routine
;       prints an error message, and halts the processor.
;

RAMTST:				; Perform a checksum on RAM
	LD	HL,DISKBUF	; Starting address in RAM
	LD	BC,RAMTSZ	; Number of bytes to check
	CALL	CHKSUM		; Compute checksum of RAM
	LD	DE,RAMBAD	; Point to RAMBAD message
	JR	NZ,MEMERR	; Print the message if the checksumw as bad
	RET			; RAM OK, continue

; ----------------------------------------------------------------------
; Subroutine: CHKSUM
; -------------------
;               
; Function: This routine performs a checksum on a given block of
;       memory. The memory may be ROM or RAM, but must be set
;       such that the correct checksum is zero. The start of
;       the block to be tested should be addressed by the HL  
;       register pair, and the number of bytes to check should
;       be in the BC register pair.  If the checksum is correct,
;       then the accumulator will be zero upon return.
;       If the accumulator is non-zero, then the memory is bad.
;       The test used is a parity test, by column. The parity
;       is computed by xor'ing all the bytes together. The last
;       byte in the memory being tested is chosen to cause
;       correct parity to yield a 0 after the xor's.
;

CHKSUM:	XOR	A		; Initialize checksum
	PUSH	AF		; Because it's popped later
CHK:	POP	AF		; Get current check byte value
	XOR	(HL)		; Calculate new check byte
	INC	HL		; Next location to test
	DEC	BC		; Decrement byte count
	PUSH	AF		; Save check byte
	LD	A,B		; Test if BC == 0
	OR	C		; B or C == 0 ==> DONE
	JR	NZ,CHK		; If not done, loop
	POP	AF		; Get completed check byte
	RET			; And return it

; ----------------------------------------------------------------------
; Subroutine BUFCHK
; ------------------
;
; Function: This routine moves the memory test program to
;       the disk buffer area, and pads the disk buffer ram
;       such that when a checksum is computed on the buffer
;       area, the result will be zero. It then calculates a
;       checksum on the disk buffer RAM at location FC00h to
;       FFFFh as an indication of system integrity. If the
;       test passes, then the routine will return, and the
;       accumulator will be equal to zero. If a bad checksum
;       is computed, then execution will be passed to memerr.
;

BUFCHK:	LD	HL,BLOCK		; Set up to move program from...
	LD	DE,DISKBUF		; To...
	LD	BC,ENDBLK-STBLK		; How many...
	LDIR				; Move it
	LD	BC,03F7H-ENDBLK+STBLK	; Number of bytes to pad
	PUSH	DE			; Save this
	POP	HL			; Get it for source of LDIR
	INC	DE			; Dest for LDIR
	LD	(HL),0FFH		; Initial pad
	LDIR				; Walk up and fill
	LD	(HL),RAMCKSUM		; The last byte is the RAM checksum
	RET

;
; *****************************************
; This section of code is run at FC00h!!!!
; *****************************************
;
;	The code is referenced for copying by address BLOCK
;	but it is phased to address relative to DISKBUF.

BLOCK:

.phase	DISKBUF

STBLK:	IN	A,(ROMCTL)	; Turn off ROM
	LD	A,(00)		; Get value at location 0
	CP	JPOPCOD		; Test if it's a JP opcode
	OUT	(ROMCTL),A	; Turn ROM back on
	JR	NZ,MTEST	; If not a JP then cold boot
	JP	WARM		; Otherwise warm boot

MTEST:	LD	DE,TSTMSG	; Point to test message
	CALL	MESG		; Print it
	IN	A,(ROMCTL)	; Turn off the ROM
	LD	HL,0000		; Low address to test
	LD	BC,DISKBUF-1	; Highest to test
	CALL	MEMTEST		; Test memory
	OR	A		; Test flags
	OUT	(ROMCTL),A	; Turn ROM back on
	LD	DE,RAMBAD	; Point to RAMBAD message
	JP	NZ,MEMERR	; If Memfail, then jump
	LD	DE,ROMOK	; Point to memory OK message
	CALL	MESG		; and print it
	JP	WARM		; All OK, so continue booting

; ----------------------------------------------------------------------
; Subroutine: MEMTEST
; --------------------
;
; Function: This routine performs a memory test on a block of memory.
;       The block of memory may be of any size, and is determined
;       by the values passed upon entry.
;       When called, register pair HL points to the start of the
;       block to be tested, and register pair BC points to the last
;       location to be tested. When an error is encountered, the 
;       routine will return a non-zero value in the accumulator.
;       When no errors are found, the routine will return with
;       the accumulator equal to zero.
;

MEMTEST: LD	(SMEM),HL	; Save start pointer
	LD	HL,PATLST	; Point to test table

TESTLP:	LD	D,(HL)		; Get data to test
	INC	HL
	BIT	7,(HL)		; Check if end of table
	JR	NZ,PASS		; If so, then test passed
	LD	E,(HL)		; Get offset (0,1,2) & RD/WR
	BIT	6,E		; Test if read or write
	RES	6,E		; Reset the bit
	INC	HL		; Next in table
	PUSH	HL		; Save table pointer
	JR	Z,FILL		; 0 ==> Fill memory
	CALL	CHECK		; 1 ==> Check memory
	JR	OVER

FILL:	LD	IX,WRMEM	; Calculate start
	CALL	TESTM		; Test memory

OVER:	POP	HL		; Restore table pointer
	JR	TESTLP		; Loop while not done
PASS:	XOR	A		; Set accumulator to pass
	RET			; Return successful

CHECK:	LD	IX,RDMEM	; Set for read
TESTM:	LD	HL,(SMEM)	; Get where to start
	LD	A,E		; Get offset from start
	ADD	A,L		; Compute actual starting
	LD	L,A		;   address
	LD	A,H		;   HL <- HL+offset
	ADC	A,0
	LD	H,A

LP:	CALL	INDJP		; Indirect jump via index

	INC	HL		; Skip two bytes
	INC	HL
	INC	HL
	LD	A,B
	CP	H
	RET	C

	JR	NZ,LP		; If top not reached, keep going

	LD	A,C
	CP	L
	RET	C
	JR	LP

INDJP:	JP	(IX)		; Perform jump to WR or RD

WRMEM:	LD	(HL),D		; Write test value
	RET

RDMEM:	LD	A,(HL)		; Get test value
	CP	D		; Compare it to expected value
	JR	NZ,ERR		; If it's bad, return an error
	RET			; Return that memory byte compare succeeded

ERR:	POP	AF		; Clean up the stack
	POP	AF		;	...
	POP	AF		;	...
	LD	A,0FFH		; Set error return indicator
	RET

; ----------------------------------------------------------------------
; MEMORY PATTERN TEST TABLE
; --------------------------
; 
; format is:
;               1st byte -- value to use (0-ffh)
;               2nd byte -- bit 6 r/w flag 0 => write
;                                          1 => read
;                           bits [0,1]     offset (0,1,2)
;

PATLST: DB      0FFH,WR OR 0
        DB      00,WR OR 1
        DB      0FFH,WR OR 2
        DB      00,RD OR 1
        DB      0FFH,RD OR 2
        DB      0FFH,RD OR 0
        DB      00,WR OR 0
        DB      0FFH,WR OR 1
        DB      00,WR OR 2
        DB      00,RD OR 2
        DB      00,RD OR 0
        DB      0FFH,RD OR 1
        DB      0AAH,WR OR 0
        DB      55H,WR OR 2
        DB      0AAH,WR OR 1
        DB      0AAH,RD OR 1
        DB      55H,WR OR 1
        DB      0AAH,RD OR 0
        DB      55H,WR OR 0
        DB      55H,RD OR 2
        DB      55H,RD OR 1
        DB      55H,RD OR 0
        DB      00,80H          ; MSB=1 ==> End of pattern list


SMEM	EQU	$
ENDBLK:
.dephase 	; END OF FC00 AREA!@#

BLOCKEND:

BOOTCPM:	
	LD	HL,RAMDATX	; Starting at area RAMDATX,
	LD	DE,0FF00H	;   Copy the contents to FF00
	LD	BC,42H		;   for a total of 42H (66) bytes.
	LDIR			; Do the copy
	LD	IX,0FF00H	; Set up the IX index
	LD	IY,0FF1FH	; Set up the IY index
	CALL	SELSK		; HOME the drive
	CALL	DISCIO		; Try to read
	LD	A,(IX+ERFLAG)	; Get the load status
	OR	A		; Test for error bits set
	JP	Z,BOOTBF	; If successful load, jump

BTER:	LD	DE,BTERR	; Point to "boot error" message
	JP	MEMERR		; Print it, and force user to press RESET

; ----------------------------------------------------------------------
; Serial port routines
; ---------------------
;
; Following are output routines for both serial ports. when serout is called,
; It expects the character to output to be in the c regester, and the b reg.
; is to contain the port address for the serial port to be used.
;
;

; Serial output routines

CRTOUT:	
	IN	A,(S1STAT)	; Check serial 1 output status
	BIT	0,A		; See if ready
	JR	Z,CRTOUT	; Jump if not ready
	LD	A,C		; Load the byte to be output
	OUT	(S1DATA),A	; Send it
	RET			; DONE

; Serial input routines

CRTIN:	IN	A,(S1STAT)	; Check serial 1 input status
	BIT	1,A		; See if ready
	JR	Z,CRTIN		; Jump if not ready
	IN	A,(S1DATA)	; Get the entered character
	AND	7FH		; Strip off the 8th bit
	RET			; DONE

; ----------------------------------------------------------------------
; Message routines
; -----------------    
;       
; Clear the console srceen and output a prompt
; ---------------------------------------------
;       1) The DE reg pair must be set to the start of the prompt string
;

CLRMSG:	LD	B,50		; Line count 50 (32H)
CLRLP:	LD	A,LF		; Load an LF character
	CALL	OUTCN		; Output it to the console
	DJNZ	CLRLP		; Count down, and if not zero then loop

; Output the message pointed to by the DE reg pair
; -------------------------------------------------
;       1) The message string must be terminated by a zero
;

MESG:	LD	A,(DE)	
	OR	A
	RET	Z
	CALL	OUTCN
	INC	DE
	JR	MESG

; Ouput a character to the current console device
; ------------------------------------------------
;

OUTCN:	LD	C,A
	PUSH	DE
	PUSH	HL
	LD	L,(IY+CONOUT)	; Get CONOUT address in HL
	LD	H,(IY+CONOUT+1)
	CALL	CNTJP
	POP	HL
	POP	DE
	RET

; Utility routine to perform and indirect jump to the 
; appropriate console output routine.

CNTJP:	
	JP	(HL)

; ----------------------------------------------------------------------
; Read a physical sector
; -----------------------
;

RDHST:	LD	HL,RDIO		; Get read address
	LD	(IX+IOADD),L	; Put address in RAMDATX
	LD	(IX+IOADD+1),H

	LD	C,RDCMD		;  C = 6 for read command
	JR	IOHST		; Go do I/O

; ----------------------------------------------------------------------
; Write a physical sector
; ------------------------
;

WRTHST:	
	LD	HL,WRIO		; Get write address
	LD	(IX+IOADD),L	; Put address in RAMDATX
	LD	(IX+IOADD+1),H

	LD	C,WRCMD		; C = 5, write command

; ----------------------------------------------------------------------
; Routine common to read/write a physical sector
; -----------------------------------------------
;

IOHST:	LD	A,(IX+HSTDSK)	; Get host drive
	CALL	GDSK		; Get pointer to DSKDEF
	INC	HL		; Increment to DSKDEF1 entry

	LD	A,(HL)		; A = DSKDEF1
	AND	DENMSK		; Mask off density bit
	OR	C		; Or into command (5 is write, 6 is read)
	LD	(IX+FDCCMD),A	; Put in command buffer
	LD	(IX+CMDCNT),9	; Set command count

	LD	A,SIZMSG	; Get sector size bits
	AND	(HL)		; DSKDEF1
	RRCA			; Shift to 2 LSB
	RRCA
	RRCA
	LD	(IX+N),A	; Store sector size in CMDBUF(5)

	LD	B,80H		; B = DTL for 128
	LD	DE,80H		; DE = size bytes for 128
	CP	0		; See if 128 byte sectors
	JR	Z,STSIZ		; Jump if 128

	LD	B,0FFH		; B is FF for 256, 512 and 1024 byte sectors
	LD	DE,0		; DE is 0 for 256 byte sectors
	CP	1
	JR	Z,STSIZ		; Jump if 256 byte sectors

	LD	D,1		; DE is 1 for 512 byte sectors
	CP	2
	JR	Z,STSIZ		; Jump if 512 byte sectors

	LD	D,3		; Else DE is 3 for 1024 byte sectors (default)

STSIZ:	LD	(IX+DTL),B	; CMDBUF(7) is 128 for 128 byte sectors
				;   and FF for all others

	BIT	7,(IX+SECSIZ+1)	; Check if non-standard block size
	JR	NZ,NSTD		; Jump if non-standard block size

	LD	(IX+SECSIZ),E	; Else store 0 for 256, 1 for 512 byte sectors
	LD	(IX+0DH),D	;   and 3 for 1024 in RAMDATX SECSIZ

NSTD:	XOR	A		; Assume side 0 and clear CY
	BIT	2,(HL)		; Check DSKDEF1 for double sided

	LD	B,(IX+HSTTRK)	; Get host track
	LD	C,(IX+HSTSEC)	; Get host sector

	JR	Z,SSIDE		; Jump if single sided
	RR	B		; Divide track by 2.  B=TRACK, CY=HEAD
	RLA			; If track was odd, carry bit was set in 
				; divide. rotate that into A to get 
				; the head bit

SSIDE:	LD	(IX+HD),A	; Set head
	RLCA
	RLCA
	LD	(IX+PHYHD),A	; Store

	LD	(IX+PHYTRK),B	; Set track
	LD	(IX+CY),B	; Also store at CMDBUF(2)
	LD	(IX+R),C	; Set sector at CMDBUF(4)

	LD	A,3		; Mask out physical drive address
	AND	(HL)
	OR	(IX+PHYHD)	; OR in head bit
	LD	(IX+FDCCMD+1),A	; Store in CMDBUF(1)

	LD	DE,EOTOF	; GET EOT SECTOR
	ADD	HL,DE		; Add to MTAB pointer
	
	LD	A,(HL)		; Set EOT
	LD	(IX+EOT),A	; Store in CMDBUF(6)
	INC	HL		; Set GPL byte
	LD	A,(HL)		; Store in CMDBUF(7)
	LD	(IX+1DH),A

	; The IX area has not been set up
DISCIO:	LD	B,(IX+RETRY)	; Get retry count for this drive
	LD	DE,0300H

	; Start of Retry loop
DLOP:	PUSH	BC		; Save retry count
	PUSH	DE		; Save Not-Ready retry

	LD	A,SEKBT		; Set bit for seek command
	CALL	SELHM		; Seek track and wait for head to settle

	CALL	IOUTIL1

	PUSH	IY		; Push IY
	CALL	STRTI0		; Start FDC
	CALL	DIO		; Go do I/O
	POP	IY		; Restore IY

	LD	B,STCNT		; Set up and read
	PUSH	IX		;   FDC status bytes
	POP	HL		;   into status memory

	LD	DE,STADD
	ADD	HL,DE		; HL = Status memory

GSTLP:	CALL	INFDC		; Wait for FDC
	INI			; Get byte
	JR	NZ,GSTLP

SKST:	POP	DE		; restore not ready retry
	POP	BC		; Restore retry count
	LD	A,(IX+STADD)	; a = STATUS0
	AND	MERR		; Check for error
	JR	Z,DRET1		; Return if no error

	CALL	ECODE		; Calculate error code
	LD	A,WPERR		; If write protect error
	CP	C
	JR	Z,NORTRY

	LD	A,NTRDY		; If not ready error
	CP	C
	CALL	Z,CNT

	LD	A,SKERR		; If wrong track
	CP	C
	CALL	Z,WNGTRK	;      home

	INC	E		; Increment retry count
	LD	A,0BH		; Half-way through retries? (11 of 20)
	CP	B		; Compare with retries performed
	CALL	Z,HMOK		; If necessary, HOME the disk

	DJNZ	DLOP		; Retry loop

	; No retries, or retries exhausted
NORTRY:	LD	A,(IX+ERFLAG)	; Check ERFLAG
	CP	0FFH		; 0FFH == No error handling
	JR	Z,DRET

	CALL	EDSP
	JR	C,DRET		; Return if 'A' (abort)

	OR	A		; See if 'R' (retry)
	JP	NZ,DISCIO	; Try again

DRET1:	LD	C,A		; Else, 'I' (ignore)

DRET:	LD	(IX+ERFLAG),C	; Update error flag
	RET

STRTI0:	LD	C,FDCDATA	; Set up to xfer FDC command
	PUSH	IX		; Calc addr of comand list
	POP	HL		; and put in HL
	LD	DE,CMDCNT	; offset to command list
	ADD	HL,DE	

	LD	B,(HL)		; b=command list length
	INC	HL		; Increment command list pointer

	CALL	RDY		; Make sure FDC is ready

STATLP:	CALL	OUTFDC		; Wait for FDC
	OUTI			; Write comamnd byte
	JR	NZ,STATLP	; Loop 'til done

	LD	B,(IX+SECSIZ)	; Set up registers for I/O
	LD	E,(IX+SECSIZ+1)
	RES	7,E		; Clear non-standard bit
	LD	D,(IX+SECCNT)
	DEC	D		; D = sector count - 1

	LD	L,(IX+IOADD)	; HL = I/O address
	LD	H,(IX+IOADD+1)
	PUSH	HL		; Save on stack
	POP	IY		; IY = I/O address

	LD	L,(IX+HSTBUF)	; HL = DMA address
	LD	H,(IX+HSTBUF+1)

	RET

; ----------------------------------------------------------------------
; Do the actual I/O routine
; --------------------------
;

DILP:	BIT	MRQ,A			; See if master request
	RET	NZ

DIO:	IN	A,(FDCSTAT)
	BIT	EXB,A			; See if executing
	JR	Z,DILP

	PUSH	DE			; Save DE
	LD	DE,DOUT			; Load time-out count
	JP	(IY)			; Jump to respective I/O for this drive

IOUTIL1:				; Index DRIVET table for drive bits
	LD	HL,DRIVET		; Load the table address
	PUSH	DE			; Save DE
	LD	D,0			; Blank out MSB
	SRA	E			; Shift A to the right one bit
	ADD	HL,DE			; Offset HL by DE bytes
					; Looks up 
	POP	DE			; Restore DE
	LD	A,(HL)			; Select drive in A
	OUT	(MOTOR),A		; Start motor
	RET

EDSP:	LD	A,C
	RLCA
	LD	E,A
	LD	D,0
	LD	HL,ETAB-2
	ADD	HL,DE
	LD	E,(HL)
	INC	HL
	LD	D,(HL)

	PUSH	BC			; Save status
	PUSH	DE			; Save DE
	LD	DE,DMESG		; Basic error message
	CALL	MESG

	LD	A,ASCA			; Get basic drive letter "A"
	ADD	A,(IX+HSTDSK)		; Increment to get ASCII drive letter
	CALL	OUTCN			; Display drive

	LD	DE,COLN
	CALL	MESG

	POP	DE			; Restore

	CALL	MESG
	CALL	ERSP
	POP	BC			; C = Status
	RET

; ----------------------------------------------------------------------
; Output the virtual drive message
; ---------------------------------
;

VIRTM:	CALL	MESG		; Print message

PLOP:	CALL	CINY		; Get response
	CP	CR		; See if CR
	JR	NZ,PLOP		; Loop if not

	LD	DE,CRLF		; Set up CR/LF
	JP	MESG		; Print CR/LF

ERSP:	LD	DE,RESM		; Set up retry message
	CALL	MESG		; Print it

ERLP:	CALL	CINY		; Get response
	AND	0DFH		; Convert to upper-case
	LD	E,A
	CP	ASCA		; See if 'A'
	SCF			; Set carry flag
	JR	Z,ERET		; Jump if zero (Abort)

	CP	ASCR		; See if 'R'
	JR	Z,ERET		; Jump if zero (Retry)

	SUB	'I'		; See if 'I'
	JR	NZ,ERLP		; Jump if not zero (Select again)

	; Ignore the error per the user -- set up an error return
ERET:	PUSH	AF
	LD	A,E
	CALL	OUTCN
	LD	DE,CRLF
	CALL	MESG
	POP	AF
	RET

; ----------------------------------------------------------------------
; Get a character from the console
; ---------------------------------
;

CINY:	PUSH	HL		; Save HL
	LD	L,(IY+CONIN)	; HL = RAMDATY 'conin' address
	LD	H,(IY+CONIN+1)
	CALL	CNTJP		; Call the 'conin' routine indirectly
	POP	HL		; Restore HL
	RET

; ----------------------------------------------------------------------
; Read from the disk controller
; ------------------------------
;

RLOOP1:	AND	EXEC		; See if still executing
	JR	Z,EXDN		; Jump if done

	IN	A,(FDCSTAT)	; Get status
	CP	RDRDY		; See if byte ready
	JR	Z,RDSC		; Jump if 1st byte ready

	DEC	DE		; Decrement time-out count
	LD	A,E		; See if timed out
	OR	D
	JR	Z,TMOUT		; Jump if timed out

RDIO:	IN	A,(FDCSTAT)	; Get status
	CP	RDRDY		; See if byte ready
	JR	NZ,RLOOP1	; Loop if byte not ready

RDSC:	INI			; Xfer 1st byte
	POP	DE		; Get DE
	PUSH	DE		; Save DE
RLOOP3:	LD	A,E		; See if last part of last sector
	OR	D
	JR	NZ,RLOOP2	; Jump if not last part
	DEC	B		; Decrement byte count if last

RLOOP2:	EI			; Enable interrupts
	HALT			; Wait for byte
	IN	A,(FDCSTAT)	; Get status
	AND	EXEC		; See if still executing
	JR	Z,EXDN		; Jump if not

	INI			; Xfer byte
	JR	NZ,RLOOP2	; Loop if more bytes
	DEC	E		; Decrement MSB of sector length
	JP	P,RLOOP3
	POP	DE		; Restore sector size and count
	DEC	D		; Decrement sector count
	PUSH	DE
	JP	P,RLOOP3	; Jump if not end
	EI			; Enable interrupts
	HALT			; Wait for last byte
	IN	A,(MOTOR2)	; Stop FDC
	INI			; Xfer last byte

EXDN:	POP	DE
	RET

; ----------------------------------------------------------------------
; Time out handler
; ------------------
;

TMOUT:	LD	A,8		; ??? Unknown constant '8'
	OUT	(MOTOR),A

TMLOOP:	IN	A,(MTRCHK)	; Turn motor on
	IN	A,(FDCSTAT)	; Get FDC status byte
	BIT	EXB,A		; See if executing
	JR	NZ,TMLOOP	; If still busy, loop
	JR	EXDN

; ----------------------------------------------------------------------
; Write data to the floppy disk controller
; -----------------------------------------
;

WLOOP1:	AND	EXEC		; See if still executing
	JR	Z,EXDN		; Jump if done

	IN	A,(FDCSTAT)	; Get status
	CP	WRRDY		; See if byte ready
	JR	Z,WRSC		; Jump if 1st byte ready

	DEC	DE		; Decrememt time-out count
	LD	A,E		; See if timed out
	OR	D		
	JR	Z,TMOUT		; Jump if timed out

WRIO:	IN	A,(FDCSTAT)	; Get status
	CP	WRRDY		; See if byte ready
	JR	NZ,WLOOP1	; Loop if byte not ready

WRSC:	OUTI			; Xfer 1st byte

	POP	DE		; Get DE
	PUSH	DE		; save DE

WLOOP3:	LD	A,E		; See if last part of last sector
	OR	D		
	JR	NZ,WLOOP2	; Jump if not last part
	DEC	B		; Decrement byte count if last

WLOOP2:	EI			; Enable interrupts
	HALT			; Wait for byte
	IN	A,(FDCSTAT)	; Get status
	AND	EXEC		; See if still executing
	JR	Z,EXDN		; Jump if not

	OUTI			; Transfer byte
	JR	NZ,WLOOP2	; Loop if more bytes

	DEC	E		; Decrement MSB of sector length
	JP	P,WLOOP3 	; Jump if not end

	POP	DE		; Restore sector size and count
	DEC	D		; Decrement sector count
	PUSH	DE		
	JP	P,WLOOP3	; Jump if not end

	EI			; Enable interrupts
	HALT			; Wait for last byte

	IN	A,(0F8H)	; Stop FDC
	OUTI			; Transfer last byte

	POP	DE
	RET

CNT:	DEC	E
	DEC	D
	RET	NZ
	LD	B,1
	RET

; ----------------------------------------------------------------------
; Home the disk head
; -------------------
;

HOME:	BIT	HSTWRT,(IY+DFLAG)	; Clear host active flag
	JR	NZ,HMSK			; Unless write is pending.
	RES	HSTACT,(IY+DFLAG)

HMSK:	LD	A,(IX+HSTDSK)		; Get host drive
	PUSH	AF			; Save host drive
	LD	A,(IY+SEKDSK)		; Get the drive to seek
	LD	(IX+HSTDSK),A		; Set the current drive
	CALL	SELSK			; Select drive and seek
	POP	AF			; Recall the host drive
	LD	(IX+HSTDSK),A		; Store the host drive from A
	RET

HMOK:	LD	E,0			; Set bit for home command
	JR	SELSK

WNGTRK:	CALL	CNT

SELSK:	LD	A,HMBT		; Set bit for seek command
SELHM:	PUSH	BC		; Save retry
	PUSH	DE

	LD	C,A		; Save A in C (8 code ROM read routine)
	LD	A,(IX+HSTDSK)	; Get new drive

	PUSH	AF		; Save new drive
	CALL	GDSK		; Get start of MTAB/DSKDEF for drive 0
	LD	A,(HL)		; Get motor control byte
	AND	MTRMSK1		; Mask motor control bits

MTOK:	LD	E,A		; E = Motor control bit
	INC	HL		; HL points to DSKDEF1
	POP	AF		; A = New drive

	CP	(IY+CDSK)	; See if drive changed
	JR	Z,SAME		; Jump if no change
	LD	(IY+CDSK),A	; Update current drive
	BIT	VD,(HL)		; See if virtual

	PUSH	BC		; Save code
	JR	Z,NVIRT		; Jump if not virtual

	CP	(IY+VDSK)	; New drive == VDSK?
	LD	(IY+VDSK),A	; Update VDSK to new drive

	PUSH	AF		; Save new drive
	PUSH	DE
	PUSH	HL
	CALL	NZ,VIRT		; Print "exchange disk" message 
	POP	HL		; Restore registers
	POP	DE
	POP	AF

NVIRT:	CALL	SPECFY		; Specify srt/hut and hlt/nd
	POP	BC

SAME:	BIT	DEN,(HL)	; HL points to DSKDEF1. SINGLE or DOUBLE sided
	JR	Z,SINGLE	; Jump if SINGLE

DOUBLE:	LD	A,PR40		; Load ACC with 19 tracks
	BIT	TK80,(HL)		; Check for 40 or 80 tracks
	JR	Z,TRK40		; Jump if 40

	LD	A,PR80		; Load ACC with 39 tracks
TRK40:	CP	(IX+PHYTRK)	; Compare to present track
	JR	NC,SINGLE	; Jump if track greater than max
	SET	PREC,E		; Update motor control byte

SINGLE:	LD	A,MTRMSK1
	OUT	(MOTOR),A	; Turn motor on
	IN	A,(MTRCHK)	; See if motor on
	AND	MTRMSK1		; Mast motor bits
	LD	D,A		; Save motor bits
	AND	E		; NZ if motor on, CY=0

	PUSH	AF		; Save flags
	LD	A,D
	OR	E
	OUT	(MOTOR2),A	; Set the motor control
	LD	(IY+PDSK),A	; Get motor byte
	CALL	GTRK		; Get current track from MTAB in D

	LD	A,(IX+PHYTRK)	; A = New track
	BIT	3,C		; See if HOME
	JR	Z,SK0		; Jump if HOME

NOT0:	CP	D		; New trk = old trk ?
	JR	Z,VSKP		; Jump if the track is the same one

SK0:	CALL	SEEK		; Perform drive SEEK
	POP	AF		; get flags

	SCF			; cy=1 for head settle
	PUSH	AF		; save flags
VSKP:	POP	AF		; restore flags

	INC	HL		; Inc HL to motor wait time
	CALL	WAIT		; Wait for head to settle

	POP	DE
	POP	BC		; restore retry
	RET

; ----------------------------------------------------------------------
; Virtual drive processing
; --------------------------------------   
;

VIRT:	INC	A		; Convert drive number to ASCII.  Increment
	SET	6,A		; ... and set bit 6 to make it ASCII

	LD	E,(IY+VDRVP)	; de=pntr to vdrv
	LD	D,(IY+VDRVP+1)

	LD	(DE),A		; put vdrv in vmsg.

	LD	E,(IY+VMSGP)	; de=pntr to vmsg
	LD	D,(IY+VMSGP+1)
	JP	VIRTM		; Output message

; ----------------------------------------------------------------------
; GET DISK - set hl pair as a pointer to mtab
; ---------------------------------
;

GDSK:	PUSH	IY		; Get IY into HL
	POP	HL		; Calculate pointer to MTAB for drive
	LD	DE,MTAB
	ADD	HL,DE		; Get start of MTAB

	LD	D,A		; Multiply drive num by 9
	RLCA			;	...
	RLCA			;	...
	RLCA			;	...
	ADD	A,D		;	...
	LD	E,A		;	...
	LD	D,0		;	...

				; HL = MTAB for the drive
				; Add to MTAB start to get correct
	ADD	HL,DE		;   entry for the drive
	RET

GTRK:	PUSH	HL		; Save HL - It points to DSKDEF1
	LD	DE,TROFF	; Offset of 7 to current TRACK
	ADD	HL,DE		; HL = MTAB for drive
	LD	D,(HL)		; Load current track
	POP	HL
	RET

; ----------------------------------------------------------------------
; Wait in 4 ms increments
; ------------------------
;
;       wait routine expects hl to point to dskdef1 in mtab.
;       the z and the cy flags determine if motor and/or head settle
;       delays are needed.
;       if z=1 then motor delay is needed.
;       if cy=1 then head settle delay is needed.
;       delay time = 4ms. times the values in mtab.
;

WAIT:	LD	B,(IY+PDSK)	; Get motor byte
	LD	C,MOTOR2	; Get motor port
	LD	D,(HL)		; HL is motor wait time
	LD	E,0
	INC	HL		; HL is now settle time
	JR	NC,WAIT1	; Jump if no settle delay

	IM	2		; Change interrupt mode to 2
	LD	A,ROM SHR 8 AND 0FFH ; Set interrupt table address for ROM+7F
	LD	I,A
	JR	NZ,WAIT2	; JUMP IF NOT 0
	EI			; ENABLE INTERUPTS

WLOP1:	DEC	DE		; Wait loop - Decrement count
	LD	A,D		; Check if count is zero
	OR	E
	JR	Z,WLOP2		; Jump if count is zero - TIMEOUT
	LD	C,MOTOR2	; Get motor port and time pad
	OUT	(C),B		; Keep motors running
	JR	WLOP1		; Loop

WAIT2:	LD	D,(HL)		; Get head settle delay
	EI			; Enable interrupts

WLOP2:	OUT	(C),B		; Keep motors running
	JR	WLOP2		; Jump self

SKINT:	IM	0		; Set interrupt mode back to 0
	INC	SP		; Adjust stack
	INC	SP		; Adjust stack again
	CALL	SINTR		; Clear interrupt
	LD	A,D		; Compare settle time to what's left
	CP	(HL)		;   of motor time.
	JR	NC,WLOP3	; Jump if motor bigger

	LD	D,(HL)		; DE = settle time
	JR	WLOP3

WAIT1:	RET	NZ		; Return if no motor delay needed

WLOP3:	OUT	(C),B		; Keep motors running
	OUT	(C),B		; Time pad
	NOP			; Time pad
	DEC	DE		; Decrement count
	LD	A,D
	OR	E		; See if count is zero
	JP	NZ,WLOP3	; Jump if done
	RET

; ----------------------------------------------------------------------
; Check if the FDC is ready
; --------------------------
;

RDY:	IN	A,(FDCSTAT)	; Ensure FDC is ready
	BIT	4,A		; Check the READY bit
	JR	NZ,RDY		; If not ready yet, loop
	RET			; Return when ready

; ----------------------------------------------------------------------
; Hang until FDC output buffer is empty
; --------------------------------------
;

OUTFDC:	IN	A,(FDCSTAT)	; Wait for FDC to signal
	AND	0C0H		;   that is it ready to
	CP	80H		;   accept input
	JR	NZ,OUTFDC	; Loop if not ready
	RET			; Return when ready

; ----------------------------------------------------------------------
; Hang until FDC input buffer is ready
; -------------------------------------
;

INFDC:	IN	A,(FDCSTAT)	; Wait for FDC to signal
	AND	0C0H		;   that it is ready to
	CP	0C0H		;   return output.
	JR	NZ,INFDC	; Loop if not ready
	RET			; Return when ready

; ----------------------------------------------------------------------
; Seek Routine
; -------------
;

SEEK:	CALL	OUTFDC		; Wait for FDC
	LD	A,SKCMD		; Root of SEEK and HOME command
	OR	C		; OR in bit to get HOME or SEEK
	OUT	(FDCDATA),A	; Output command
	CALL	OUTFDC		; Wait for FDC
	LD	A,MTRMSK	; Get mask for DS
	AND	(HL)		; Mask off DS
	BIT	3,C		; See if HOME
	JR	Z,HDO		; Jump if HOME
	OR	(IX+phyhd)	; OR in head bit

HDO:	OUT	(FDCDATA),A	; Output HD and DS
	BIT	3,C		; See if HOME
	RET	Z		; Return if HOME

	CALL	OUTFDC		; Wait for FDC
	LD	A,(IX+PHYTRK)	; Get track
	OUT	(FDCDATA),A	; Output track
	RET

; ----------------------------------------------------------------------
; Write the two bytes of the FDC specify command
; -----------------------------------------------
; 
TMPBC	EQU	(200H OR FDCDATA) ; B:=2, C:=FDC data port

SPECFY:PUSH	HL		; Save HL - points to DSKDEF1
	INC	HL		; Move HL to specify parameters
	INC	HL		; for current drive.
	INC	HL		; Increment to FDC bytes
	CALL	OUTFDC		; Wait for FDC
	LD	A,SPCMD		; Load SPECIFY command
	OUT	(FDCDATA),A	; Send it to the FDC
	LD	BC,TMPBC	; Parameter count

SPLP:	CALL	OUTFDC		; Wait for FDC
	OUTI			; Output to FDC SRT/HUT and HLT/ND
	JR	NZ,SPLP		; Still waiting?  Then loop
	POP	HL		; Restore HL pointer to DSKDEF1
	RET

; ----------------------------------------------------------------------
; Do a Sense Interrupt Status Command
; ------------------------------------
;

SINT:	CALL	OUTFDC		; Wait for FDC
	LD	A,SICMD		; Sense interrupt status command
	OUT	(FDCDATA),A	; Send the command to the floppy controller
	CALL	INFDC		; Wait for FDC input
	IN	A,(FDCDATA)	; Read the STO
	CALL	INFDC		; Wait for FDC input
	IN	A,(FDCDATA)	; Read the STO again
	RET

; ----------------------------------------------------------------------
; Clear Interrupt status and update current track in MTAB
; --------------------------------------------------------
;

SINTR:	CALL	SINT		; CLR IRQ status - Check command valid
	PUSH	HL		; Save HL
	PUSH	DE		; Save DE
	LD	DE,TROFF-2	; Offset to current track in MTAB
	ADD	HL,DE		; HL points to current track
	LD	(HL),A		; Update current track
	POP	DE		; Restore DE
	POP	HL		; Restore HL
	RET

; ----------------------------------------------------------------------
; Error Code Computation
; -----------------------
;       1) This routine uses the status that has been stored in STADD
;          through STADD+2 to compute an error code.  Possible error
;          codes are:
;
;               Write Protect   1
;               Seek Error      2
;               Data CRC        3
;               ID CRC          4
;               Not Found       5
;               Drive Not Ready 6
;               Sync            7
;               Equipment Check 8
;               Invalid Command 9
;               Unknown Error  10
;

WP	EQU	1
BCE	EQU	1
WC	EQU	4
DD	EQU	5
EN	EQU	7
ND	EQU	2
MA	EQU	0
CM	EQU	6
MD	EQU	0
NR	EQU	3
ORE	EQU	4
EQ	EQU	4
DER	EQU	5

ECODE:	
	LD	C,1			; Error root
	BIT	WP,(IX+STADD+1)		; See if WP
	RET	NZ

	INC	C
	BIT	BCE,(IX+STADD+2)	; See if Seek Error
	RET	NZ

	BIT	WC,(IX+STADD+2)		; See if WC
	RET	NZ

	INC	C
	BIT	DD,(IX+STADD+2)		; See if Data CRC
	RET	NZ

	INC	C
	BIT	DER,(IX+STADD+1)	; See if ID CRC
	RET	NZ

	INC	C
	BIT	EN,(IX+STADD+1)		; See if unreadable
	RET	NZ
	BIT	ND,(IX+STADD+1)
	RET	NZ
	BIT	MA,(IX+STADD+1)
	RET	NZ
	BIT	CM,(IX+STADD+2)
	RET	NZ
	BIT	MD,(IX+STADD+2)
	RET	NZ

	INC	C
	BIT	NR,(IX+STADD)		; See if Drive Not Ready
	RET	NZ

	INC	C
	BIT	ORE,(IX+STADD+1)	; See if SYNC
	RET	NZ

	INC	C
	BIT	EQ,(IX+STADD)		; See if Equipment Check
	RET	NZ

	INC	C			; Unknown error
	RET

; ----------------------------------------------------------------------
; DATA TABLES
; -----------
;

; DRIVET is per-drive indexed
DRIVET:	DB	8			; Drive A motor index
	DB	9			; Drive B motor index
	DB	7		; Drive C motor index 
	DB	10		; Drive D motor index
	DB	6		; Drive E - Virtual

RAMDATX:

RHSTDV:	DB	0
RHSTTK:	DB	0
RHSTSC:	DB	1
RSECCT:	DB	1
RRETRY:	DB	20
RHSTBF:	DW	BOOTBF
RERFLG:	DB	0
RPHYTK:	DB	0
RPHYHD:	DB	0
RIOADD:	DW	RDIO
RSECSZ:	DW	0
RSTADD:	DW	0
	DW	0
	DW	0
	DB	0

RCMDCT: DB      9

RCMDBF: DB      46H
        DB      0
        DB      0
        DB      0
        DB      1
        DB      3
        DB      5
        DB      28
        DB      0FFH

ROMDATY:	

RSEKDK:	DB	0
RSEKTK:	DB	0
RSEKSC:	DB	0
;
RSEKHT:	DB	0
;
RUNACT:	DB	0
RUNADK:	DB	0
RUNATK:	DB	0
RUNASC:	DB	0
RUNAMX:	DB	0
RSECTK:	DB	0
;
RWRTYP:	DB	0
RDFLAG:	DB	0
RTRSEC:	DB	0
RVMSGP:	DW	0
RVDRVP:	DW	0
;
RCDSK:	DB	0FFH
RPDSK:	DB	0
RVDSK:	DB	0
;
RDMADR:	DW	0
ROUTP:	DW	CRTOUT
RIMP:	DW	CRTIN

;
; Drive 1 parameter table
;

RMTAB:  DB      1
	DB      0D8H
	DB      88
	DB      5
	DB      6FH
	DB      3
	DB      5
	DB      28
	DB      0FFH

;
; Error message index table
;

ETAB:   DW	MWPROT		; Write protect
	DW	MWRTRK		; Wrong track
	DW	MDATER		; Data error
	DW	MDATER		; Data error
	DW	MNOFND		; Not found  
	DW	MNORDY		; Drive not ready
	DW	MLOSTD		; Lost data
	DW	MNORDY		; Drive not ready
	DW	MUNKER		; Unknown error

MWRTRK:	DB	'Wrong track',MSGTAG,0
MWPROT:	DB	'Write protected',MSGTAG,0
MDATER:	DB	'Data error',MSGTAG,0
MNOFND:	DB	'Not found',MSGTAG,0
MLOSTD:	DB	'Lost data',MSGTAG,0
MNORDY:	DB	'Drive not ready',MSGTAG,0
MUNKER:	DB	'Unknown error',MSGTAG,0

DMESG:	DB	CR,LF,'Disk error on drive',0A0H,0
RESM:	DB	CR,LF,'Type R to try again, A to abort, or I to ignore:'
	DB	0A0H,0
BTERR:	DB	LF,'Error on CP/M system diskette.',0
RERR:	DB	CR,LF,'Push reset to try again.',0A0H,0
SIGNON:	DB	CR,'Micro-Decision -- ROM Rev. 1.3',CR,LF
	DB	   'Copyright 1982 Morrow Designs, Inc.'
	DB	CR,LF,LF,0
TSTMSG:	DB	'Testing memory -- please stand by: ',0
ROMOK:	DB	'Memory O.K.',CR,LF,LF,0
BOOTMSG:DB	'Insert CP/M system diskette in Drive A and press [RETURN] '
	DB	BELL,0
RAMBAD:	DB	BELL,BELL,'RAM Memory error',0
ROMMSG:	DB	BELL,BELL,'ROM Memory error',0

		; Fill up the rest of the ROM with 0FFHs, except
		; for the last 4 bytes... Handled manually.

	DS	(ROM+800H-$-4),0FFH

	DB	REV		; Stick the ROM version number in there

	DB	0FFH,0FFH,0FFH 	; Pad the rest (last three).  Supposedly,
				; this was being reserved for a serial
				; number.

	END

