DataMuseum.dk

Presents historical artifacts from the history of:

Christian Rovsing CR7, CR8 & CR16 CP/M

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Christian Rovsing CR7, CR8 & CR16 CP/M

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦4ede929c2⟧ TextFile

    Length: 57088 (0xdf00)
    Types: TextFile
    Names: »CR8B5MB.A86«

Derivation

└─⟦98758eebd⟧ Bits:30004394 CP/M-86 sources for adaptation to CR8 release 1
    └─ ⟦this⟧ »CR8B5MB.A86« 

TextFile


	TITLE 'CR8 - CP/M-86 BIOS VERS. 1.0'
	PAGESIZE 56
	PAGEWIDTH 111


;	AUTHOR:	LARS WINTHER			DATE: 830208
;******************************************************************************
;	CHRISTIAN ROVSING A/S, CP/M-86 BASIC I/O MODULE FOR MP2 BOARD,
;	USING SEAGATE ST-506 DRIVE AND TANDON MINI FLOPPY DISK DRIVES.
;******************************************************************************
;
;
;	THIS CP/M-86 BIOS IS SETUP FOR CP/M-86 ON A MP2 BOARD.
;	CONSOLE,PRINTER  AND FLOPPY DISK I/O IS HANDLED VIA Z80 BASED
;	DRIVERS USING THE ONBOARD FLOPPY AND SERIAL I/O CONTROLLER CHIPS.
;	WINCHESTER DISK I/O IS OBTAINED THRU A MICRO COMPUTER SYSTEMS
;	MSC 9205 WINCHESTER WINCHESTER DISK CONTROLLER.
;
;	THE WINCHESTER DISK CONTROLLER FIRMWARE SUPPORTS A PARAMETER
;	BLOCK FORMAT SIMILAR TO AN INTEL SBC 206 DISK CONTROLLER WITH 
;	A MODIFIED SET OF PARAMETERS THAT MAP TO THE CHARACTERISTICS OF
;	THE SEAGATE ST-506  5 1/4 INCH WINCHESTER DRIVE.
;	THE CONTROLLER / DRIVE CHARACTERISTICS ARE MAPPED AS FOLLOWS:
;
;		153 CYLINDERS
;		17 RECORDS PER CYLINDER
;		FOUR HEADS/SURFACES
;		512 BYTE RECORDS
;		CP/M-86 MAPS LOGICAL DRIVES A: TO D: TO THE FOUR HEADS
;
;	THE INCLUSION OF THE MINI FLOPPY DRIVER COMMUNICATION WITHIN THE
;	BIOS IS ALWAYS ENABLED. ACCESS AND CONTROL OF THE MINI FLOPPY WILL 
;	BE THROUGH THE DISK CONTROLLER CIRCUITRY OF THE MP2 BOARD 
;	USING Z80 BASED DRIVERS. 
;	THIS BIOS CONFIGURATION WILL SUPPORT ONE AND OPTIONALLY TWO
;	MINI FLOPPY DRIVES.
;
	EJECT
;	MINI FLOPPY FORMAT IS AS FOLLOWS:
;
;		77 TRACKS
;		16 RECORDS PER TRACK
;		TWO HEADS/SIDES
;		256 BYTE RECORDS
;		CP/M-86 MAPS LOGICAL DRIVE E: TO THE MINI FLOPPY.
;
;
;	BIOS ACCESS TO THE MINI FLOPPY IS DONE IN REAL PAIRS OF 256 BYTE
;	SECTORS TO AND FROM THE 512 BYTE HOST BUFFER.
;	REAL ACCESS TO THE SECTOR PAIRS IS AS FOLLOWS:
;
;	       1,2; 5,6; 9,10; 13,14; 3,4; 7,8; 11,12; 15,16;
;
;	MAPPING OF THE HARD DISK IS DONE TO ALLOW THE FIRST TWO TRACKS OF
;	EACH DISK SURFACE TO BE SYSTEM TRACKS. THIS ALLOWS HARD DISK 
;	BOOTING FROM ANY SURFACE SHOULD ANOTHER SURFACE GET "WIPED OUT"
;	FOR SOME STRANGE REASON. THE FIRST SECTOR OF TRACK 0 CONTAINS
;	BOOT TIME CONFIGURATION INFORMATION INCLUDING THE BASE ADDRESS 
;	AND THE LENGTH OF CP/M-86 + BIOS IN BYTES, AND THE SYSTEM ENTRY POINT.
;	SECTOR SIZE ON THE HARD DISK IS 512 BYTES SO THIS BIOS DOES SECTOR
;	DEBLOCKING TO THE 128 BYTE LOGICAL BLOCK SIZE REQUIRED BY CP/M-86.
;
;
;	MAPPING OF THE MINI FLOPPY ALLOWS THE FIRST TWO TRACKS TO BE
;	SYSTEM TRACKS ALSO. NOTE THAT THIS BIOS WILL NEVER, IN GENERAL,
;	ACCESS THESE TRACKS FOR NORMAL OPERATIONS. THE SYSTEM CONTAINED
;	ON THOSE TRACKS WOULD BE ANOTHER "MINI-FLOPPY ONLY"  BIOS THAT
;	OPERATES MINI FLOPPY DISKS ONLY. IN EITHER CASE BOTH HARD DISK AND
;	THE MINI FLOPPY ONLY BIOS'S WOULD RETAIN THE SAME DISK FORMAT
;	CONFIGURATION FOR THE DATA TRACKS.
;
	EJECT
;
;DEFINE TRUE AND FALSE ASSEMBLY PARAMETERS
;
TRUE	EQU	-1			;DEFINE TRUE
FALSE	EQU	NOT TRUE		;DEFINE FALSE
;
;
;CP/M-86 SYSTEM PARAMETERS
;
BDOS	EQU	0B06H			;BDOS ENTRY POINT
BIOS	EQU	2500H			;START OF BIOS JUMP VECTOR TABLE
SYSINT	EQU	224			;SYSTEM CALL INTERRUPT
;
;
;VERSION NUMBER AND DATE
;
VERSION	EQU	10			;VERSION NUMBER
MONTH	EQU	02			;MONTH
DAY	EQU	08			;DAY
YEAR	EQU	83			;YEAR LAST TWO DIGITS
;
;
;ASCII CHARACTER DEFINITIONS
;
BS	EQU	008H			;ASCII BACK SPACE CHARACTER
LF	EQU	00AH			;ASCII LINE FEED CHARACTER
CR	EQU	00DH			;ASCII CARRIAGE RETURN CHARACTER
ESC	EQU	01BH			;ASCII ESCAPE CHARACTER
RUBOUT	EQU	07FH			;ASCII RUBOUT CHARACTER
;
;
;BDOS CONSTANTS ON ENTRY TO "WRITE"
;
WRALL	EQU	0			;WRITE TO ALLOCATED BLOCK
WRDIR	EQU	1			;WRITE TO DIRECTORY
WRUAL	EQU	2			;WRITE TO UNALLOCATED BLOCK
;
	EJECT
;******************************************************************************
;	CP/M-86 BIOS DISK CONFIGURATION SELECTION OPTIONS
;******************************************************************************
;
DEFDSK	EQU	00H			;DEFAULT BOOTED DISK
					;  00=A:
					;  01=B:
					;  02=C:
					;  03=D:
;
MINISEL	EQU	04H			;DRIVE SELECT DESIGNATOR FOR THE FIRST
					;MINI FLOPPY DISK DRIVE. 04=E:, 05=F:
;
ONEMINI	EQU	TRUE			;TRUE IF ONLY ONE MINI FLOPPY SELECTED
					;..FALSE IF TWO MINI'S DESIRED
;
;
;CP/M-86  TO HOST DISK CONSTANTS
;
BLKSIZ	EQU	2048			;CP/M-86 ALLOCATION SIZE
HSTSIZ	EQU	512			;HOST DISK SECTOR SIZE
;
HDSPT	EQU	17			;HOST HARD DISK SECTORS/TRACK
;
FDSPT	EQU	8			;HOST MINI FLOPPY DISK 256 BYTE SECTOR
					;PAIRS PER TRACK
HSTBLK	EQU	HSTSIZ/128		;CP/M-86 SECTS/HOST BUFF
;
;CPMSPT	EQU	HSTBLK * HSTSPT		;CP/M-86 SECTORS/TRACK
;					;THIS VALUE CHANGES ACCORDING TO
;					;MINI OR HARD DISK.
;
;
;SECTOR SKEW INTERLACE FACTOR
;
SKEW	EQU	00			;SECTOR SKEW FACTOR
;
SECMSK	EQU	03			;SECTOR MASK
SECSHF	EQU	02			;SECTOR SHIFT FACTOR (LOG2(SECMSK))
;	
	EJECT
;******************************************************************************
;	INTER-CPU COMMUNICATION CONSTANTS
;******************************************************************************
;
;
IFTOFF	EQU	8020H			;INTERFACE TABLE OFFSET IN Z80 RAM
CHANNEL	EQU	40H			;NBR OF BYTES PR INTERFACE CHANNEL
QUEUE	EQU	10H			;NBR OF BYTES PR QUEUE
;
TXE	EQU	0*QUEUE			;OFFSET IN CHANNEL TO TX EMPTY QUEUE
TXF	EQU	1*QUEUE			;OFFSET IN CHANNEL TO TX FULL QUEUE
RXF	EQU	2*QUEUE			;OFFSET IN CHANNEL TO RX FULL QUEUE
RXE	EQU	3*QUEUE			;OFFSET IN CHANNEL TO RX EMPTY QUEUE
;
BC	EQU	01			;OFFSET IN QUEUE TO BUFFER COUNT
FB	EQU	02			;OFFSET IN QUEUE TO "FIRST" POINTER
LB	EQU	04			;OFFSET IN QUEUE TO "LAST" POINTER
SF	EQU	06			;OFFSET IN QUEUE TO SLAVE FLAG
MF	EQU	08			;OFFSET IN QUEUE TO MASTER FLAG
;
RETQ	EQU	-2			;OFFSET FROM BUFFER POINTER TO RETURN Q
CHAIN	EQU	00			;OFFSET FROM BUFFER POINTER TO CHAIN
BYTEC	EQU	02			;OFFSET FROM BUFFER POINTER TO BYTECNT
DPTR	EQU	04			;OFFSET FROM BUFFER POINTER TO DATAPTR
;
CONCHA	EQU	CHANNEL*2		;CONSOLE CHANNEL OFFSET IN I/F TABLE
CONOPC	EQU	0FFH			;CONSOLE DATA OPCODE
;
LPCHA	EQU	CHANNEL*3		;LINE PRINTER CH. OFFSET IN I/F TABLE 
LPOPC	EQU	0FFH			;LINE PRINTER DATA OPCODE
;
ENTDLDU	EQU	03H			;BUFFER TYPE FOR ENTIRE DATA LDU
ENTCLDU	EQU	07H			;BUFFER TYPE FOR COMMAND/STATUS LDU
;
OPENOPC	EQU	04H			;OPEN OPCODE
OPFORM	EQU	10H			;SERIAL OPEN PARAMETER FORMAT CODE
;
CBLENGTH	EQU	20H		;MAX BYTECOUNT IN SERIAL BUFFERS
LBLENGTH	EQU	20H		;...DEFINED IN SCRIMP/M
;
	EJECT
;
;FLOPPY DISK INTERFACE DRIVER CONSTANTS
;
FDCHA	EQU	CHANNEL*4		;FLOPPY CHANNEL OFFSET IN I/F TABLE
COMOPC	EQU	09H			;COMMAND OPCODE
RESTORE	EQU	00H			;RESTORE COMMAND CODE
READFD	EQU	01H			;READ DISK COMMAND CODE
WRITEFD	EQU	02H			;WRITE DISK COMMAND CODE
WRTRACK	EQU	03H			;FORMAT TRACK COMMAND CODE 
;
CPUSYNC	EQU	0FDH			;FLOPPY DRIVER SYNCHRONIZATION ERROR
;
;
;
;******************************************************************************
;	MSC 9205A WINCHESTER DISK BOARD I/O PARAMETER DEFINITION
;******************************************************************************
;
;HARD DISK INSTRUCTION PARAMETER DEFINITION
;
;
DINOP	EQU	00H			;NO OPERATION
DISEEK	EQU	01H			;SEEK
DIFMT	EQU	02H			;FORMAT TRACK
DIRST	EQU	03H			;RESTORE
DIREAD	EQU	04H			;READ DATA
DIVERF	EQU	05H			;VERIFY DATA
DIWRT	EQU	06H			;WRITE DATA
DISINT	EQU	07H			;SET INTERLIEVE
;
;
MAXDSK	EQU	000			;MAXIMUM DISK NUMBER
MAXTRK	EQU	152			;MAXIMUM TRACK NUMBER OF ST-506
MAXREC	EQU	017			;MAXIMUM RECORD NUMBER
MAXPLAT	EQU	003			;MAXIMUM DRIVE PLATTER (HEAD) NUMBER
MAXSUR	EQU	000			;MAXIMUM DISK SURFACE (THIS IS HEAD 2^2 BIT)
;
	EJECT
