*******************************************************************************
*
*       Generic CBIOS for MICROMATION DOUBLER and CP/M 1.4
*
*                Last updated:    November 13, 1979
*
*	Micromation I/O Board printer routines deleted on this version
*
*******************************************************************************
TRUE		EQU	-1	!	FALSE		EQU	0
*******************************************************************************
*The following Labels may be user defined for different operating environments:

MEMSIZE		EQU	62	;This value is the same as the parameter passed
				;to the MOVCPM program.
EXTENDED	EQU	FALSE	;This is to extend the CBIOS by 640 bytes
				;If extended is TRUE then decrease MEMSIZE by 1
CONTROLLER	EQU	0F800H	;Base address of DOUBLER disk controoller
				;
STEPTIME	EQU	6	;Track to Track Steptime in milliseconds
				;SHUGART 801r = 6 ms
				;YE-DATA 174  = 4 ms
				;REMEX 4000   = 3 ms
				;MEMOREX 500  = 6 ms 
TWOSIDED	EQU	FALSE	;Set TRUE only when using Double Sided drives
				;Allows Double Density only on Double Sided
BAUD		EQU	2400	;User defined for the baud rate of the
				;ACE on the MICROMATION I/O board when used
VIDEO		EQU	FALSE	;Set TRUE if there is a Micromation VIDEO Board
				;in the system
*==============================================================================
*Unless IOBYTE is tested, any LOGICAL device may have only one PHYSICAL device
				;
DUBLIN		EQU	TRUE	;Micromation DOUBLER Serial port as CONIN
PARALIN		EQU	FALSE	;VIDEO or I/O Board Parallel port as CONIN
*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
DESTROY		EQU	TRUE	;Set TRUE for destructive backspace on DELETE
*------------------------------------------------------------------------------
DUBLOUT		EQU	TRUE	;Micromation DOUBLER Serial port as CONOUT
VIDEOUT		EQU	FALSE	;Micromation Video Board as CONOUT
*------------------------------------------------------------------------------
SERREAD		EQU	FALSE	;Micromation I/O Board Serial port as RDR:
*------------------------------------------------------------------------------
SERLPUN		EQU	FALSE	;Micromation I/O Board Serial port as PUN:
*------------------------------------------------------------------------------
SERLIST		EQU	FALSE	;Micromation I/O board Serial Port as LST:
DIABLIST	EQU	FALSE	;Micromation Printer Driver as LST:
GODBIO		EQU	FALSE	;Godbout I/O board as LST:
SSMIO		EQU	TRUE	;Solid State Music 2S+P as LST:
DPIO		EQU	FALSE	;Delta Products CPU board as LST:
*------------------------------------------------------------------------------
*Note:	If EXTENDED is TRUE then reduce MEMSIZE and the parameter passed
*	to MOVCPM by one and be sure to save 34 pages of the RAM image of
*	the Operating System.
*
*	If DIABLIST is set true without a MICROMATION VIDEO board then
*	EXTENDED must be TRUE
*
*	If TWOSIDED is set TRUE, and EXTENDED is greater than zero,
*	this CBIOS will support the following formats:
*		Single Density - Single Sided
*		Double Density - Single Sided
*		Single Density - Double Sided (on front side only)
*		Double Density - Double Sided (1 MEGABYTE per diskette)
*
*******************************************************************************
;CPM ENTRY POINTS

CBASE	EQU	(MEMSIZE-16)*1024	;Bias for Systems larger than 16K
CPMB	EQU	CBASE+2900H	;Base of CCP in CP/M for this system size
BDOS	EQU	CBASE+3106H	;Base of BDOS +6 for this system size

		;CP/M status bytes in parameter page:
IOBYTE		EQU	3	;Defines logical to physical device mapping
CURRDRIVE	EQU	4	;Defines currently logged default disk

	;HARDWARE PORT DEFINITIONS
WRCONT		EQU	CONTROLLER+600H
RDSTAT		EQU	WRCONT
WRCLK		EQU	WRCONT+1
UARTDATA	EQU	WRCONT+2
RDMARK		EQU	WRCONT+5
UARTSTAT	EQU	WRCONT+0AH
BUFF		EQU	CONTROLLER+400H	;Base addr of 64 Bytes of scratch RAM

	;RAM VARIABLE DEFINITIONS