;
;HARD DISK CONTROLLER I/O PORT ADDRESSING PARAMETERS
;
IOBASE	EQU	090H			;BASE I/O PORT ADDRESS OF CONTROLLER
;
WRSEG	EQU	IOBASE			;WRITE SEGMENT ADDRESS PORT
WRPBLO	EQU	IOBASE+1		;WRITE I/O PARAMETER BLOCK ADDR LOW
WRPBHI	EQU	IOBASE+2		;WRITE I/O PARAMETER BLOCK ADDR HIGH
STDIAG	EQU	IOBASE+5		;START DISK CONTROLLER SELF DIAGNOSTIC
CTLRST	EQU	IOBASE+7		;CONTROLLER RESET PORT
;
RDSSTAT	EQU	IOBASE			;READ SUBSYSTEM STATUS PORT
RDRTYP	EQU	IOBASE+1		;READ RESULT TYPE PORT
RDCSTAT	EQU	IOBASE+2		;READ CONTROLLER STATUS PORT
RDRBYT	EQU	IOBASE+3		;READ RESULT BYTE PORT
RDISTAT	EQU	IOBASE+4		;READ INTERRUPT STATUS PORT
RDDCONF	EQU	IOBASE+7		;READ DISK CONFIGURATION PORT
;
;
HRETRY	EQU	03			;HARD DISK RETRY COUNT, CONTROLLER 
					;ALREADY DOES 5-9
;
RECSIZE	EQU	0200H			;512 BYTE RECORDS
;
;
PIOB	EQU	09H			;CHANNEL B PIO DATA ADDRESS
;
;
TIMER	EQU	20H			;INT.CTLR BASE VECTOR TYPE
TICKI	EQU	2			;INT LEVEL USED FOR TICK INT
RTC	EQU	(TIMER+TICKI)*4		;RTC INT. VECTOR OFFSET
	EJECT
;
	CSEG
;
	ORG	0
CCP:
	ORG	BIOS			;START OF BIOS 
;
;
;I/O JUMP VECTOR
;THIS IS WHERE CP/M-86 CALLS WHENEVER IT NEEDS
;TO DO ANY INPUT/OUTPUT OPERATION 
;USER PROGRAMS MAY USE THESE ENTRY POINTS
;
;
	JMP	INIT			;FROM COLD START LOADER 
	JMP	WBOOT			;FROM WARM BOOT 
	JMP	CONST			;CHECK CONSOLE KB STATUS 
	JMP	CONIN			;READ CONSOLE CHARACTER 
	JMP	CONOUT			;WRITE CONSOLE CHARACTER 
	JMP	LPTOUT			;WRITE LISTING CHAR 
	JMP	PUNCH			;WRITE PUNCH CHAR 
	JMP	READER			;READ READER CHAR 
	JMP	HOME			;MOVE DISK TO TRACK ZERO 
	JMP	SELDSK			;SELECT DISK DRIVE 
	JMP	SETTRK			;SEEK TO TRACK IN REG A 
	JMP	SETSEC			;SET SECTOR NUMBER 
	JMP	SETDMA			;SET DMA OFFSET ADDRESS
	JMP	READ			;READ SELECTED SECTOR
	JMP	WRITE			;WRITE SELECTED
	JMP	LPTSTAT			;RETURN LIST STATUS
	JMP	SECTRAN			;SECTOR TRANSLATE
	JMP	SETDMAB			;SET DMA SEGMENT ADDRESS
	JMP	GETSEGB			;GET MEM DESCRIB TABLE OFFSET
	JMP	GETIOB			;GET I/O MAPPING BYTE
	JMP	SETIOB			;SET I/O MAPPING BYTE
;
	EJECT
;
;	ROUTINE TO CALL	SIMPLE I/O FUNCTIONS VIA VECTABLE JUMP VECTORS
;
SIMPLEIO:
	PUSH	DS
	PUSH	CS
	POP	DS
	PUSH	DX
	MOV	DX,SI
	ADD	SI,DX
	ADD	SI,DX			;MULTIPLY SI BY 3
	ADD	SI,OFFSET VECTABLE
	POP	DX
	CALL	SI			;INDIRECT CALL THRU ADDRESS FIELDS OF
	PUSHF
	POP	SI			;RETURN FLAGS TO CALLER IN SI REG.
	POP	DS
	IRET				;...SIMPLE I/O VECTOR TABLE
;
;
;******************************************************************************
;	COLD BOOT INITIALIZATION ROUTINE
;******************************************************************************
;
INIT:
	CLI				;LOCK OUT INTERRUPTS
	MOV	AX,CS			;GET OS SEGMENT ADDRESS
	MOV	SS,AX			;INITIALIZE STACK SEGMENT
	MOV	SP,OFFSET STKBASE	;...AND INITIALIZATION STACK
	MOV	AX,0
	MOV	DS,AX			;PREPARE FOR INT VECTOR INIT
	MOV	ES,AX 
	MOV	INT0OFF,OFFSET TRAP	;SETUP INT0 TO ADDRESS TRAP ROUTINE
	MOV	INT0SEG,CS		;THIS IS 4-BYTE VECTORS
	MOV	DI,4
	MOV	SI,0
	MOV	CX,510			;SET REPEAT COUNTER
	CLD				;SET FORWARD DIRECTION
	REP	MOVS AX,AX		;PROPAGATE TRAP VECTOR TO ALL OF TABLE
	MOV	BDOSOFF,BDOS		;INIT SYSTEM CALL VECTOR
	MOV	RTCOFF,OFFSET RTCINT	;INIT REAL TIME CLOCK VECTOR
	MOV	BIOSOFF,OFFSET SIMPLEIO
;
;	*** INTERRUPT VECTORS INITIALIZED, NOW INIT SYSTEM WORKING STORAGE ***
;
	MOV	AX,CS			;GET OS SEGMENT ADDRESS
	MOV	DS,AX			;INITIALIZE DATA SEGMENT
	MOV	ES,AX			;...AND EXTRA SEGMENT
	MOV	IOERR,0			;START OF ZERO FILL AREA
	MOV	SI,OFFSET IOERR
	MOV	DI,OFFSET IOERR+1 
	MOV	CX,WORD PTR (ENDZ-STARTZ)-1
	CLD				;SET FORWARD DIRECTION
	REP	MOVS AL,AL		;DO THE ZERO FILL
;
;
;	*** INITIALIZE MEMMORY REGION TABLE ***
;
	IN	AL,PIOB			;GET SWITCH SETTING
	AND	AL,0CH			;LEAVE ONLY SWITCH 3 AND 4 BITS
	XOR	AL,0CH			;IF BOTH SWITCHES ARE OPEN
	JZ	NOEXTRAM		;THEN ASSUME NO RAM BOARD INSTALLED
	SHR	AL,1
	SHR	AL,1
	MOV	CL,AL			;COMPUTED SHIFT COUNT
	MOV	AH,10H			;COMPUTE MEMMORY SEGMENT SIZE
	SHL	AH,CL			;...IN PARAGRAPH UNITS
	MOV	AL,0
	MOV	WORD PTR MTR+7,AX	;SAVE IN TABLE
	MOV	WORD PTR MTR+5,4000H	;SAVE START OF EXTRA RAM BOARD
	MOV	BYTE PTR MTR,02		;ELSE ASSUME TWO RAM SEGMENTS
NOEXTRAM:
;
;	*** INITIALIZE INTERRUPT CONTROLER ***
;
	MOV	AL,13H
	OUT	0C0H,AL			;SEND ICW1
	MOV	AL,TIMER
	OUT	0C1H,AL			;SEND ICW2
	MOV	AL,03
	OUT	0C1H,AL			;SEND ICW4
	MOV	AL,0FBH
	OUT	0C1H,AL			;ENABLE PIT-0 INTERRUPTS
	STI				;LET IN REAL TIME CLOCK
;
;	*** PRINT HELLO MESSAGE ON CONSOLE ***
;
	MOV	BX,OFFSET SMSG		;PRINT OPENING MESSAGE 
	CALL	PRTMSG			;PRINT MESSAGE STRING
	MOV	CL,DEFDSK		;GIVE CP/M-86 "DISK SELECTED" NUMBER
	JMP	CCP			;JUMP TO CCP WITH MINI SET CODE
;
	EJECT
;
WBOOT:
	MOV	AL,0			;SELECT HEAD ZERO (SELECTED DISK = A:)
	MOV	SEKMHD,AL		;SET FLAGS TO HARD DISK FOR POSSIBLE ERROR
	MOV	HSTMHD,AL		;..ROUTINE SCAN
	MOV	HSTACT,AL		;SET HOST BUFFER INACTIVE
	MOV	UNACNT,AL		;CLEAR UNALLOCATED COUNT
	JMP	CCP+6			;GO BACK TO CP/M-86 
;
;
;
;CONSOLE STATUS ROUTINE
;	RETURNS AL=00H IF NO INPUT READY
;	RETURNS AL=FFH IF INPUT READY
CONST:
	PUSH	ES
	MOV	AX,2000H		;INITIALIZE ES TO START
	MOV	ES,AX			;...OF MP2-Z80 RAM
	MOV	BX,IFTOFF+CONCHA+RXF	;QUEUE OFFSET
	CALL	LOOKQ			;GET NBR OF BUFFERS IN QUEUE
	AND	AL,AL
	JZ	CSEXIT			;EXIT IF QUEUE EMPTY
	MOV	AL,0FFH			;FLAG QUEUE NOT EMPTY
CSEXIT:
	POP	ES
	RET
;
	EJECT
;
;CONSOLE INPUT ROUTINE
;	GETS CHAR TO (AL) REGISTER
CONIN:
	PUSH	ES
	MOV	AX,2000H		;Z80 RAM BASE ADDRESS
	MOV	ES,AX
CONIN1:
	MOV	BX,IFTOFF+CONCHA+RXF	;QUEUE OFFSET
	CALL	DEQ			;GET BUFFER
	JNC	CONIN1			;WAIT IF NO BUFFER IN QUEUE
	XCHG	BX,DX			;GET BUFFER ADDRESS INTO BX
	MOV	DX,ES:ÆBXÅ+DPTR		;GET 1. DATA ADDR. FROM QHEAD
	XCHG	BX,DX
	CMP	ES:BYTE PTR ÆBXÅ,ENTDLDU;MUST BE ENTIRE DATA LDU
	JE	CONIN2			;GO GET DATA IF LDU TYPE OK
	MOV	BX,IFTOFF+CONCHA+RXE	;ELSE
	CALL	ENQ			;RELEASE BUFFER AND
	JMPS	CONIN1			;...GO TRY AGAIN
CONIN2:
	INC	BX
	INC	BX			;POINT TO DATABYTE
	MOV	AL,ES:ÆBXÅ		;GET DATA FROM BUFFER
	MOV	BX,IFTOFF+CONCHA+RXE	;EMPTY QUEUE OFFSET
	PUSH	AX			;SAVE CHARACTER
	CALL	ENQ			;RETURN BUFFER
	POP	AX
	POP	ES
	AND	AL,7FH			;MASK OFF PARITYBIT
	RET
;
	EJECT
;
;
;CONSOLE OUTPUT ROUTINE 
;	SENDS CHAR IN (CL) REGISTER
;
CONOUT:
	MOV	AX,2000H		;Z80 RAM BASE ADDRESS
	MOV	CONSEMAP,AH		;LOCKOUT FORWARD TIMER
	PUSH	ES
	MOV	ES,AX
	MOV	BX,CONPTR		;GET CONSOLE BUFFER DATA POINTER
	OR	BX,BX			;IF OLD POINTER INVALID
	JZ	NEWCBUF			;...GO INITIATE NEW CONSOLE BUFFER
;
	MOV	ES:ÆBXÅ,CL		;ELSE SAVE CHARACTER IN PENDING BUFFER
	INC	CONPTR			;UPDATE POINTER
	MOV	BX,CONBUF		;GET BUFFER ADDRESS
	MOV	CL,ES:BYTECÆBXÅ		;GET OLD BYTECOUNT FROM BUFFERHEAD
	INC	CL
	MOV	ES:BYTECÆBXÅ,CL		;STORE NEW BYTECOUNT
	CMP	CL,CBLENGTH		;TEST IF BUFFER IS FULL
	JB	CONOEXIT		;...EXIT IF NOT
;
	MOV	CONPTR,0		;FLAG POINTER AS INVALID
	MOV	DX,IFTOFF+CONCHA+TXF	;QUEUE OFFSET
	XCHG	BX,DX
	CALL	ENQ			;SEND BUFFER TO Z80
	JMPS	CONOEXIT		;EXIT CONOUT
;
	EJECT
;
NEWCBUF:
	PUSH	CX			;SAVE CHARACTER IN CL
CONOUT1:
	MOV	BX,IFTOFF+CONCHA+TXE	;QUEUE OFFSET
	CALL	DEQ			;GET EMPTY BUFFER
	JNC	CONOUT1			;WAIT IF NO BUFFER IN QUEUE
;
	MOV	CONBUF,DX
	MOV	BX,0FH
	ADD	BX,DX			;COMPUTE NEXT-DATA POINTER
	MOV	CONPTR,BX		;SAVE TILL NEXT CONOUT OPERATION
	DEC	BX
	POP	AX			;RESTORE CHARACTER INTO AL
	MOV	ES:ÆBXÅ,AL		;SAVE CHARACTER IN BUFFER
	DEC	BX
	MOV	ES:BYTE PTR ÆBXÅ,CONOPC	;NO LDU NUMBERS
	DEC	BX
	MOV	ES:BYTE PTR ÆBXÅ,ENTDLDU;BUFFER IS ENTIRE DATA LDU
	XCHG	BX,DX
	MOV	ES:DPTRÆBXÅ,DX		;SAVE POINTER IN QHEAD
	MOV	ES:WORD PTR BYTECÆBXÅ,3	;SET BYTECOUNT
CONOEXIT:
	POP	ES			;RESTORE SEGMENT
	MOV	CONSEMAP,0		;LET IN FORWARD TIMER
	RET
;
	EJECT
;
;PUNCH PAPER TAPE, DEFAULT IS SERIAL PRINTER CHANNEL
;
PUNCH:
	JMP	LPTOUT			;SEND PUNCH TO SERIAL OUTPUT
					;RETURN THROUGH LPTOUT
;
;
;READ PAPER TAPE, DEFAULT IS SERIAL PRINTER CHANNEL
;
READER:
	JMP	LPTINP			;READ FROM SERIAL INPUT 
					;RETURN THROUGH LPTINP
;
;
;
;
;LINEPRINTER STATUS ROUTINE
;	RETURNS AL=00H IF LP NOT READY
;	RETURNS AL=FFH IF LP READY
LPTSTAT:
	PUSH	ES
	MOV	AX,2000H		;INITIALIZE ES TO START
	MOV	ES,AX			;...OF MP2-Z80 RAM
	MOV	BX,IFTOFF+LPCHA+TXE	;QUEUE OFFSET
	CALL	LOOKQ			;GET NBR OF BUFFERS IN QUEUE
	AND	AL,AL
	JZ	LPEXIT			;EXIT IF QUEUE EMPTY
	MOV	AL,0FFH			;FLAG QUEUE NOT EMPTY
LPEXIT:
	POP	ES
	RET
;
	EJECT
;
;LINEPRINTER OUTPUT ROUTINE
;	SEND CHARACTER IN (CL) REGISTER
;
LPTOUT:
	MOV	AX,2000H		;Z80 RAM BASE ADDRESS
	MOV	LPTSEMAP,AH		;LOCK OUT FORWARD TIMER
	PUSH	ES
	MOV	ES,AX
	MOV	BX,LPTPTR		;GET PRINTER BUFFER DATA POINTER
	OR	BX,BX			;IF OLD POINTER INVALID
	JZ	NEWPBUF			;...GO INITIATE NEW PRINTER BUFFER
;
	MOV	ES:ÆBXÅ,CL		;ELSE SAVE CHARACTER IN PENDING BUFFER
	INC	LPTPTR			;UPDATE POINTER
	MOV	BX,LPTBUF		;GET BUFFER ADDRESS
	MOV	CL,ES:BYTECÆBXÅ		;GET OLD BYTECOUNT FROM BUFFERHEAD
	INC	CL
	MOV	ES:BYTECÆBXÅ,CL		;STORE NEW BYTECOUNT
	CMP	CL,LBLENGTH		;TEST IF BUFFER IS FULL
	JB	LPTOEXIT		;...EXIT IF NOT	
;
	MOV	LPTPTR,0		;FLAG POINTER AS INVALID
	MOV	DX,IFTOFF+LPCHA+TXF	;QUEUE OFFSET
	XCHG	BX,DX
	CALL	ENQ			;SEND BUFFER TO Z80
	JMPS	LPTOEXIT		;EXIT LPTOUT
;
	EJECT
;
NEWPBUF:
	PUSH	CX			;SAVE CHARACTER IN CL
LPTOUT1:
	MOV	BX,IFTOFF+LPCHA+TXE	;QUEUE OFFSET
	CALL	DEQ			;GET EMPTY BUFFER
	JNC	LPTOUT1			;WAIT IF NO BUFFER IN QUEUE
;
	MOV	LPTBUF,DX
	MOV	BX,0FH
	ADD	BX,DX			;COMPUTE NEXT-DATA POINTER
	MOV	LPTPTR,BX		;SAVE TILL NEXT LPTOUT OPERATION
	DEC	BX
	POP	AX			;RESTORE CHARACTER INTO AL
	MOV	ES:ÆBXÅ,AL		;SAVE CHARACTER IN BUFFER
	DEC	BX
	MOV	ES:BYTE PTR ÆBXÅ,LPOPC	;NO LDU NUMBERS
	DEC	BX
	MOV	ES:BYTE PTR ÆBXÅ,ENTDLDU;BUFFER IS ENTIRE DATA LDU
	XCHG	BX,DX
	MOV	ES:DPTRÆBXÅ,DX		;SAVE POINTER IN QHEAD
	MOV	ES:WORD PTR BYTECÆBXÅ,3	;SET BYTECOUNT
LPTOEXIT:
	POP	ES			;RESTORE SEGMENT
	MOV	LPTSEMAP,0		;LET IN FORWARD TIMER
	RET
;
	EJECT
;
;SELECT HEAD/SURFACE (CP/M-86 THINKS THIS IS DISK) NUMBER ACCORDING TO (CL)
;
;	  00 = HEAD 0 (DISK A:)
;	  01 = HEAD 1 (DISK B:)
;	  02 = HEAD 2 (DISK C:)
;	  03 = HEAD 3 (DISK D:)
;	  04 = MINI FLOPPY (DISK E:)
;	  05 = OPTIONAL MINI FLOPPY (DISK F:)
;
SELDSK:
	MOV	BX,0			;RETURN 0000 IN BX IF ERROR
	CMP	CL,MINISEL+1		;IS SELECT MORE THAN MAX TABLE ENTRY
	JB	SEL1			;RETURN WITH ERROR, IF NOT VALID TABLE ENTRY
	RET
SEL1:
	PUSH	BX			;CHECK IF VALID FROM TABLE
	MOV	BL,CL
	SHL	BL,1			;MAKE UNIT SELECT TABLE INDEX
	SHL	BL,1
	ADD	BX,OFFSET DSTAB		;DRIVE SETUP TABLE BASE
	MOV	AL,ÆBXÅ			;GET VALIDITY BYTE THIS UNIT
	OR	AL,AL			;ZERO = ILLEGAL
	XCHG	BX,DX			;SAVE POINTER IF NO EXIT
	POP	BX			;GET BACK BX TO ZERO FOR ERROR EXIT
	JNZ	SEL2			;RETURN ON ILLEGAL UNIT
	RET
SEL2:
	MOV	SEKDSK,CL
;
	XCHG	BX,DX			;IS A LEGAL SELECT THEN PROCESS 
					;PARAMETERS
	INC	BX			;POINT TO DRIVE PHYSICAL UNIT NUMBER
					;DRIVE PHYSICAL UNIT NUMBER NOT USED
					;TILL I/O ROUTINES WHEN THE DRIVE IS USED
;
	INC	BX			;POINT TO MINI/HARD FLAG
	MOV	AL,ÆBXÅ
	MOV	SEKMHD,AL		;SET CURRENTLY SELECTED TYPE FLAG
;
	INC	BX			;POINT TO CP/M-86 DISK PARAMETER TABLE INDEX
	MOV	BL,ÆBXÅ			;MAKE DOUBLE TABLE INDEX*16
	MOV	BH,00H
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	ADD	BX,OFFSET DPBASE	;BX=POINTER TO DRIVE PARAMETER TABLE
	RET
;
;
;
;
;SUBROUTINE TO GET THE PHYSICAL DRIVE NUMBER OUT OF THE DRIVE
;SELECT PARAMETER TABLE INTO (CL)
;
GETDRNO:
	MOV	BL,HSTDSK		;GET UNIT CP/M-86 THINKS WE HAVE
	SHL	BL,1			;SETUP INDEX INTO FOUR BYTE TABLE
	SHL	BL,1			;ENTRIES
	MOV	BH,0
	MOV	CL,OFFSET DSTAB+1ÆBXÅ	;GET PHYSICAL NUMBER INTO (CL)
	RET
;
	EJECT
;
;MOVE DISK TO TRACK ZERO 
;
HOME:
	MOV	AL,HSTWRT		;CHECK FOR PENDING WRITE
	OR	AL,AL
	JNZ	HOMEIT
	MOV	HSTACT,AL		;CLEAR HOST ACTIVE FLAG
HOMEIT:
	MOV	SEKTRK,0000H		;ITS LIKE WE ARE GOING TO TRACK 0
;
	MOV	BX,WORD PTR HSTMHD	;GET PRESENT HOST DISK NUMBER AND TYPE FLAG
	MOV	WORD PTR TEMPMHD,BX	;..SAVE TO RESTORE ON HOME EXIT
;
	MOV	AL,SEKDSK		;TEMPORARY SET OF HOST DISK FROM 
	MOV	HSTDSK,AL		;...SEKDSK
	CALL	GETDRNO			;GET PHYSICAL DRIVE NUMBER TO (CL)
	TEST	SEKMHD,0FFH		;CHECK IF MINI OR HARD DISK
	JNZ	FLOPHME			;GO TO FLOPPY RESTORE ROUTINE
;
	CALL	HPLAT			;SET HARD DISK UNIT IOPB
	MOV	CL,00H
	CALL	HSURF
	CALL	HREST			;GO RESTORE HARD DISK
	OR	AL,AL
	JNZ	HOMEERR			;CHECK FOR RESTORE ERROR
HOMEOK:
	XOR	AL,AL
HOMEXIT:
	MOV	ERFLAG,AL		;RESET ERROR FLAG
	MOV	BX,WORD PTR TEMPMHD	;RESTORE BIOS ACTIVE TYPE FLAG AND HOST DISK
	MOV	WORD PTR HSTMHD,BX	;..TO VALUES BEFORE HOME OPEATION
	RET				;RETURN FROM HOME, IF O.K. 
;
HOMEERR:
	CALL	EREXIT			;PRINT BIOS ERROR MESSAGE
	MOV	AL,01H			;SET ERROR FLAG
	JMPS	HOMEXIT			;TO HOME EXIT POINT
;
	EJECT
;
;MINI FLOPPY DISK RESTORE ROUTINE
;
FLOPHME:
	CALL	MUNIT			;SET MINI UNIT TO RESTORE
	CALL	MREST			;GO RESTORE
	OR	AL,AL
	JZ	HOMEOK
	JMPS	HOMEERR			;GO WORK FOR ERROR RECOVERY
;
;
;SET TRACK NUMBER SPECIFIED BY CX REG.
;
SETTRK:
	MOV	SEKTRK,CX		;TRACK TO SEEK
	RET
;
;
;
;TRANSLATE THE SECTOR GIVEN BY CX REG.
;
;	NO TRANSLATE DONE AT THIS TIME. HARD DISK CONTROLLER DOES IT
;	AND WE WILL TRANSLATE THE MINI FLOPPY AT THE PHYSICAL SECTOR
;	BASIS IN THE MINI FLOPPY READ/WRITE SETUP ROUTINE.
;
SECTRAN:
	MOV	BX,CX
	RET				;RETURN FROM SECTRAN
;
;
;
;
;SET DISK SECTOR NUMBER 
;
SETSEC:
	MOV	SEKSEC,CL		;SECTOR TO SEEK
	RET				;RETURN FROM SETSEC
	EJECT
;
;
;SET DISK DMA OFFSET
;
SETDMA:
	MOV	DMAADR,CX		;PUT AT DMA ADR ADDRESS
	RET				;RETURN FROM SETDMA