CONTROLBYTE	EQU	BUFF+3		;RAM IMAGE OF RDSTAT OR WRCONT
TRACK		EQU	BUFF+4
PRESDSK		EQU	BUFF+5		;DRIVE ACTUALLY SELECTED
SECTOR		EQU	BUFF+0AH
DMA		EQU	BUFF+0BH	;DMA ADDRESS
DISK		EQU	BUFF+0DH	;DRIVE TO USE FOR NEXT I/O
TWOSIDE		EQU	BUFF+0FH	;SAME AS CONTROLBYTE
STEPBYTE	EQU	BUFF+10H	;STEP TIME IN MSEC
DENMAP		EQU	BUFF+16H	;FOR EACH OF 4 DRIVES,
					;00 FOR SINGLE DENSITY
					;10H FOR DOUBLE DENSITY
TRY1		EQU	BUFF+20H
RETRYCOUNT	EQU	BUFF+21H
LASTCHAR	EQU	BUFF+24H	;CONIN log for destructive backspace
SIDEMAP		EQU	BUFF+25H	;Side information for logged drives
					;(uses 4 bytes)
;Next available scratch location is BUFF+29h


		;JUMP VECTORS TO ROUTINES IN CONTROLLER PROM
HOME		EQU	CONTROLLER+3	;HOMES THE DISK
SELDSK		EQU	CONTROLLER+6	;SELECTS DRIVE POINTED TO BY C REG
					;AND LOADS HEAD
SETTRK		EQU	CONTROLLER+9	;STEPS DRIVE TO TRACK (C)
SETSEC		EQU	CONTROLLER+0CH	;SET SECTOR NUMBER
SETDMA		EQU	CONTROLLER+0FH	;SET DMA ADDRESS
DISKREAD	EQU	CONTROLLER+12H	;SECTOR READ
DISKWRITE	EQU	CONTROLLER+15H	;WRITE SECTOR
SKEWER		EQU	CONTROLLER+18H	;SKEW CALCULATOR IN PROM
SETDEN		EQU	CONTROLLER+1BH	;TEST DENSITY OF CURRENT DRIVE
WRITEPROTECT	EQU	CONTROLLER+1EH	;CHECK FOR WRITE PROTECT

IF (SERLIST OR SERLPUN OR SERREAD OR PARALIN OR DIABLIST) AND NOT VIDEO
		;Conditional assembly for Micromation I/O board follows:
ACEDAT		EQU	20H		;RCVR/XMTR PORT TO SERIAL I/O
STATA		EQU	ACEDAT+08H	;Parallel input port A STATUS of 8255
STATB		EQU	ACEDAT+0AH	;Parallel input port B STATUS of 8255
PLLDATA		EQU	ACEDAT+09H	;Parallel input port DATA of 8255
CONTROL		EQU	ACEDAT+0BH	;CONTROL port of 8255
		ENDIF		;STOP assembling for the Micromation I/O board

		IF VIDEO
		;Start conditional assembly for the Micromation VIDEO board:
VPROM		EQU	0F800H		;Base of EPROM
SCREEN		EQU	VPROM - 800H	;Base of screen memory (to F7FFh)
ACEDAT		EQU	VPROM + 400H	;Base of Memory Mapped I/O page
STATA		EQU	ACEDAT + 08H	;Parallel input port A STATUS of 8255
STATB		EQU	ACEDAT + 0AH	;Parallel input port B STATUS of 8255
PLLDATA		EQU	ACEDAT + 0DH	;Parallel input port DATA of 8255
CONTROL		EQU	ACEDAT + 0BH	;CONTROL port of 8255
VINIT		EQU	VPROM		;Initialize the screen and the 8255
PINIT		EQU	VPROM + 3	;Initialize the printer and the 8255
VIDGRAF		EQU	VPROM + 6	;Put GRAPHICS char for CONTROL CHARS
VIDSEND		EQU	VPROM + 9	;Put '^Char' for CONTROL CHARACTERS
DIABLOUT	EQU	VPROM + 0CH	;Send char to DIABLO HYTYPE II
BAUDSET		EQU	VPROM + 0FH	;Set baud rate divisor of ACE
		ENDIF			;STOP assembly for VIDEO BOARD only

IF (SERLIST or SERREAD or SERLPUN or PARALIN or DIABLIST)
	;Assembled only for serial port on MICROMATION VIDEO or I/O board
ACELC		EQU	ACEDAT + 3	;ACE LINE  CONTROL REGISTER
ACEMC		EQU	ACEDAT + 4	;ACE MODEM CONTROL REGISTER
ACELS		EQU	ACEDAT + 5	;ACE LINE  STATUS  REGISTER
ACEMS		EQU	ACEDAT + 6	;ACE MODEM STATUS  REGISTER
ACELBR		EQU	ACEDAT		;ACE LOW ORDER baud rate divisor
ACEHBR		EQU	ACEDAT + 1	;ACE HIGH ORDER  "   "     "
					;HBR & LBR valid only when DLAB is set