;
;
;
;SET DMA SEGMENT
;
SETDMAB:
	MOV	DEFDMASG,CX
	RET
;
;
;
;GET MEMMORY REGION TABLE OFFSET
;
GETSEGB:
	MOV	BX,OFFSET MTR
	RET
;
;
;
;GET IOBYTE
;
GETIOB:
	MOV	AL,IOBYTE
	RET
;
;
;
;SET IOBYTE
;
SETIOB:
	MOV	IOBYTE,CL
	RET
;
	EJECT
;
;	***  READ THE SELECTED CP/M-86  SECTOR  ***
;
READ:
	XOR	AL,AL
	MOV	UNACNT,AL		;CLEAR UNALLOCATED COUNT
	INC	AL
	MOV	READOP,AL		;READ OPERATION
	MOV	RSFLAG,AL		;MUST READ DATA
	MOV	AL,WRUAL
	MOV	WRTYPE,AL		;TREAT AS UNALLOCCATED
	JMP	RWOPER			;TO PERFORM THE READ
;
;
;	***  WRITE THE SELECTED CP/M-86  SECTOR  ***
;
WRITE:
	MOV	READOP,0		;NOT A READ OPERATION
	MOV	WRTYPE,CL
	CMP	CL,WRUAL		;WRITE UNALLOCATED?
	JNE	CHKUNA			;CHECK FOR UNALLOCATED
;
;WRITE TO UNALLOCATED, SET PARAMETERS
;
	MOV	UNACNT,BLKSIZ/128	;NEXT UNALLOCATED RECORDS
	MOV	AL,SEKDSK		;DISK TO SEEK
	MOV	UNADSK,AL		;UNADSK = SEKDSK
	MOV	BX,SEKTRK
	MOV	UNATRK,BX		;UNATRK = SECTRK
	MOV	AL,SEKSEC
	MOV	UNASEC,AL		;UNASEC = SEKSEC
;
;CHECK FOR WRITE TO UNALLOCATED SECTOR
;
CHKUNA:
	MOV	AL,UNACNT		;ANY UNALLOCATED REMAINING?
	OR	AL,AL
	JZ	ALLOC			;SKIP IF NOT
;
	EJECT
;
;MORE UNALLOCATED RECORDS REMAIN
;
	DEC	AL			;UNACNT = UNACNT-1
	MOV	UNACNT,AL
	MOV	AL,SEKDSK		;SAME DISK?
	CMP	AL,UNADSK		;SEKDSK = UNADSK?
	JNE	ALLOC			;SKIP IF NOT
;
;
;DISKS ARE THE SAME
;
	MOV	BX,UNATRK
	CMP	BX,SEKTRK		;SEKTRK = UNATRK?
	JNE	ALLOC			;SKIP IF NOT
;
;
;TRACKS ARE THE SAME
;
	MOV	AL,SEKSEC		;SAME SECTOR?
	MOV	BX,OFFSET UNASEC
	CMP	AL,ÆBXÅ			;SEKSEC = UNASEC?
	JNE	ALLOC			;SKIP IF NOT
;
	EJECT
;
;MATCH, MOVE TO NEXT SECTOR FOR FUTURE REFERENCE
;
	INC	AL
	MOV	ÆBXÅ,AL			;UNASEC=UNASEC+1
	MOV	AH,HDSPT		;USE HARD DISK SECTORS PER TRACK
	TEST	SEKMHD,0FFH		;CHECK KIND OF DISK
	JZ	MTCHCMP			;HARD DISK MATCH
	MOV	AH,FDSPT		;USE MINI DISK SPT
MTCHCMP:
	CMP	AL,AH
	JB	NOOVF			;SKIP IF NO OVERFLOW
;
;
;OVERFLOW TO NEXT TRACK
;
	MOV	BYTE PTR ÆBXÅ,0		;UNASEC = 0
	INC	WORD PTR UNATRK		;UNATRK = UNATRK+1
;
;
;MATCH FOUND, MARK AS UNNECESSARY READ
;
NOOVF:
	MOV	RSFLAG,0		;RSFLAG = 0
	JMPS	RWOPER			;TO PERFORM THE WRITE
;
;
;NOT AN UNALLOCATED RECORD, REQUIRES PRE-READ
;
ALLOC:
	XOR	AL,AL			;0 TO A REG.
	MOV	UNACNT,AL		;UNACNT = 0
	INC	AL			;1 TO A REG.
	MOV	RSFLAG,AL		;RSFLAG = 1
;
	EJECT
;******************************************************************************
;	COMMON CODE FOR READ AND WRITE OPERATIONS
;******************************************************************************
;
RWOPER:					;ENTER HERE TO PERFORM THE READ/WRITE
	MOV	ERFLAG,0		;NO ERRORS (YET)
	MOV	AL,SEKSEC		;COMPUTE HOST SECTOR
	MOV	CL,SECSHF
	SHR	AL,CL
;
;
;OUR DISK CONTROLLER NUMBERS ITS RECORDS FROM 1 AND NOT ZERO
;SO FIXUP THE "SEKHST" SECTOR NUMBER
;
	INC	AL
	MOV	SEKHST,AL		;HOST SECTOR TO SEEK
;
;
;ACTIVE HOST SECTOR?
;
	MOV	BX,OFFSET HSTACT	;HOST ACTIVE FLAG
	MOV	AL,ÆBXÅ
	MOV	BYTE PTR ÆBXÅ,1		;ALWAYS BECOMES 1
	OR	AL,AL			;WAS IT ALREADY?
	JZ	FILHST			;FILL HOST IF NOT
;
;
;HOST BUFFER ACTIVE, SAME AS SEEK BUFFER?
;
	MOV	AL,SEKDSK
	CMP	AL,HSTDSK		;SAME DISK?
	JNE	NOMATCH
;
;
;SAME DISK, SAME TRACK?
;
	MOV	BX,HSTTRK
	CMP	BX,SEKTRK		;SEKTRK = HSTTRK?
	JNE	NOMATCH
;
	EJECT
;
;SAME DISK, SAME TRACK, SAME BUFFER?
;
	MOV	AL,SEKHST
	CMP	AL,HSTSEC		;SEKHST = HSTSEC?
	JE	MATCH			;SKIP IF MATCH
;
;
;PROPER DISK, BUT NOT CORRECT SECTOR
;
NOMATCH:
	TEST	HSTWRT,0FFH		;HOST WRITTEN?
	JZ	FILHST
	CALL	WRITEHST		;CLEAR HOST BUFF
	OR	AL,AL			;CHECK FOR WRITE ERRORS
	JZ	FILHST
	RET				;BALE OUT TO BDOS IF ERROR
;
;
;MAY HAVE TO FILL THE HOST BUFFER
;
FILHST:
	MOV	AL,SEKMHD		;COPY SELECTED TYPE TO OPERATION TYPE
	MOV	HSTMHD,AL
	MOV	AL,SEKDSK		;COPY SELECTED DISK TO OPERATION DISK
	MOV	HSTDSK,AL
	MOV	BX,SEKTRK		;COPY SELECTED TRACK TO OPERATION TRACK
	MOV	HSTTRK,BX
	MOV	AL,SEKHST		;COPY SELECTED PHYS SECTOR TO OPERATION SECTOR
	MOV	HSTSEC,AL
	TEST	RSFLAG,0FFH		;NEED TO READ?
	JZ	MATCH			;SKIP PREREAD
;
	CALL	READHST			;YES, IF 1
	MOV	HSTWRT,0		;NO PENDING WRITE
	OR	AL,AL			;CHECK ERROR ON PREREAD?
	JZ	MATCH
	RET				;BACK TO BDOS IF ERROR IN READING
;
	EJECT
;
;COPY DATA TO OR FROM BUFFER
;
MATCH:
	MOV	BL,SEKSEC		;MASK BUFFER NUMBER
	AND	BL,SECMSK		;LEAST SIGNIF BITS
	MOV	BH,0
;
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1
	SHL	BX,1			;SHIFT INTO HOST BUFFER OFFSET
;
;
;BX HAS RELATIVE HOST BUFFER ADDRESS
;
	MOV	SI,OFFSET HSTBUF
	ADD	SI,BX			;SI = HOST ADDRESS
	MOV	DI,DMAADR		;GET DESTIGNATION OFFSET  FOR READ
	MOV	ES,DEFDMASG		;GET DESTIGNATION SEGMENT FOR READ
	MOV	CX,128			;LENGTH OF MOVE (CP/M-86 SECTOR SIZE)
	TEST	READOP,0FFH		;WHICH WAY?
	JNZ	RWMOVE			;SKIP IF READ
;
;
;WRITE OPERATION, MARK AND SWITCH DIRECTION
;
	MOV	HSTWRT,1		;HSTWRT = 1
	XCHG	SI,DI			;SOURCE/DESTINATION SWAP
	MOV	AX,ES
	PUSH	DS
	POP	ES
	MOV	DS,AX			;SWAP ALSO INVOLVED SEGMENTS
;
	EJECT
;
;CX INITIALLY 128, DX IS SOURCE, BX IS DESTINATION
;
RWMOVE:
	CLD				;SET FORWARD DIRECTION
	REP	MOVS AL,AL		;MOVE IT
	MOV	AX,CS
	MOV	DS,AX
	MOV	ES,AX			;RESTORE SEGMENT REGISTERS
;
;DATA HAS BEEN MOVED TO/FROM HOST BUFFER
;
	MOV	AL,WRTYPE		;WRITE TYPE
	CMP	AL,WRDIR		;TO DIRECTORY?
	MOV	AL,ERFLAG		;IN CASE OF ERRORS
	JZ	CHB
	RET				;NO FURTHER PROCESSING
;
;
;CLEAR HOST BUFFER FOR DIRECTORY WRITE
;
CHB:
	OR	AL,AL			;ERRORS?
	JZ	CHB1
	RET				;SKIP IF SO
CHB1:
	MOV	HSTWRT,0		;BUFFER WRITTEN
	CALL	WRITEHST
	RET
;
	EJECT
;PERFORMS THE PHYSICAL WRITE TO THE HOST DISK.
;HSTDSK = HOST DISK NUMBER, HSTTRK = HOST TRACK NUMBER,
;HSTSEC = HOST SECT NUMBER. WRITE "HSTSIZ" BYTES
;FROM HSTBUF AND RETURN ERROR FLAG IN ERFLAG.
;RETURN: AL = ERFLAG = NON-ZERO IF ERROR
;
WRITEHST:
	TEST	HSTMHD,0FFH		;SEE IF HARD DISK
	JNZ	FLOPWR			;GO WRITE FLOPPY SECTOR
;
	CALL	HIOPB			;GO SETUP IOPB FOR OPERATION
	CALL	HWRIT			;GO WRITE IT
	OR	AL,AL			;CHECK STATUS
	JNZ	WRTERR			;EXIT FOR WRITE ERROR
;
WRTOK:
	MOV	ERFLAG,AL		;RESET ERROR FLAG
	RET				;RETURN FROM "WRITEHST", IF O.K. 
;
WRTERR:
	CALL	EREXIT			;PRINT BIOS ERROR MESSAGE
	MOV	AL,01H			;SET ERROR FLAG
	MOV	ERFLAG,AL
	RET
;
FLOPWR:					;HERE TO PERFORM A MINI FLOPPY WRITE
	CALL	MIOPB			;SET MINI FLOPPY IOPB
	CALL	MWRIT			;GO WRITE MINI FLOPPY SECTOR
	OR	AL,AL
	JZ	WRTOK			;NORMAL "GOOD" EXIT
	JMPS	WRTERR			;EXIT FOR WRITE ERROR
;
	EJECT
;
;PERFORMS THE PHYSICAL READ FROM THE HOST DISK.
;HSTDSK = HOST DISK NUMBER, HSTTRK = HOST TRACK NUMBER,
;HSTSEC = HOST SECT NUMBER. READ "HSTSIZ" BYTES
;INTO HSTBUF AND RETURN ERROR FLAG IN ERFLAG AND AL.
;
READHST:
	TEST	HSTMHD,0FFH		;SEE IF HARD DISK
	JNZ	FLOPRD			;GO READ FLOPPY SECTOR
;
	CALL	HIOPB			;SETUP IPOB FOR READ
	CALL	HREAD			;GET IN THE SECTOR
	OR	AL,AL			;CHECK COMMAND STATUS
	JNZ	RDERR			;TO PROCESSING ROUTINE IF ERROR
RDOK:
	MOV	ERFLAG,AL
	RET
;
RDERR:
	CALL	EREXIT			;PRINT BIOS ERROR MESSAGE
	MOV	AL,01H			;SET ERROR FLAG
	MOV	ERFLAG,AL
	RET

;
FLOPRD:					;HERE TO PERFORM A MINI FLOPPY READ
	CALL	MIOPB			;SETUP MINI FLOPPY IOPB
	CALL	MREAD			;GO READ MINI FLOPPY SECTOR
	OR	AL,AL
	JZ	RDOK			;NORMAL "GOOD" EXIT
	JMPS	RDERR			;EXIT FOR READ ERROR
;
	EJECT
;READ/WRITE BIOS ERROR PRINT ROUTINE
;WAITS FOR USER RESPONSE ON CONSOLE AFTER PRINTING MESSAGE
;AND THEN RETURNS TO THE CALLER
;
EREXIT:
	MOV	IOERR,AL		;SAVE ERROR STATUS
	MOV	BX,OFFSET ERRMSH	;PRINT BIOS ERROR MESSAGE FOR HARD DISK
	TEST	HSTMHD,0FFH		;SEE WHAT KIND OF UNIT CAUSED ERROR
	JZ	ERMPRT			;0=ERROR FROM HARD DISK
	MOV	BX,OFFSET ERRMSF	;USE MINI FLOPPY ERROR MESSAGE INSTEAD
ERMPRT:
	CALL	PRTMSG
	MOV	BX,OFFSET ERRMS1	;PRINT COMMON PART OF MESSAGE
	CALL	PRTMSG
	MOV	AL,IOERR		;PRINT ERROR CODE
	PUSH	AX
	MOV	CL,4
	SHR	AL,CL
	CALL	HEXOUT
	POP	AX
	CALL	HEXOUT
	MOV	BX,OFFSET ERRMS2	;PRINT REST OF MESSAGE
	CALL	PRTMSG
	MOV	AL,HSTDSK		;PRINT CP/M-86 DRIVE DESIGNATOR WITH ERROR
	ADD	AL,'A'
	MOV	CL,AL
	CALL	CONOUT
	MOV	BX,OFFSET ERRMS3	;PRINT MESSAGE CLOSE
	CALL	PRTMSG
	CALL	CONIN			;WAIT FOR OPERATOR RESPONSE
	CMP	AL,03			;IF CTL-C THEN GO DO WARM BOOT
	JNZ	ERR1
	JMP	WBOOT
ERR1:
	MOV	BX,OFFSET ERRMS4
	CALL	PRTMSG
	RET				;BACK TO CP/M-86
;
	EJECT
;
;SUBROUTINE TO SEND A HEX CHAR TO THE CONSOLE
;
HEXOUT:
	AND	AL,0FH			;LOW NIBBLE ONLY
	CMP	AL,0AH			;GREATER THAN 9?
	JB	HEX1
	ADD	AL,07H			;ADD OFFSET FOR A-F
HEX1:
	ADD	AL,030H			;ADD IN ASCII OFFSET
	MOV	CL,AL			;PRINT RESULT
	CALL	CONOUT
	RET
;
;
;
;
;SUBROUTINE TO SETUP IOPB FOR HARD DISK READ/WRITE ROUTINES
;
HIOPB:
	CALL	GETDRNO			;SET IOPB FOR "DRIVE" SELECT
					;UNIT NUMBER
	CALL	HPLAT			;USE HEADS DIRECTLY FOR THE LOGICAL
	MOV	CL,00H			;CP/M-86 DRIVES
	CALL	HSURF
;
	MOV	CX,HSTTRK		;GET SELECTED CYLINDER NUMBER
	CALL	HCYL			;PUT INTO IOPB FOR HARD DISK CONTROLLER
	MOV	CX,OFFSET HSTBUF	;SET IOPB FOR BUFFER ADDRESS
	CALL	HBADDR
	MOV	CX,1040H		;GLOBAL SEGMENT TO HOST BUFFER
	CALL	HSEG
	MOV	CL,01H			;SET FOR SINGLE SECTOR TRANSFER
	CALL	HCNT
	MOV	CL,HSTSEC		;SET IOPB FOR RECORD NUMBER
	CALL	HREC
	RET
;
	EJECT
;
;MINI FLOPPY I/O PARAMETER BLOCK SET UP ROUTINE
;
MIOPB:
	CALL	GETDRNO			;GET PHYSICAL DRIVE NUMBER FROM
	CALL	MUNIT			;..FROM DRIVE SELECT TABLE
	MOV	CX,HSTTRK		;GET TRACK NUMBER
	AND	CL,01H			;FIND OUT WHAT THE SIDE NUMBER IS
	CALL	MSURF			;SET MINI HEAD NUMBER
	MOV	CX,HSTTRK		;GET TRACK NUMBER
	SHR	CX,1			;DIVIDE TRACK BY 2
	CALL	MCYL
;
	MOV	CL,00H			;SET SIZE CODE TO 256 BYTES
	CALL	MSIZE
	MOV	CL,02H			;SET SECTOR COUNT TO 2
	CALL	MCNT
;
	MOV	BL,HSTSEC		;CONVERT SECTOR NUMBER TO PROPER SKEW
	DEC	BL			;NOW ZERO BASED FOR INDEXING
	XOR	BH,BH			;DOUBLE BYTE INDEX WORD
	MOV	CL,MFSTRANÆBXÅ
	CALL	MREC			;SET ACTUAL SECTOR TO START READ UPON
	MOV	CX,OFFSET HSTBUF	;SET FLOPPY BUFFER ADDRESS
	CALL	MBADDR
	MOV	CX,1040H		;GLOBAL SEGMENT TO HOST BUFFER
	CALL	MSEG
	RET
;
	EJECT
;******************************************************************************
;
;
;PRINT THE MESSAGE AT BX UNTIL A ZERO 
;
;
PRTMSG:
	MOV	CL,ÆBXÅ			;GET CHARACTER TO PRINT
	OR	CL,CL			;SET FLAGS
	JZ	PRTDONE			;RETURN, IF END OF STRING FLAG
	PUSH	BX			;SAVE MESSAGE POINTER
	CALL	CONOUT			;PRINT CHARACTER, CONSOLE OUTPUT
	POP	BX			;GET POINTER BACK
	INC	BX			;BUMP MESSAGE POINTER
	JMPS	PRTMSG
PRTDONE:
	RET
;
;
;
;
;
;******************************************************************************
;
;DUMMY INTERRUPT TRAP ROUTINE
;
TRAP:
	IRET
;
	EJECT
;******************************************************************************
;
;	FIXED ADDRESS I/O ROUTINES TO BE UTILIZED BY CP/M-86 ON MINI FLOPPY,
;	HARD DISK, CONSOLE I/O, LINE PRINTER I/O. THESE ROUTINES ARE USED BY
;	UTILITIES, SUCH AS DISK FORMATTING AND SYSTEM TRACK ACCESS UTILITIES.
;	AN EXTRA ENTRY IN THE BIOS JUMP VECTOR TABLE (CS:253FH) HAS BEEN
;	ADDED. THIS ROUTINE WILL RETURN BX = OFFSET VECTABLE
;
VECTABLE:				;VECTOR TABLE BASE OFFSET
;
LPTINP:	JMP	LPIN			;GET CHARACTER FROM LINEPRINTER
LOOKQ:	JMP	LQ			;GET QUEUE STATUS
DEQ:	JMP	DQ			;GET BUFFER FROM QUEUE
ENQ:	JMP	NQ			;PUT BUFFER INTO QUEUE
HUNIT:	JMP	HSETUNIT		;SELECT HARD DISK UNIT NUMBER
HPLAT:	JMP	HSETPLAT		;SELECT HARD DISK PLATTER NUMBER 
HSURF:	JMP	HSETSUR			;SELECT HARD DISK SURFACE(HEAD) NUMBER
HCYL:	JMP	HSETCYL			;SELECT HARD DISK CYLINDER NUMBER
HREC:	JMP	HSETREC			;SELECT HARD DISK RECORD NUMBER
HCNT:	JMP	HSETCNT			;SELECT HARD DISK SECTOR COUNT
HBADDR:	JMP	HSETBUF			;SET 9205 CONTROLLER DMA OFFSET
HSEG:	JMP	HSETSEG			;SET 9205 CONTROLLER DMA SEGMENT
HREST:	JMP	HRESTR			;RESTORE SELECTED HARD DISK UNIT
HREAD:	JMP	HGETSEC			;READ SELECTED SECTOR(S)
HVERF:	JMP	HVERSEC			;VERIFY SELECTED SECTOR(S)
HWRIT:	JMP	HPUTSEC			;WRITE SELECTED SECTOR(S);
HEXEC:	JMP	CMDEXEC			;EXECUTE SPECIAL COMMAND OF MSC 9205
HRESET:	JMP	HCTLRST			;RESET HARD DISK CONTROLLER MSC 9205
MUNIT:	JMP	MSETUNIT		;SELECT MINI FLOPPY UNIT NUMBER
MSURF:	JMP	MSETSUR			;SELECT MINI FLOPPY SURFACE(HEAD) NUMBER
MCYL:	JMP	MSETCYL			;SELECT MINI FLOPPY CYLINDER (TRACK) NUMBER
MREC:	JMP	MSETREC			;SELECT MINI FLOPPY RECORD NUMBER
MCNT:	JMP	MSETCNT			;SELECT MINI FLOPPY PHYSICAL SECTOR COUNT
MSIZE:	JMP	MSETSIZE		;SET MINI FLOPPY SECTOR SIZE (00=256 BYTES)
MBADDR:	JMP	MSETBUF			;SET FLOPPY DISK DATA BUFFER ADDRESS
MSEG:	JMP	MSETSEG			;SET FLOPPY DISK DATA BUFFER SEGMENT
MREST:	JMP	FRESTR			;RESTORE SELECTED MINI FLOPPY UNIT
MVERF:	JMP	MVERSEC			;VERIFY (READ) SELECTED SECTOR
MWRIT:	JMP	MPUTSEC			;WRITE SELECTED SECTOR
MREAD:	JMP	MGETSEC			;READ SELECTED SECTOR
MWTRK:	JMP	MWRTTRK			;WRITE SELECTED FLOPPY TRACK
;
;
;
;LINEPRINTER INPUT ROUTINE
;	GETS CHAR TO (AL) REGISTER
LPIN:
	PUSH	ES
	MOV	AX,2000H		;Z80 RAM BASE ADDRESS
	MOV	ES,AX
LPIN1:
	MOV	BX,IFTOFF+LPCHA+RXF	;QUEUE OFFSET
	CALL	DEQ			;GET BUFFER
	JNC	LPIN1			;WAIT IF NO BUFFER IN QUEUE
	XCHG	BX,DX			;GET BUFFER ADDRESS INTO BX
	MOV	DX,ES:ÆBXÅ+DPTR		;GET 1. DATA ADDR. FROM QHEAD
	XCHG	BX,DX
	CMP	ES:BYTE PTR ÆBXÅ,ENTDLDU;MUST BE ENTIRE DATA LDU
	JE	LPIN2			;GO GET DATA IF LDU TYPE OK
	MOV	BX,IFTOFF+LPCHA+RXE	;ELSE
	CALL	ENQ			;RELEASE BUFFER AND
	JMPS	LPIN1			;...GO TRY AGAIN
LPIN2:
	INC	BX
	INC	BX			;POINT TO DATABYTE
	MOV	AL,ES:ÆBXÅ		;GET DATA FROM BUFFER
	MOV	BX,IFTOFF+LPCHA+RXE	;EMPTY QUEUE OFFSET
	PUSH	AX			;SAVE CHARACTER
	CALL	ENQ			;RETURN BUFFER
	POP	AX
	POP	ES
	AND	AL,7FH			;MASK OFF PARITYBIT
	RET
;
	EJECT
;
;******************************************************************************
;	THIS MODULE CONTAINS THE INTERFACE ROUTINES NESSESARY FOR
;	INTER-CPU COMMUNICATION VIA THE CHRISTIAN ROVSING A/S CR80 -
;	LTU STANDARD QUEUE INTERFACE.
;******************************************************************************
;
;
;	THE MODULE CONSISTS OF THE FOLLOWING ROUTINES:
;
;		LOOKQ:	RETURNS AL = NBR. OF BUFFERS IN QUEUE
;
;		DEQ:	POLL DESIRED QUEUE
;
;		ENQ:	SEND BUFFER TO DESIRED QUEUE
;
;	ALL ROUTINES EXPECTS REG. ES TO CONTAIN THE BASE ADDRESS OF THE
;	BOARD IN WHICH THE QUEUE EXISTS. WHEN COMMUNICATING WITH A
;	SCI BOARD THIS BASE MUST BE COMPENSATED BECAUSE THE SCI RAM
;	HAS DIFFERENT BASE ADDRESS SEEN FROM THE MULTIBUS THAN SEEN
;	FROM THE ONBOARD CPU, WHILE THE Z80 RAM ON THE MP2-BOARD HAS
;	GOT SAME BASE ADDRESS SEEN FROM BOTH SIDES (LOWER 16 BITS).
;	THIS STATES THE FOLLOWING VALUES FOR ES:
;		MP2: ES=2000	SCI: ES=BOARD ADR.-200H
;
;
;******************************************************************************
	EJECT