ACEBYTE		EQU	00000111B	;ACE default line control byte
		;	abcdefgh	;a= Divisor Latch Address Bit (DLAB)
					;b= set BREAK mode
					;c= stick parity
					;d= even parity
					;e= parity enable
					;f= Stop Bit Select (SBS)
					;g & h = word length select 0 & 1
		ENDIF			;STOP conditional assembly

*******************************************************************************
BEGIN:	ORG	CBASE + 3E00H	;Base address of this CBIOS

	JMP	BOOT
EBOOT:	JMP	WBOOT
	JMP	CONSTAT
	JMP	CONIN
	JMP	CONOUT
	JMP	LIST
	JMP	PUNCH
	JMP	READER
	JMP	HOME1		;HOME
	JMP	TEMPSELDSK	;SELDSK		
	JMP	SETTRK1		;SETTRK
	JMP	SETSEC
	JMP	SETDMA
	JMP	READ
	JMP	WRITE

*******************************************************************************
BOOT:		;GETS CONTROL AFTER COLD OR WARM BOOT

	IF	GODBIO		;START Conditional for GODBOUT I/O board
	XRA	A		;INITIALIZE THE I/O BOARD
	OUT	3		;BY SENDING A 00 TO THE STATUS PORT
	ENDIF			;STOP assembling for the Godbout only

	IF	DPIO		;UART INIT FOR DELTA PRODUCTS CPU BOARD
	MVI	A,0EH		;BRING UART TO COMAND INSTRUCTION MODE
	OUT	01		;Port 1 is UART A status
	OUT	03		;Port 3 is UART B status (also time fill)
	MVI	A,40H		;COMAND INSTRUCTION MODE - RESET UART
	OUT	01
	OUT	03
	MVI	A,0EEH		;MODE - BAUD RATE FACTOR = 16X
	OUT	01		;DISABLE PARITY, 8 BIT CHARACTER LENGTH
	OUT	03		;
	MVI	A,37H		;COMAND INSTRUCTION MODE-TRANSM ENABLE,
	OUT	01		;DATA TERM READY, RECEIVE ENABLE, ERROR
	OUT	03
	ENDIF			;RESET, REQUEST TO SEND (IF NEEDED)

	IF	DIABLIST and (EXTENDED or VIDEO)
	CALL	PINIT		;Initialize the printer and 8255 on the	
	ENDIF			;Micromation I/O board

	IF	VIDEO	
		;Conditional assembly for Micromation VIDEO board follows
DIVISOR	EQU	12500/(BAUD/10)	;This divides the system clock in ACE
	LXI	B,DIVISOR	;The VPROM routine gets the divisor in B & C
	CALL	BAUDSET		;Set the Baud rate of the ACE on VIDEO board
	ENDIF			;STOP assembling for Micromation VIDEO board

	IF	(SERLIST OR SERREAD OR SERLPUN OR PARALIN) AND NOT VIDEO
		;Conditional assembly for Micromation I/O board follows
	MVI	A,ACEBYTE OR 80H;Turn on the Divisor Latch Address Bit
	OUT	ACELC		;Put it in the line control register
DIVISOR	EQU	12500/(BAUD/10)	;This divides the system clock in ACE
	MVI	A,DIVISOR SHR 8	;Store the high order baud rate
	OUT	ACEHBR		; divisor in it's register
	MVI	A,DIVISOR AND 0FFH
	OUT	ACELBR		;Do the same for the low order byte
	MVI	A,ACEBYTE	;Turn off the DLAB and setup the ACE
	OUT	ACELC		; for normal operation.
	ENDIF			;STOP assembling for the Micromation I/O board

	IF 	PARALIN and not (VIDEO OR DIABLIST)
	MVI	A,97H		;SETUP the 8255 on the I/O board for mode 1
	OUT	CONTROL		;to operate with the parallel input keyboard
	MVI	A,9		;
	OUT	CONTROL		;
	INR	A		;
	OUT	CONTROL		;
	ENDIF			;STOP Conditional for 8255 initialization


	MVI	A,STEPTIME	;Get the user defined steptime
	STA	STEPBYTE	;save it for future use
	JMP	SETUP		;Setup page 0 parameters and start the system