;******************************************************************************
;
;	THIS ROUTINE WILL POLL THE SPECIFIED QUEUE FOR A BUFFER COUNT.
;
;	ENTRY:
;		ES = BOARD/RAM BASE ADDRESS
;		BX = QUEUE OFFSET FROM BASE
;
;	EXIT:
;		AL = NBR OF BUFFERS IN QUEUE
;
LQ:
	MOV	ES:BYTE PTR ÆBXÅ+SF,01	;SET SLAVE FLAG
	TEST	ES:BYTE PTR ÆBXÅ+MF,01	;TEST MASTER FLAG
	JZ	LOOKACC			;JUMP IF ACCESS GRANDED
	MOV	ES:BYTE PTR ÆBXÅ+SF,00	;CLEAR SLAVE FLAG
LOOKBSY:
	CALL	QDELAY			;DON'T USE ALL OF HIS BUS TIME
	TEST	ES:BYTE PTR ÆBXÅ+MF,01	;SEE IF MASTER ACCESS COMPLETED
	JNZ	LOOKBSY			;WAIT IF NOT
	JMPS	LQ			;GO TRY TO GET GRAND AGAIN
LOOKACC:
	MOV	AL,ES:ÆBXÅ+BC		;GET BUFFERCOUNT FROM QUEUE
	MOV	ES:BYTE PTR ÆBXÅ+SF,00	;CLEAR SLAVE FLAG
	RET
;
;
;
;
QDELAY:
	NOP
	NOP
	RET
;
	EJECT
;******************************************************************************
;
;	THIS ROUTINE WILL POLL THE SPECIFIED QUEUE FOR A BUFFER.
;
;	ENTRY:
;		ES = BOARD/RAM BASE ADDRESS
;		BX = QUEUE OFFSET FROM BASE
;
;	EXIT:
;		IF CARRY=1, DX=POINTER TO BUFFER
;		IF CARRY=0, DX UNDEFINED
;
DQ:
	MOV	ES:BYTE PTR ÆBXÅ+SF,01	;SET SLAVE FLAG
	TEST	ES:BYTE PTR ÆBXÅ+MF,01	;TEST MASTER FLAG
	JZ	DEQACC			;JUMP IF ACCESS GRANDED
	MOV	ES:BYTE PTR ÆBXÅ+SF,00	;CLEAR SLAVE FLAG
DEQBSY:
	CALL	QDELAY			;DON'T USE ALL OF HIS BUS TIME
	TEST	ES:BYTE PTR ÆBXÅ+MF,01	;SEE IF MASTER ACCESS COMPLETED
	JNZ	DEQBSY			;WAIT IF NOT
	JMPS	DQ			;GO TRY TO GET GRAND AGAIN
DEQACC:
	TEST	ES:BYTE PTR ÆBXÅ+BC,0FFH;TEST IF QUEUE IS EMPTY (+CY:=0)
	JZ	DEQMTY			;EXIT IF NO BUFFERS IN QUEUE
	DEC	ES:BYTE PTR ÆBXÅ+BC	;DECREMENT BUFFERCOUNT
	MOV	DX,ES:ÆBXÅ+FB		;GET BUFFER ADDRESS FROM QUEUE
	XCHG	BX,DX
	MOV	AX,ES:ÆBXÅ		;GET POINTER TO NEXT BUFFER IN QUEUE
	XCHG	BX,DX
	MOV	ES:ÆBXÅ+FB,AX		;INSERT AS FIRST BUFFER IN QUEUE
	STC				;FLAG BUFFER RECEIVED
DEQMTY:
	MOV	ES:BYTE PTR ÆBXÅ+SF,00	;CLEAR SLAVE FLAG
	RET
;
	EJECT
;******************************************************************************
;
;	THIS ROUTINE WILL APPEND A BUFFER TO THE SPECIFIED QUEUE.
;
;	ENTRY:
;		BX = POINTER TO QUEUE
;		DX = POINTER TO BUFFER
;		ES = BOARD/RAM BASE ADDRESS
;
;	EXIT:
;		ES UNCHANGED, NO PARAMETERS
;
NQ:
	MOV	ES:BYTE PTR ÆBXÅ+MF,01	;SET MASTER FLAG
ENQBSY:
	TEST	ES:BYTE PTR ÆBXÅ+SF,01	;SEE IF SLAVE IS IN THERE
	JZ	ENQACC			;PROCEED IF NOT
	CALL	QDELAY			;DON'T USE ALL OF HIS BUS TIME
	JMPS	ENQBSY			;WAIT UNTIL HE COMES OUT
ENQACC:
	MOV	AX,ES:ÆBXÅ+LB		;GET OLD LAST BUFFER
	MOV	ES:ÆBXÅ+LB,DX		;SAVE NEW LAST BUFFER
	TEST	ES:BYTE PTR ÆBXÅ+BC,0FFH;TEST BUFFERCOUNT IN QUEUE
	JZ	ENQMTY			;JUMP IF QUEUE WAS EMPTY
;
	XCHG	BX,AX
	MOV	ES:ÆBXÅ,DX		;UPDATE ITS CHAIN ELEMENT
	XCHG	BX,AX
	JMPS	ENQXIT
ENQMTY:
	MOV	ES:ÆBXÅ+FB,DX		;UPDATE ALSO FIRST BUFFER POINTER
ENQXIT:
	INC	ES:BYTE PTR ÆBXÅ+BC	;INCREMENT BUFFER COUNT IN QUEUE
	MOV	ES:BYTE PTR ÆBXÅ+MF,00	;CLEAR MASTER FLAG
	RET
;
	EJECT
;******************************************************************************
;
;	SHORT DELAY ROUTINE FOR TIME DELAYS OF (AL) X 1 MSEC
;
;
SDLY:
	MOV	CX,250			;ONE MILLISECOND TIME FACTOR
SDLOP:
	LOOP	SDLOP
	DEC	AL
	JNZ	SDLY
	RET
;
	EJECT
;******************************************************************************
;	HARD DISK CONTROLLER I/O ROUTINES
;******************************************************************************
;
;
;DISK RESTORE SUBROUTINE
;
HRESTR:
	MOV	CX,0000H		;SET IOPB TO CYLINDER ZERO
	CALL	HSETCYL
	MOV	CL,DISEEK		;USE A TRACK 0 SEEK BEFORE RESTORE
	CALL	CMDEXEC			;TO SPEED UP RESTORE SIGNIFICANTLY
	MOV	CL,DIRST		;SETUP RESTORE INSTRUCTION
	CALL	CMDEXEC			;GO EXECUTE COMMAND WITH RETRIES
	RET				;RETURN ZERO IF O.K.
;
;
;RECORD READING SUBROUTINE
;
HGETSEC:
	MOV	CL,DIREAD		;SETUP READ COMMAND
	CALL	CMDEXEC			;GO EXECUTE COMMAND WITH RETRIES
	RET				;RETURN ZERO IF O.K.
;
;
;RECORD VERIFICATION SUBROUTINE
;
HVERSEC:
	MOV	CL,DIVERF		;SETUP VERIFY COMMAND
	CALL	CMDEXEC			;GO EXECUTE COMMAND WITH RETRIES
	RET				;RETURN ZERO IF O.K.
;
;
;RECORD WRITING ROUTINE
;
HPUTSEC:
	MOV	CL,DIWRT		;SETUP WRITE COMMAND
	CALL	CMDEXEC			;GO EXECUTE COMMAND WITH RETRIES
	RET				;RETURN ZERO IF O.K.
;
	EJECT
;
;
;CONTROLLER RESET SUBROUTINE
;
HCTLRST:
	MOV	AL,0			;ZERO DATA (DON'T CARE ANYWAY)
	OUT	CTLRST,AL		;COMMAND CONTROLLER
	MOV	AL,100			;DO 100 MILLISECOND DELAY AFTER 
	CALL	SDLY			;..CONTROLLER RESET
	RET
;
;
;
;SETUP COMMAND BYTE IN IOPB WITH INSTRUCTION FROM (CL) REGISTER
;
HSETCMD:
	AND	CL,07			;GET ONLY INSTR BITS
	AND	INSTR,0F8H		;REMOVE OLD INSTRUCTION
	OR	INSTR,CL		;PUT IN NEW COMMAND
	RET
;
;
;
;SETUP IOPB UNIT ADDRESS ACCORDING TO (CL) REGISTER
;
HSETUNIT:
	AND	CL,03			;GET ONLY UNIT BITS
	SHL	CL,1
	SHL	CL,1
	SHL	CL,1
	SHL	CL,1			;SHIFT INTO POSITION
	AND	INSTR,0CFH		;STRIP OUT OLD UNIT
	OR	INSTR,CL		;INSERT NEW UNIT
	RET
;
	EJECT
;
;
;SETUP RECORD COUNT INTO IOPB ACCORDING TO (CL) REGISTER
;
HSETCNT:
	MOV	RECNT,CL		;PUT INTO IOPB
	RET
;
;
;
;SETUP RECORD NUMBER IN IOPB ACCORDING TO (CL) REGISTER
;
HSETREC:
	AND	CL,3FH			;LEAVE ONLY RECORD BITS
	AND	REC,0C0H		;STRIP OUT OLD RECORD
	OR	REC,CL			;PUT IN NEW RECORD NUMBER
	RET
;
;
;
;SETUP SURFACE NUMBER INTO IOPB ACCORDING TO (CL) REGISTER
;
HSETSUR:
	AND	CL,01			;GET ONLY SURFACE MASK
	SHL	CL,1
	SHL	CL,1
	SHL	CL,1			;SHIFT INTO POSITION
	AND	INSTR,0F7H		;STRIP OUT OLD SURFACE BIT
	OR	INSTR,CL		;PUT IN NEW BIT
	RET
;
	EJECT
;
;SETUP CYLINDER NUMBER IN IOPB ACCORDING TO (CX) REGISTER
;
;	HIGH ORDER TWO BITS OF CYLINDER ADDRESS GOES INTO THE 
;	UPPER BIT OF THE RECORD ADDRESS BYTE.
;
HSETCYL:
	MOV	CYL,CL			;PUT LSB OF CYLINDER NUMBER INTO IOPB
	AND	CH,03			;ONLY TWO BITS IN MSB OS VALID
	ROR	CH,1			;PUT LOW BIT TO HIGH POSITION
	ROR	CH,1
	AND	REC,03FH		;STRIP OUT OLD CYLINDER HIGH BIT
	OR	REC,CH			;PUT IN NEW BIT
	RET
;
;
;SETUP PLATTER NUMBER IN IOPB ACCORDING TO (CL) REGISTER
;
HSETPLAT:
	AND	CL,03			;GET ONLY PLATTER BITS
	ROR	CL,1			;MOVE BITS INTO POSITION
	ROR	CL,1
	AND	INSTR,03FH		;STRIP OUT OLD PLATTER BITS
	OR	INSTR,CL		;PUT IN NEW ONES
	RET
;
;
;SETUP THE DATA BUFFER OFFSET INTO THE I/O PARAMETER BLOCK BY (CX) REGS
;
HSETBUF:
	MOV	DMAOFST,CX
	RET
;
;
;SETUP THE DATA BUFFER SEGMENT INTO THE I/O PARAMETER BLOCK BY (CX) REGS
;
HSETSEG:
	MOV	DMASEG,CX		;SAVE LSB OF SEGMENT ADDRESS IN IOPB
	RET
;
	EJECT
;******************************************************************************
;ROUTINE TO PERFORM DISK CONTROLLER COMMAND, BUSY WAIT, AND STATUS
;CHECKS WITH RETRIES. RETURNS ZERO IN (A) IF OPEATION SUCCESSFUL
;AND REAL ERROR CODE IF OPERATION DID NOT SUCCEEDE AFTER SPECIFIED
;NUMBER OF RETRIES. USER CALLS THIS WITH APPROPIATE COMMAND CODE
;IN REGISTER (CL).
;
CMDEXEC:
	CALL	HSETCMD			;GO SETUP COMMAND FROM (CL)
	MOV	CH,HRETRY		;SET RETRY COUNT
CMDLP:
	PUSH	CX
	CALL	HCMD			;START OPERATION
	CALL	BUSYW			;WAIT TILL CONTROLLER NOR BUSY
	CALL	STCHK			;GET STATUS
	POP	CX
	JNZ	NOGOOD
	RET				;DONE WITH COMMAND IF O.K.
NOGOOD:
	DEC	CH			;DEC RETRY COUNT
	JNZ	CMDLP			;GO TRY AGAIN
;
	RET				;RETURN WITH ERROR CODE IF REALLY BAD
;
	EJECT
;ROUTINE TO SEND PARAMETER BLOCK ADDRESS TO DISK CONTROLLER AND
;THUS INITIATE AN OPERATION. PARAMETER BLOCK ADDRESSING IS SENT 
;SO AS TO ENABLE THE 20-BIT ADDRESSING MODE. CONTROLLER BUSY STATUS
;IS MONITORED FOR UP TO 2 SECONDS BEFORE SENDING COMMAND. IF NOT
;UNBUSY WITHIN 2 SECONDS, THEN CONTROLLER IS HARD RESET ONE TIME
;TO TERMINATE PREVIOUSLY HUNG COMMAND. IF NOT UNBUSY WITHIN THE 
;SECOND TWO SECOND TIME PARAMETER THEN THE ROUTINE IS EXITED TO
;LET USER CALL TO SUBSEQUENT BUSY WAIT ROUTINE ABORT OPERATION
;AND RETURN NOT SUCCESSFUL STATUS
;
HCMD:
	MOV	RSTFLG,02H		;SET ONE RESET TRY FLAG
TRY2:
	MOV	DH,32			;2 SECOND DELAY PARAMETER
BUSYC:
	IN	AL,RDCSTAT		;GET CONTROLLER STATUS
	AND	AL,80H			;MASK BUSY BIT
	JZ	CTLRDY			;EXIT DELAY IF CONTROLLER READY
	MOV	AL,64			;DELAY FOR 64 MILLISECONDS
	CALL	SDLY
	DEC	DH
	JNZ	BUSYC			;IF NOT TWO SECONDS, GO CHECK STATUS 
	DEC	RSTFLG			;TEST IF WE HAVE TRIED THIS BEFORE
	JNZ	GO1
	RET				;BALE OUT AND LET BUSY WAIT FIND TIMEOUT
GO1:
	CALL	HCTLRST			;GO CLEAR CONTROLLER AFTER 2 SECONDS
	JMPS	TRY2			;GO CHECK FOR READY AFTER RESET
CTLRDY:
	MOV	BX,OFFSET IOPB		;POINT AT I/O PARAMETER BLOCK
	MOV	DX,1040H		;GET SEGMENT ADDRESS OF IOPB 
	MOV	AL,BL			;SEND LOW BYTE OF IOPB ADDRESS
	OUT	WRPBLO,AL
	MOV	AL,DL
	OUT	WRSEG,AL		;OUTPUT LSB OF ADDRESS TO CONTROLLER
	MOV	AL,DH
	OUT	WRSEG,AL		;OUTPUT MSB OF ADDRESS
	MOV	AL,BH			;SEND HIGH BYTE OF IOPB ADDR
	OUT	WRPBHI,AL		;..AND START OPERATION
	RET
;
;
;
;ROUTINE TO WAIT TILL COMMAND COMPLETION INDICATED BY CONTROLLER
;BUSY BIT IN CONTROLLER STATUS REGISTER
;
;	ROUTINE WILL RETURN 0FFH IN (AL) IF CONTROLLER DID NOT CLEAR
;	BUSY EITHER AS A RESULT OF TRYING TO START A COMMAND OR
;	AS A RESULT OF BECOMING HUNG ON CURRENT COMMAND
;
BUSYW:
	MOV	DX,04000H		;APPROX 16 SECOND BUSY TIMEOUT
BSYW1:
	IN	AL,RDCSTAT		;GET STATUS BYTE
	AND	AL,80H			;MASK BUSY BIT
	JNZ	GO2
	RET				;EXIT IF NOT BUSY ANY MORE
GO2:
	MOV	AL,01			;1 MILLISECOND
	CALL	SDLY
	DEC	DX			;DEC TOTAL TIMEOUT VALUE
	JNZ	BSYW1			;MORE DELAY IF NOT COUNTED OUT
;
	MOV	AL,0FFH			;SEND BUSY TIMEOUT TO STATCHK ROUTINE
	RET
;
	EJECT
;************************************************************************
;
;	ROUTINE TO CHECK COMMAND COMPLETION STATUS
;		BUSY WAIT WILL SENT (AL) NON ZERO IF THERE WAS A TIMEOUT
;		ON COMMAND EXECUTION WAIT FOR NOT BUSY OR IF BUSY TIMEOUT
;
;		RETURNS (AL) AS FOLLOWS
;			000H=SUCCESSFUL COMPLETION
;			0FEH=CONTROLLER PROVIDED UNDEFINED CODE TYPE
;			0FFH=TIMEOUT ON CMD START OF COMMAND COMPLETION
;			     (IE BUSY ALWAYS ON)
;
;
STCHK:
	CMP	AL,0FFH			;CHECK ENTRY STATUS
	JNE	GO3
	RET				;SEND STATUS = 0FFH IF ALWAYS BUSY
GO3:
	IN	AL,RDRTYP		;GET RESULT TYPE
	AND	AL,03H			;GET RID OF UNUSED BITS
	JZ	ERSTAT			;GO PROCESS AN ERROR STATUS BYTE
;
;
;CONTROLLER PROVIDED AN INVALID OPERATIONAL STATUS
;
CTLFAL:
	IN	AL,RDRBYT		;MAKE SURE CONTROLLER BUSY IS CLEARED
	MOV	AL,0FEH			;RETURN 0FEH BYTE IF CONTROLLER FAILURE
	RET
;
;
;HERE TO PROCESS AN OPERATION ERROR STATUS
;
ERSTAT:
	IN	AL,RDRBYT		;GET RESULT BYTE
	OR	AL,AL			;CHECK FOR SUCCESSFUL COMPLETION
	RET				;RETURN ACTUAL ERROR STATUS TO CALLER
					;(00)=SUCCESSFUL COMPLETION
;
	EJECT
;******************************************************************************
;	MINI FLOPPY DISK CONTROLLER I/O ROUTINES
;******************************************************************************
;
;
;
;SELECT MINI FLOPPY UNIT NUMBER
;(CL)=FLOPPY PHYSICAL UNIT NUMBER
;
MSETUNIT:
	MOV	FUNIT,CL
	RET
;
;
;
;SELECT MINI FLOPPY SURFACE(HEAD) NUMBER
;(CL)=FLOPPY SIDE NUMBER
;
MSETSUR:
	MOV	FSIDE,CL
	RET
;
;
;
;SELECT MINI FLOPPY CYLINDER (TRACK) NUMBER
;(CL)=FLOPPY TRACK NUMBER
;
MSETCYL:
	MOV	FTRK,CL
	RET
;
;
;
;SELECT MINI FLOPPY RECORD NUMBER
;(CL)=FLOPPY SECTOR NUMBER
;
MSETREC:
	MOV	FSEC,CL
	RET
;
	EJECT
;
;
;
;SELECT MINI FLOPPY PHYSICAL SECTOR COUNT
;(CL)=MINI FLOPPY SECTOR COUNT
;
MSETCNT:
	MOV	FCNT,CL
	RET
;
;
;
;SET FLOPPY SECTOR SIZE SELECT
;(CL)=SECTOR SIZE IN BYTES UP TO (00)=256 BYTES
;
MSETSIZE:
	MOV	FSIZE,CL
	RET
;
;
;
;SET FLOPPY DISK DATA BUFFER OFFSET
;(CX)=FLOPPY BUFFER OFFSET
;
MSETBUF:
	MOV	XFRPNT,CX	;STORE FLOPPY DATA BUFFER OFFSET
	RET
;
;
;
;SET FLOPPY DISK DATA BUFFER SEGMENT
;(CX)=FLOPPY BUFFER SEGMENT
;
MSETSEG:
	MOV	XFRSEG,CX	;STORE FLOPPY DATA BUFFER SEGMENT
	RET
;
	EJECT
;
;
;
FRESTR:
	MOV	AL,RESTORE
	CALL	FDCMD		;GO EXECUTE RESTORE
	RET
;
;
;
MVERSEC:
	MOV	AL,0FFH
	RET
;
;
;
MPUTSEC:
	MOV	AL,WRITEFD
	CALL	FDCMD		;GO EXECUTE FLOPPY WRITE
	RET
;
;
;
MGETSEC:
	MOV	AL,READFD
	CALL	FDCMD		;GO EXECUTE FLOPPY READ
	RET
;
;
;
MWRTTRK:
	MOV	AL,WRTRACK
	CALL	FDCMD		;GO FORMAT TRACK
	RET
;
	EJECT
;******************************************************************************
;
;	THIS ROUTINE WILL EXECUTE THE FLOPPY COMMAND IN (AL), USING THE
;	THE PARAMETERS STORED IN THE FLOPPY PARAMETER I/O BLOCK.
;
;	ENTRY:	AL = FLOPPY COMMAND
;	EXIT:	IF COMMAND SUCCESSFULL, AL = 0
;		IF COMMAND FAILED, AL = ERROR STATUS
;
FDCMD:
	MOV	FINST,AL		;SAVE COMMAND TYPE
	PUSH	ES
	MOV	AX,2000H		;Z80 RAM BASE ADDRESS
	MOV	ES,AX
FDCMD1:
	MOV	BX,IFTOFF+FDCHA+TXE	;QUEUE OFFSET
	CALL	DEQ			;GET EMPTY BUFFER
	JNC	FDCMD1			;WAIT IF NO BUFFER IN QUEUE
;
	MOV	BX,12
	ADD	BX,DX			;COMPUTE 1. DATA POINTER
	MOV	DI,BX
	MOV	SI,OFFSET FDIOPB	;GET PARAMETER BLOCK ADDRESS
	MOV	CX,ENDFDIO-FDIOPB	;GET NBR.OF BYTES TO MOVE
	REP	MOVS AL,AL		;MOVE PARAMETERS INTO BUFFER
	XCHG	BX,DX
	MOV	ES:ÆBXÅ+DPTR,DX		;SAVE POINTER IN QHEAD
	MOV	ES:WORD PTR ÆBXÅ+BYTEC,ENDFDIO-FDIOPB	;SET BYTECOUNT
	XCHG	BX,DX
	MOV	BX,IFTOFF+FDCHA+TXF	;QUEUE OFFSET
	CALL	ENQ			;SEND BUFFER TO Z80
;
	EJECT
;
;COMMAND SEND, NOW WAIT FOR RESPONCE
;
FDWAIT:
	MOV	BX,IFTOFF+FDCHA+RXF	;QUEUE OFFSET
	CALL	DEQ			;GET BUFFER
	JNC	FDWAIT			;WAIT IF NO BUFFER IN QUEUE
	XCHG	BX,DX			;GET BUFFER ADDRESS INTO BX
	MOV	DX,ES:ÆBXÅ+DPTR		;GET 1. DATA ADDR. FROM QHEAD
	XCHG	BX,DX
	CMP	ES:BYTE PTR ÆBXÅ,ENTCLDU;MUST BE ENTIRE COMMAND LDU
	JE	FDW1			;GO GET STATUS TYPE IF LDU TYPE OK
FDERR:
	MOV	AL,CPUSYNC		;FLAG ERROR
	JMPS	FDEXIT
FDW1:
	INC	BX
	INC	BX			;POINT TO STATUS TYPE
	MOV	AL,ES:ÆBXÅ		;GET STATUS TYPE FROM BUFFER
	CMP	AL,FINST		;MUST MATCH COMMAND TYPE
	JNE	FDERR			;ERROR EXIT IF NOT
;
	INC	BX			;POINT TO STATUS BYTE
	MOV	AL,ES:ÆBXÅ		;GET DATA FROM BUFFER
FDEXIT:
	MOV	BX,IFTOFF+FDCHA+RXE	;EMPTY QUEUE OFFSET
	PUSH	AX			;SAVE CHARACTER
	CALL	ENQ			;RETURN BUFFER
	POP	AX
	POP	ES			;RESTORE SEGMENT
	RET
;
	EJECT
;******************************************************************************
;	REAL TIME CLOCK INTERRUPT SERVICE ROUTINE
;******************************************************************************
;
RTCINT:
	PUSH	DS			;SAVE DATA SEGMENT
	PUSH	ES
	PUSH	AX
	PUSH	BX
	PUSH	DX
	PUSH	CS
	POP	DS
	TEST	CONSEMAP,0FFH		;IF SEMAPHORE SET
	JNZ	TESTLPT			;...GO LOOK AT PRINTER
	TEST	CONPTR,0FFFFH		;TEST IF BUFFER PENDING
	JZ	TESTLPT			;DON'T FORGET THE PRINTER
	MOV	CONPTR,0		;REMOVE PENDING BUFFER
	MOV	AX,2000H
	MOV	ES,AX			;GET Z80-RAM BASE ADDRESS
	MOV	BX,IFTOFF+CONCHA+TXF	;GET QUEUE OFFSET
	MOV	DX,CONBUF		;GET BUFFER ADDRESS
	CALL	ENQ			;FORWARD BUFFER
TESTLPT:
	TEST	LPTSEMAP,0FFH		;IF SEMAPHORE SET
	JNZ	RTCEXIT			;...EXIT INTERRUPT
	TEST	LPTPTR,0FFFFH		;TEST IF BUFFER PENDING
	JZ	RTCEXIT			;...EXIT IF NOT
	MOV	LPTPTR,0		;REMOVE PENDING BUFFER
	MOV	AX,2000H
	MOV	ES,AX			;GET Z80-RAM BASE ADDRESS
	MOV	BX,IFTOFF+LPCHA+TXF	;GET QUEUE OFFSET
	MOV	DX,LPTBUF		;GET BUFFER ADDRESS
	CALL	ENQ			;FORWARD BUFFER
RTCEXIT:
	POP	DX
	POP	BX
	POP	AX
	POP	ES	
	POP	DS			;RESTORE DATA SEGMENT
	IRET
;
	EJECT
;
DATAOFF	EQU	OFFSET $
	DSEG
	ORG	DATAOFF
;
	INCLUDE	DISK5MB.LIB
;
	EJECT
;
;VALID DISK DRIVE SELECT TABLES
;
;	TABLE ENTRIES CONTAIN VALUES FOR VARIOUS DISK CONFIGURATIONS
;	AND ALLOW QUICK INDEXING TO THE DRIVE SELECT CODE PARAMETERS.
;       EACH ENTRY CONSISTS OF:
;	FIRST BYTE:      LEGAL/ILLEGAL 0=ILLEGAL, 0FFH=LEGAL
;	SECOND BYTE:     PHYSICAL UNIT SELECT CODE 00 TO 03
;	THIRD BYTE:      MINI/HARD FLAG 0=HARD UNIT, 0FFH=MINI FLOPPY
;	FOURTH BYTE:     DISK PARAMETER TABLE INDEX (FROM DISKDEF)
;
DSTAB:
	DB	0FFH,000H,000H,000H	;HARD DISK HEAD 0
	DB	0FFH,001H,000H,001H	;HARD DISK HEAD 1
	DB	0FFH,002H,000H,002H	;HARD DISK HEAD 2
	DB	0FFH,003H,000H,003H	;HARD DISK HEAD 3
;
;
	IF	ONEMINI
	DB	0FFH,000H,0FFH,004H	;MINI DISK UNIT 0
	DB	000H,000H,000H,000H	;..ILLEGAL, ONLY ONE MINI
	ENDIF
;
	IF	NOT ONEMINI
	DB	0FFH,000H,0FFH,004H	;MINI DISK UNIT 0
	DB	0FFH,001H,0FFH,005H	;MINI DISK UNIT 1
	ENDIF
;
;
	EJECT
;
;HDBIOS SIGNON MESSAGE
;
SMSG	DB	CR,LF,ESC,'Æ7m','CHRISTIAN ROVSING A/S'
	DB	'    CR8 - '
	DB	'CP/M-86 Vers. 1.1     '
	DB	YEAR/10+'0',YEAR MOD 10+'0'
	DB	MONTH/10+'0',MONTH MOD 10+'0'
	DB	DAY/10+'0',DAY MOD 10+'0'
	DB	CR,LF,ESC,'Æm','SEAGATE ST-506 HARD DISK / 5 1/4 MINI '
	DB	'FLOPPY BIOS, VER. '
	DB	VERSION/10+'0','.',VERSION MOD 10+'0'
	DB	0
;
	EJECT
;
;ERROR ROUTINE MESSAGES
;
;
ERRMSH	DB	CR,LF,CR,LF,' *** WINCHESTER',0
ERRMSF	DB	CR,LF,CR,LF,' *** MINI FLOPPY',0
ERRMS1	DB	' DISK ERROR (#',0
ERRMS2	DB	') - DRIVE ',0
ERRMS3	DB	': ***  ',0
ERRMS4	DB	CR,LF,0
;
	EJECT
;****************************************************************************
;
;	STORAGE AREA FOR VARIABLES BEGINS HERE...
;
;		THE NEXT SEVERAL BYTES, BETWEEN STARTZ AND
;		ENDZ, ARE SET TO ZERO AT COLD BOOT TIME 
;
STARTZ	EQU	OFFSET $		;START OF ZEROED AREA 
;
;
;NOTE:	THIS LOCATION STORES THE DISK CONTROLLER
;I/O ERROR CODE FOR DEBUGGING PURPOSES.
;
IOERR	RB	1			;DISK I/O ERROR TYPE
;
;
;
;
;HOST DISK BLOCKING/DE-BLOCKING DATA AREA
;
;
;(FOLLOWING TWO VARIABLES MUST BE KEPT TOGETHER HERE TO PERMIT WORD
; REFERENCE TO THEM DURING THE DISK HOME ROUTINE)
;
TEMPMHD	RB	1			;TEMP STORAGE FOR CURRENT TYPE OF DRIVE;
TEMPDSK	RB	1			;TEMPORARY STORAGE FOR LOGGED DISK
;
MINIHARD RB	1			;FLAG FOR MINI OR HARD DISK SELECTED
;
SEKMHD	RB	1			;FLAG FOR SELECTED DRIVE TYPE FLAG
SEKDSK	RB	1			;SEEK DISK NUMBER
SEKTRK	RW	1			;SEEK TRACK NUMBER
SEKSEC	RB	1			;SEEK SECTOR NUMBER
;
	EJECT
;(FOLLOWING TWO VARIABLES MUST BE KEPT TOGETHER HERE TO PERMIT WORD
; REFERENCE TO THEM DURING THE DISK HOME ROUTINE)
;
HSTMHD	RB	1			;HOST DISK TYPE FLAG
HSTDSK	RB	1			;HOST DISK NUMBER
HSTTRK	RW	1			;HOST TRACK NUMBER
HSTSEC	RB	1			;HOST SECTOR NUMBER
;
SEKHST	RB	1			;SEEK SHR SECSHF
HSTACT	RB	1			;HOST ACTIVE FLAG
HSTWRT	RB	1			;HOST WRITTEN FLAG
;
UNACNT	RB	1			;UNALLOCATED RECORD COUNT
UNADSK	RB	1			;LAST UNALLOCATED DISK
UNATRK	RW	1			;LAST UNALLOCATED TRACK
UNASEC	RB	1			;LAST UNALLOCATED SECTOR
;
ERFLAG	RB	1			;ERROR REPORTING
RSFLAG	RB	1			;READ SECTOR FLAG
READOP	RB	1			;1 IF READ OPERATION
WRTYPE	RB	1			;WRITE OPERATION TYPE
DMAADR	RW	1			;DISK DMA TRANSFER ADDRESS
;
DEFDMASG	RW	1		;STORAGE FOR DMA SEGMENT VALUE
IOBYTE		RB	1		;STORAGE FOR IOBYTE
;
CONPTR		RW	1		;CONSOLE BUFFER FILLING POINTER
CONBUF		RW	1		;CONSOLE BUFFER ADDRESS STORAGE
CONSEMAP	RB	1		;CONSOLE BUFFER ACCESS SEMAPHORE
;
LPTPTR		RW	1		;PRINTER BUFFER FILLING POINTER
LPTBUF		RW	1		;PRINTER BUFFER ADDRESS STORAGE
LPTSEMAP	RB	1		;PRINTER BUFFER ACCESS SEMAPHORE
;
ENDZ	EQU	OFFSET $		;END OF ZEROED AREA
;
	EJECT
;******************************************************************************
;	MEMMORY REGION TABLE (MAX 5 MEMMORY BLOCKS)
;******************************************************************************
;
MTR		DB	01		;NBR OF MEMMORY REGIONS
		DW	STARTTPA	;START OF FIXED SEGMENT
		DW	1000H-STARTTPA	;LENGTH OF FIXED SEGMENT
		RW	4*2		;MAX 4 EXTRA SEGMENTS MAY BE SPECIFIED
;
;
;
;HOST DATA BUFFER  MEMORY AREA MUST BE ACCESSABLE VIA
;DMA AND BY THE HARD DISK CONTROLLER BOARD.
;
;
HSTBUF	RB	HSTSIZ			;HOST BUFFER
;
;
;
;SCRATCH RAM AREA FOR BDOS USE
;
;	ENDEF				;LET DISKDEF FIXUP BDOS BUFFERS
;
;
;
;
;
;
;MINI FLOPPY PHYSICAL SECTOR TRANSLATE TABLE
;
MFSTRAN	DB	1,5,9,13,3,7,11,15
;
	EJECT
;***********************************************************************
;	MSC 9205 HARD DISK CONTROLLER I/O PARAMETER BLOCK
;***********************************************************************
;
;THIS MUST BE IN MEMORY ACCESSABLE BY DISK CONTROLLER
;VIA DMA OPERATION
;
;
IOPB	EQU	OFFSET $		;INPUT/OUTPUT PARAMETER BLOCK
;
INCTL	DB	010H			;INTERRUPT CONTROL BYTE
					;   SEQUENTIAL FORMATTING
					;   NO INTERRUPTS ENABLED
					;   BYTE TRANSFERS SELECTED
INSTR	DB	0			;DISK INSTRUCTION BYTE
					;   ASSEMBLED PRIOR TO EXECUTION
RECNT	DB	1			;RECORD COUNT
					;   ONE RECORD NORMALLY TRANSFERRED
CYL	DB	0			;CYLINDER ADDRESS
					;   PUT IN PRIOR TO EXECUTION
REC	DB	1			;RECORD ADDRESS
					;   PUT IN PRIOR TO EXECUTION
DMAOFST	DW	OFFSET HSTBUF		;DMA OFFSET ADDRESS 
					;   SET TO SINGLE RECORD BUFFER
DMASEG	DW	1040H			;DMA SEGMENT ADDRESS
					;   SET TO GLOBAL CP/M-86 BASE
IOPBEND	EQU	OFFSET $
;
;
;
;
;HARD DISK CONTROLLER COMMAND SENDING RESET FLAG COUNTER
;
RSTFLG	DB	0			;FLAG TO COUNT 9205 RESETS;
;
	EJECT
;***********************************************************************
;	MINI FLOPPY DRIVE ACCESS PARAMETER BLOCK
;***********************************************************************
;
FDIOPB	EQU	OFFSET $
;
	DB	ENTCLDU			;ENTIRE COMMAND LDU
	DB	COMOPC			;COMMAND BUFFER FLAG
FINST	DB	0			;DISK ACCESS INSTRUCTION BYTE 
FUNIT	DB	0			;SELECTED UNIT NUMBER
FSIDE	DB	0			;SELECTED SIDE NUMBER
FTRK	DB	0			;SELECTED TRACK NUMBER
FSEC	DB	1			;SELECTED SECTOR NUMBER
FCNT	DB	2			;SELECTED 256 BYTE SECTOR COUNT
XFRPNT	DW	OFFSET HSTBUF		;READ/WRITE BUFFER OFFSET
XFRSEG	DW	1040H			;READ/WRITE BUFFER SEGMENT BASE
;
ENDFDIO	EQU	OFFSET $
;
FSIZE	DB	00			;SECTOR SIZE DEFAULT TO 256 BYTES
;
;
;
	RW	32			;INITIALIZATION STACK
STKBASE	DB	0			; ***** END OF OS *****
;
;
;
;DEFINE TPA SEGMENT START ADDRESS
;
STARTTPA	EQU	((OFFSET STKBASE)+15+400H)/16
	EJECT
;
;	DEFINE INTERRUPT VECTORS
;
	DSEG	0
	ORG	0
;
INT0OFF	RW	1
INT0SEG	RW	1
STEPOFF	RW	1
STEPSEG	RW	1
NMIOFF	RW	1
NMISEG	RW	1
TYP3OFF	RW	1
TYP3SEG	RW	1
OVRFOFF	RW	1
OWRFSEG	RW	1
;
	ORG	RTC
RTCOFF	RW	1
;
	ORG	378H
BIOSOFF	RW	1
;
	ORG	380H
BDOSOFF	RW	1
BDOSSEG	RW	1
;
	END
«eof»