*******************************************************************************
;READ DISKETTE FOR TWO TRACKS, STARTING AT BOOT LOADER POSITION

WBOOT:	LXI	SP,80H		;GETS CONTROL ON A WARM START
	MVI	C,0
	CALL	SELDSK		;SELECT DRIVE A TO REBOOT
	CALL	HOME
	LXI	H,CPMB-256	;Will increment by 128 before first READ
	SHLD	DMA		;Start 128 bytes low to account for Boot sector
RDTRK:	MVI	C,0		;Start at sector 0 on both tracks
RDSEC:	MOV	A,C		;C IS SECTOR NUMBER
	CPI	26		;End of the first track?
	JZ	NXTTRK		;
	LXI	D,128		;INCREMENT DMA ADDRESS
	LHLD	DMA		;
	DAD	D		;
	MOV	A,H		;Get the current page boundary
	CPI	LAST/100H+1	;Don't read past the top of available RAM
	JNC	NXTTRK		;Finish the Warmboot so the next Read won't
	INR	C		;INCREMENT SECTOR NUMBER
	CALL	SETSEC		;
	PUSH	B		;
	SHLD	DMA		;STORE DMA ADDRESS
	DI
	CALL	READ		;READ THE DATA
	ORA	A		;SUCCESSFUL READ SETS Z
	EI			;
	JNZ	WBOOT		;ON READ FAILURE TRY AGAIN
	POP	B		;RECALL SECTOR
	JMP	RDSEC

NXTTRK:	LDA	TRACK
	ORA	A
	JNZ	SETUP		;STOP AT TRACK 1
	MVI	C,1		;SEEK 1 IF NOT
	CALL	SETTRK
	JMP	RDTRK

SETUP	MVI	A,0C3H		;SET UP PARAMETERS ON PAGE 0
	STA	0
	LXI	H,EBOOT
	SHLD	1
	STA	5
	LXI	H,BDOS
	SHLD	6

	LDA	CURRDRIVE	;RESELECT THE DRIVE THAT WAS ACTIVE
	MOV	C,A		;BEFORE THE WARM START
	JMP	CPMB		;Jump to the CCP to restart Operating System

*******************************************************************************
HOME1:		;HOMES SELECTED DRIVE (INDICATED IN "DISK")
	CALL	TESTSELDSK	;SELECT REQUESTED DRIVE
	IF	EXTENDED and TWOSIDED
	CALL UPSIDE
	ENDIF
	JMP	HOME		;HOME THE DRIVE

TEMPSELDSK	;SETS UP DRIVE TO BE SELECTED FOR NEXT DISK OPERATION
	MOV	A,C		;C CONTAINS REQUESTED DRIVE NO.
	STA	DISK		;(DISK)=(C)

	IF	TWOSIDED	;Begin Conditional for Double Sided disk drives
	LDA	TWOSIDE		;Get the RAM Image of the controlbyte
	ORI	80H		;Turn on the BACKSIDE bit
	STA	TWOSIDE		;Save the Image
	ENDIF			;STOP Conditional assembly for Double Sided

	LXI	H,DENMAP
	MVI	B,0
	DAD	B
	MOV	A,M		;GET DENSITY OF SELECTED DRIVE
	ORA	A
	JZ	SDSW		;SET UP BDOSI
	JMP	DDSW

TESTSELDSK	;IF REQUESTED DRIVE ("DISK") IS NOT THE SAME AS
		;SELECTED DRIVE ("PRESDSK") THIS ROUTINE CALLS SELDSK
		;OTHERWISE, IT JUST RETURNS
	LDA	DISK
	MOV	C,A
	LDA	PRESDSK
	CMP	C
	RZ
	JMP	SELDSK

SETTRK1		;STEPS REQUESTED DRIVE TO TRACK (C)
	PUSH	B		;SAVE TRACK NUMBER
	CALL	TESTSELDSK	;SELECT REQUESTED DRIVE

	IF	EXTENDED AND TWOSIDED
	CALL	UPSIDE		;Update the side table
	ENDIF

	POP	B		;RESTORE TRACK NUMBER
	CALL	SETTRK		;STEP DRIVE TO TRACK
				;IF DENSITY WAS TESTED, CARRY WAS SET
	RC			;IF NO DENSITY TEST, RETURN
	JNZ	SDSW		;ELSE, SET UP BDOSI
	JMP	DDSW

	IF	Extended and TWOsided
				;This is for supporting all four formats:
UPSIDE:	LDA	RDMARK		;This subroutine saves the
RDYYET:	LDA	RDSTAT		;value of the side status
	ANI	80H		;for each of 4 drives
	JNZ	RDYYET		;
	LDA	RDSTAT		;Ignore the rest of the byte
	PUSH	PSW		;Save it for a tad
	LXI	H,SIDEMAP	;Point to the map
	LDA	PRESDSK		;This drive is really selected
	MOV	C,A		;
	MVI	B,0		;
	DAD	B		;Add the drive # to map pointer
	POP	PSW		;Get back the side data
	MOV	M,A		;Put it in the map
	RET
	ENDIF			;STOP assembling for Side uptate only

SDSW:	;Single Density parameter SWap puts SINGLE table in BDOS
	LXI	H,SKEWTABLE	;Use the skew table in the BDOS area if
	CALL	SKMO		;Only 26 sectors/track
	LXI	H,SINGLE	;Use standard setup
	JMP	PARMOV		;Move in the SINGLE parameter block

DDSW:	;DOUBLE DENSITY parameter SWap puts DOUBLE or QUAD table in BDOS
	LXI	H,SKEWROUTINE	;for more than 26 sectors
	CALL	SKMO		;Moving new table TO BDOS area

	IF	not TWOSIDED
	;Begin Conditional assembly for Standard single sided drives
	LXI	H,DOUBLE	;DOUBLE density swap
	ENDIF			;STOP Conditional assembly for Single Sided

	IF	TWOSIDED and not EXTENDED
	;Begin Conditional assembly for Double Sided disk drives
	LXI	H,QUAD		;Quad density swap
	ENDIF			;STOP Conditional assembly for Double Sided

	IF	TWOSIDED and EXTENDED	;Support Single/Double//density/sided
	CALL	SIDETEST	;Two sided? sets Z if so
DOUBSW	LXI	H,DOUBLE	;DOUBLE density swap
	JNZ	PARMOV		;If double sided/double density
QUADSW	LXI	H,QUAD		;QUAD density swap
	ENDIF			;STOP Conditional assembly FOR FOUR FORmats

PARMOV	LXI	D,BDOS+34H	;This is where the paramater
	MVI	B,6		;block resides in CP/m
	JMP	MOVE		;

SKMO	LXI	D,BDOS+9	;USE THE SKEW TABLE IN BDOS
	MVI	B,7

MOVE	MOV	A,M		;This subroutine moves data
	STAX	D		;from the address in H+L
	INX	H		;to the address in D+E
	INX	D		;for B bytes (C is ignored)
	DCR	B
	JNZ	MOVE
	RET

SINGLE:	DB	26		;26 sectors/track
	DB	63		;Sets up directory for 64 entries
	DB	3		;There are 2^3=8 sectors/block
	DB	7		;
	DB	242		;CP/M can address 242 blocks
	DB	0C0H		;BLOCKMASK byte. 
	;1100 0000= 2 blocks * 8 sectors/block= 16 sectors for the directory
	;4 entries/sector * 16 sectors = 64 entries

	IF	(EXTENDED and TWOSIDED) or not TWOSIDED
		;Begin Conditional assembly for Standard single sided drives
;Note: 64 entries for compatability with OLD DOUBLE format of 1 block directory
DOUBLE:	DB	52		;
	DB	63		;Set to 64 entries. Up to 128 entries. WOW
	DB	4		;2^4=16 sectors/block
	DB	15		;
	DB	242		;
	DB	0C0H		;2 blocks * 16 sec/blk = 32 sec/directory
	ENDIF			;STOP Conditional assembly for Single Sided

	IF	TWOSIDED	;Begin Conditional for Double Sided disk drives
QUAD:	DB	104		;Sectors 1-52 on side 0, 53-104 on side 1
	DB	91		;Now 92 entries. Up to 128 entries available
	DB	5		;2^5=32 sectors/block
	DB	31		;
	DB	242		;
	DB	80H		;1 block for the directory = 32 sectors
				;Note: as more directory entries are allowed,
				; the amount of time for login of a disk grows.
	ENDIF			;STOP Conditional assembly for Double Sided

	IF	EXTENDED AND TWOSIDED
SIDETEST:			;CHECK the side bit in SIDEMAP for QUAD format
	LXI	H,SIDEMAP	;point to the 4 bytes A,B,C,D
	LDA	DISK		;get the drive now selected
	MOV	C,A		;
	MVI	B,0		;get the proper sidetest byte
	DAD	B
	MOV	A,M		;
	ANI	40H		;sets zero flag if double sided
	RET
	ENDIF			;STOP Conditional assembly for SIDETEST only

SKEWTABLE:
	LXI	H,(BDOS-6)+1AH	;This command is inserted into BDOS
	MVI	B,0		;to use the tranlate table for
	DAD	B		;logical to physical sector translation
	MOV	C,M		;(NORMAL MODE)

SKEWROUTINE:
	CALL	SKEWER		;This command string is inserted in the BDOS
	MVI	B,0		;to use the C-PROM "SKEW" routine for
	DAD	B		;the logical/physical sector translation
	NOP			;(used for more than 26 sectors)

*******************************************************************************
;ERROR CHECKING READ AND WRITE RTNS FOR MICROMATION CBIOS

CLEAR:	XRA	A
	STA	TRY1
	STA	RETRYCOUNT

	IF	TWOSIDED	;Begin Conditional for Double Sided disk drives
	LDA	TWOSIDE		;Get the RAM Image of WRCONT for 2sided drives
	STA	CONTROLBYTE	;Save it as the universal default control byte
	ENDIF			;STOP Conditional assembly for Double Sided

	RET			;Return from the CLEAR subroutine

READ:	CALL	TESTSELDSK	;SELECT REQUESTED DRIVE
	CALL	CLEAR
REREAD:	DI
	CALL	DISKREAD	;READ SECTOR
	CALL	OPTEST		;Check the results of the read
	RZ			;Retuun to CP/M with the info
	JMP	REREAD		;Uh Oh, Bad READ. Try Again.

WRITE:	CALL	TESTSELDSK	;SELECT REQUESTED DRIVE
	CALL	WRITEPROTECT	;SETS Z IF DRIVE IS PROTECTED
	LDA	RDSTAT		;GET THE DRIVE STATUS
	ANI	4		;TEST THE PROTECT BIT
	JNZ	SKIP
	LXI	H,PROTECT	;Point to the write protect message
	CALL	PRINT		;PRINT it on the screen
	CALL	CONIN		;WAIT FOR A CHAR
	CPI	3		;REBOOT IF A ^C
	JZ	WBOOT
	JMP	WRITE		;TRY THE WRITE PROTECT AGAIN

PROTECT	DB	0DH,0AH,'Protect$'

SKIP	CALL	CLEAR
REWRITE	DI
	CALL	DISKWRITE	;WRITE SECTOR
	CALL	OPTEST		;Z set if unrecoverable
	RZ			;Return to CP/M with results
	JMP	REWRITE		;RETRY 50 TIMES

OPTEST	EI			;READ and WRITE set Z if good, C if track eror
	MVI	A,0		;Return a 0 for a good disk operation
	RZ			;GOOD JOB, get back to CP/M
	CALL	ERROR		;INCREMENT RETRYCOUNT
	MVI	A,01H		;CP/M CONVENTION FOR PERMANENT ERROR
	RET

ERROR	LXI	H,RETRYCOUNT
	INR	M		;INCREMENT RETRYCOUNT
	JC	TRACKERROR	;READ AND WRITE SET C ON TRACK ERROR
	MOV	A,M
	CMA
	ANI	20H		;32 TRIES?
	RNZ

TRACKERROR:
	LDA	TRACK		;ELSE
	MOV	C,A		;GET TRACK IN C
	MOV	A,M
	CPI	50		;ALLOW 50 TOTAL ERRORS
	RZ			;IF GREATER THAN 50, RETURN A FAILURE
	PUSH	B
	CALL	HOME		;Home the head to track 0
	POP	B
	CALL	SETTRK		;RESEEK TO CORRECT TRACK
	ORI	0FFH		;RETRY
	RET

*******************************************************************************
;Beginning of Peripheral Driver Subroutines:

RUBOUT	EQU	7FH
BS	EQU	8H		;BACKSPACE

	IF	DUBLIN		;Console INPUT routines for the UART on DOUBLER
CONSTAT LDA	UARTSTAT
	ANI	2
	MVI	A,0FFH
	RNZ
	XRA	A
	RET

CONIN:	CALL	CONSTAT
	JZ	CONIN
	LDA	UARTDATA
	ANI	7FH
	ENDIF			;Stop assembling for DOUBLIN only

	IF	PARALIN and not VIDEO	
	;BEGIN assembly for parallel input from the Micromation I/O board
CONSTAT	IN	STATB		;Read the STATUS port B of the 8255
	ANI	2		;Check the INPUT Buffer FULL bit
	RZ			;Return with 0 in accumulator means Data Ready
	MVI	A,0FFH		;Otherwise return with FF
	RET

CONIN:	CALL	CONSTAT		;Is there a character waiting?
	JZ	CONIN		;Loop until ready
	IN	PLLDATA		;Get the BYTE from the parallel port
	ANI	07FH		;and STRIP the Parity
	ENDIF			;STOP conditional assembly for I/O board

	IF	PARALIN and VIDEO
	;BEGIN assembly for parallel input from the Micromation VIDEO board
CONSTAT	LDA	STATB		;Read the STATUS port B of the 8255
	ANI	2		;Check the INPUT Buffer FULL bit
	RZ			;Return with 0 in accumulator means Data Ready
	MVI	A,0FFH		;Otherwise return with FF
	RET
CONIN:	CALL	CONSTAT		;Is there a character waiting?
	JZ	CONIN		;Loop until ready
	LDA	PLLDATA		;Get the BYTE from the parallel port
	ANI	07FH		;and STRIP the Parity
	ENDIF			;STOP conditional assembly for I/O board

	IF	DESTROY		;Assemble only if destructive backspace
	STA	LASTCHAR	;Keep a log of the input
	ENDIF			;STOP Conditional assembly

	RET			;Go back to CP/M with the character
*------------------------------------------------------------------------------
*	Put any new CONIN routines here.
*------------------------------------------------------------------------------
;CONsole OUTput subroutines follow:
CONOUT: 
	IF	DESTROY		;START conditional assembly
	LDA     LASTCHAR	;This routine is for a destructive backspace
	CPI     RUBOUT
	JNZ	CHAROUT
	LXI	H,ERASE		;Point to the string to erase a character
	JMP	PRINT		;PRINT it
ERASE	DB	BS,' ',BS,'$'
        ENDIF			;STOP conditional assembly

	IF	DUBLOUT		;START Conditional for DOUBLER UART as CONOUT:
CHAROUT	LDA	UARTSTAT
	ANI	01
	JZ	CHAROUT
	MOV	A,C
	STA	UARTDATA
	STA	LASTCHAR
	RET
	ENDIF			;Stop conditional assembly for DUBLOUT

	IF	VIDEOUT		;START Conditional for MM Video board as CONOUT
CHAROUT	MOV A,C			;CONOUT gets character in register C from CP/m
	PUSH	H
	CALL VIDSEND		;Call the screen output routine in PROM
	POP	H
	RET
	ENDIF			;Stop assembling for VIDEOUT

PRINT	MOV	A,M		;GET the character to print
	CPI	'$'		;Is this the end of the string?
	RZ			;Return if done
	MOV	C,A		;CHAROUT gets the character in register C
	CALL	CHAROUT		;Put the character on the screen
	INX	H		;point to the next character
	JMP	PRINT		;Repeat the loop

*******************************************************************************
READER:				;CP/M RDR: Logical Device Drivers
*------------------------------------------------------------------------------
	IF	SERREAD AND NOT VIDEO
		;Begin conditional assembly of MM I/O board's serial port code
	IN	ACELS		;GET THE ACE STATUS
	ANI	0FH		;Look at Rcvr status
	CPI	1		;data ready w/ no erors?
	JNZ	READER		;if not then wait
	IN	ACEDAT		;Get the byte from RCVR buffer
	ANI	7FH		;and strip the parity
	RET
	ENDIF

	IF	SERREAD AND VIDEO
		;Begin conditional assembly of VIDEO board's serial port code
	LDA	ACELS		;GET THE ACE STATUS
	ANI	0FH		;Look at Rcvr status
	CPI	1		;data ready w/ no erors?
	JNZ	READER		;if not then wait
	LDA	ACEDAT		;Get the byte from RCVR buffer
	ANI	7FH		;and strip the parity
	RET
	ENDIF
*------------------------------------------------------------------------------
* Insert any other conditional assembly for READER here.
*
*******************************************************************************
PUNCH:				;CP/M PUN: Logical device Drivers:
*------------------------------------------------------------------------------
	IF	SERLPUN AND NOT VIDEO
		;Begin conditional assembly for MM I/O board serial out
	IN	ACELS		;Get the line status from ACE
	MOV	B,A		;Save the buffer empty flag in B
	IN	ACEMS		;Gut the ACE 'modem' status
	ANA	B		;AND the two bytes together
	ANI	20H		;Look at XMTR empty AND Data Set Ready flags
	JZ	PUNCH		;Wait for both to be true
	MOV	A,C		;Get the character ready to go out
	OUT	ACEDAT		;and send it to the RCVR buffer of the ACE
	ENDIF			;STOP assembling for the Micromation I/O board

	IF	SERLPUN AND VIDEO
		;Begin conditional assembly for MM VIDEO board serial out
	LDA	ACELS		;Get the line status from ACE
	MOV	B,A		;Save the buffer empty flag in B
	LDA	ACEMS		;Gut the ACE 'modem' status
	ANA	B		;AND the two bytes together
	ANI	20H		;Look at XMTR empty AND Data Set Ready flags
	JZ	PUNCH		;Wait for both to be true
	MOV	A,C		;Get the character ready to go out
	STA	ACEDAT		;and send it to the RCVR buffer of the ACE
	ENDIF			;STOP assembling for the Micromation I/O board

	RET			;Always return to CP/m from RDR: or PUN:
*------------------------------------------------------------------------------
* Insert any other conditional assembly for PUNCH here.
*
*******************************************************************************
LIST:
	IF not (GODBIO or SSMIO or DPIO or SERLIST or DIABLIST)
	RET			;Then return because it is not supported
	ENDIF

	IF	GODBIO		;Conditional for the Godbout I/O board follows:
	IN	3
	ANI	41H		;BIT 0 IS TRANSMITTER BUFFER EMPTY
	CPI	1		;BIT 6 IS -DATA SET READY FROM PRINTER
	JNZ	LIST		;NOT READY YET
	MOV	A,C		;LIST GETS CHARACTER IN C REG.
	OUT	2		;DATA PORT
	RET
	ENDIF			;STOP assembling for the Godbout only

	IF	SERLIST AND NOT VIDEO
		;Begin conditional assembly for MM I/O board serial out
	IN	ACELS		;Get the line status from ACE
	MOV	B,A		;Save the buffer empty flag in B
	IN	ACEMS		;Gut the ACE 'modem' status
	ANA	B		;AND the two bytes together
	ANI	20H		;Look at XMTR empty AND Data Set Ready flags
	JZ	LIST		;Wait for both to be true
	MOV	A,C		;Get the character ready to go out
	OUT	ACEDAT		;and send it to the RCVR buffer of the ACE
	RET
	ENDIF			;STOP assembling for the Micromation I/O board

	IF	SERLIST AND VIDEO
		;Begin conditional assembly for MM VIDEO board serial out
	LDA	ACELS		;Get the line status from ACE
	MOV	B,A		;Save the buffer empty flag in B
	LDA	ACEMS		;Gut the ACE 'modem' status
	ANA	B		;AND the two bytes together
	ANI	20H		;Look at XMTR empty AND Data Set Ready flags
	JZ	LIST		;Wait for both to be true
	MOV	A,C		;Get the character ready to go out
	STA	ACEDAT		;and send it to the RCVR buffer of the ACE
	RET
	ENDIF			;STOP assembling for the Micromation I/O board

	IF	SSMIO		;LIST ROUTINE FOR SSM 2P+2S I/O BOARD
	IN	2
	ANI	81H		;PRINTER READY AND TX READY
	CPI	80H
	JNZ	LIST
	MOV	A,C

	CPI	0AH		;FOR AN AUTO LF DEVICE
	RZ

	OUT	3
	RET
	ENDIF

	;LIST ROUTINE FOR DELTA PRODUCTS CPU BOARD WITH 8251 UART
	IF	DPIO
	IN	01		;GET PRINTER STATUS
	ANI	81H		;PRINTER READY AND TX READY
	CPI	81H		;UART READY, DATA SET READY (BOTH)
	JNZ	LIST		;NOT READY, WAIT TILL READY
	MOV	A,C
	OUT	00		;PRINTER READY, OUTPUT DATA BYTE
	RET
	ENDIF			;STOP conditional assembly

	IF	DIABLIST AND VIDEO
	JMP	DIABLOUT
	ENDIF

	IF	DIABLIST and EXTENDED and not VIDEO
;	JMP	TTY
	RET			;Not available with this version of the CBIOS
	ENDIF
*The Printer Driver routines are available only with a MICROMATION I/O board.

*******************************************************************************
LAST	EQU	$		;Must not be past top of available RAM

LENGTH	EQU	LAST-BEGIN	;Length of this version of the CBIOS
;Note that the LENGTH must be less than 480h to fit on the disk!

OFFset	EQU	1E80H-BEGIN	;Negative offset to read CBIOS to 1E80 with DDT

