
;*********************************************
;*                                           *
;*     IBM-PC Basic I/O System (PCBIOS)      *
;*                                           *
;*     For CPM-86 v1.1                       *
;*                                           *
;*********************************************
;
; 31/12/1998
;
;   Code/data yet to comment....
;1. 3fef - setting i/o routines pointer (byte 47e1)
;2. SOSTATC	TEST	CL,AH
;3. VIDCOM -label calls to
;4. 4980 offset ??
;5. chkcp3 - comment
;6. chkcp4 - comment
;7. 2be5
;8. 3d52
;
; BUGS in original code
; ---------------------
; 1. labeled 'BUG DI' - Incorrectly using DI instead of SI
;
;#rcpmx.xxx
;
;  Start      End
;139D:0000 139D:47FF
;#l2500,47ff


; Ascii characters

CTRLC		EQU	03H
BEL		EQU	07H
BS		EQU	08H
LF		EQU	0AH
CR		EQU	0DH
ESC		EQU	1BH

; Logical

TRUE		EQU	0FFH
FALSE		EQU	0

; Addresses

CCP_OFFSET	EQU	0000H
BDOS_OFST	EQU	0B06H		; BDOS entry point
BIOS_CODE	EQU	2500H

;*********************************************
;*                                           *
;*            Dummy Data Section             *
;*                                           *
;*           for Interrupt Vectors           *
;*                                           *
;*********************************************

		DSEG	0		; MEMORY ZERO PAGE

		ORG 	0		;
ZEROOFF		RW	1		; DIV BY ZERO OFFSET
ZEROSEG		RW	1		;  AND SEGMENT

		ORG	4*01BH
BREAKOFF	RW	1		; USER BREAK OFFSET
BREAKSEG	RW	1		;  AND SEGMENT

TIMEROFF	RW	1		; TIMER INT OFFSET
TIMERSEG	RW	1		;  AND SEGMENT

		ORG	4*01EH
I1EOFF		RW	1		; DISK PARM POINTER OFFSET
I1ESEG		RW	1		;  AND SEGMENT

		ORG	4*0E0H
BDOSOFF		RW	1		; BDOS ENTRY OFFSET
BDOSSEG		RW	1		;  AND SEGMENT

		ORG	4*0E6H
UNDSKOFF	RW	1		; UNKNOWN DISK DRIVE INT OFFSET
UNDSKSEG	RW	1		;  AND SEGMENT

;*********************************************
;*                                           *
;*            Dummy Data Section             *
;*                                           *
;*              for Page 0040H               *
;*                                           *
;*********************************************

		DSEG

		ORG	10H
BIOSHDW		RB	1		; BIOS hardware setting

;*********************************************
;*                                           *
;*            Dummy Data Section             *
;*                                           *
;*               for BDOS Data               *
;*                                           *
;*********************************************

		DSEG

		ORG	24A5h
BDOSUSER	RB	1		; USER NUM HIDES HERE

		ORG	24AFh
BDOSMODABT	RB	1		; BDOS ABORT MODE

		ORG	24B7h
BDOSCURDRV	RB	1		; BDOS CURRENT DRIVE

		ORG	24BDh
DHOUR		RB	1		; DECIMAL HOURS
DMINUTE		RB	1		; DECIMAL MINUTES
DSECOND		RB	1		; DECIMAL SECONDS
;
AMONTH		RW	1		; ASCII MONTH
		RB	1		; /
ADAY		RW	1		; ASCII DAY
		RB	1		; /
AYEAR		RW	1		; ASCII YEAR
		RB	1		; ,
AHOUR		RW	1		; ASCII HOURS
		RB	1		; :
AMINUTE		RW	1		; ASCII MINUTES
		RB	1		; :
ASECOND		RW	1		; ASCII SECONDS
		RB	1

SYSMESSAGE	RB	14		; SYS STATUS MESSAGE
BDOSCONWID	RB	1		; BDOS CONSOLE WIDTH

;*********************************************
;*                                           *
;*            Dummy Code Section             *
;*                                           *
;*           for CCP entry points            *
;*                                           *
;*********************************************

	CSEG
	ORG	ccpoffset

CCP:

;*********************************************
;*                                           *
;*               Code Section                *
;*                                           *
;* BIOS Jump Vector for Individual Routines  *
;*                                           *
;*********************************************

	CSEG
        ORG     bios_code
	JMP	INIT		; Enter from BOOT ROM or LOADER
	JMP	WBOOT		; Arrive here from BDOS call 0
	JMP	CONST		; Return console keyboard status
	JMP	CONIN		; Return console keyboard char
	JMP	CONOUT		; Write char to console device
	JMP	LISTOUT		; Write character to list device
	JMP	PUNCH		; Write character to punch device
	JMP	READER		; Return char from reader device
	JMP	HOME		; Move to trk 00 on cur sel drive
	JMP	SELDSK		; Select disk for next rd/write
	JMP	SETTRK		; Set track for next rd/write
	JMP	SETSEC		; Set sector for next rd/write
	JMP	SETDMA		; Set offset for user buff (DMA)
	JMP	READ		; Read a 128 byte sector
	JMP	WRITE		; Write a 128 byte sector
	JMP	LISTST		; Return list status
	JMP	SECTRAN		; Xlate logical->physical sector
	JMP	SETDMAB		; Set seg base for buff (DMA)
	JMP	GETSEGT		; Return offset of Mem Desc Table
	JMP	GETIOB		; Return I/O map byte (IOBYTE)
	JMP	SETIOB		; Set I/O map byte (IOBYTE)

	RS	15		; Reserved - 5 more JMP's

				; Pointers to
	DW	Offset PFKTABLE	;  - Function keys data
	DW	Offset DISK	;  - Disk parameters
	DW	Offset SER1	;  - Serial port init parameters
	DB	010h,033h	;  - Device data
	DW	Offset FIDDSMEM	;  - FIDD data
	DW	Offset CRTMOD	;  - Screen data

	DB	'Copyright (C) 1983, Digital Research'

;----------------------------------------
; Arrive here from BDOS call 0
;
WBOOT:
	MOV	Byte Ptr X3A70,0	; Default drive on startup ?
	MOV	Byte Ptr X3A6D,0FFh	; Disk error or change ???
	JMP	CCP+6			; Jump to cold start entry of CCP

;*********************************************
;*                                           *
;*   CP/M Character I/O Interface Routines   *
;*                                           *
;*********************************************
; Return console keyboard char
;
;   Input: none
;   Output: AL:=char AND 07FH
;
CONIN:
	MOV	AX,Word Ptr SYS_CONI
	JMPS	XINPUT
;
; Write char to console device
;
;   Input: CL:=char
;   Output: none
;
CONOUT:
	MOV	AX,Word Ptr SYS_CONO
	JMPS	XOUTPUT
;
; Return console keyboard status
;
;   Input: none
;   Output: IF ready THEN AL:=0FFH
;                    ELSE AL:=00
;
CONST:
	MOV	AX,Word Ptr SYS_CONI
	MOV	CL,01h
	JMPS	XOSTATUS
;
; Return char from reader device
;
;   Input: none
;   Output: AL:=char AND 07FH
;
READER:
	MOV	AX,Word Ptr SYS_AUXI
	JMPS	XINPUT
;
; Write character to punch device
;
;   Input: CL:=char
;   Output: none
;
PUNCH:
	MOV	AX,Word Ptr SYS_AUXO
	JMPS	XOUTPUT
;
; Write character to list device
;
;   Input: CL:=char
;   Output: none
;
LISTOUT:
	MOV	AX,Word Ptr SYS_LST
	JMPS	XOUTPUT
;
; Return list status
;
;   Input: none
;   Output: IF ready THEN AL:=0FFH
;                    ELSE AL:=00
;
LISTST:
	MOV	AX,Word Ptr SYS_LST
	MOV	CL,20h
	JMPS	XOSTATUS
;
;	Common character input routine
;
XINPUT:	CLD
	MOV	BX,Offset INROUTINES
	MOV	CX,11			; Only 11 possible routines
	SUB	DX,DX
XINPUT1:
	ROR	AX,1h
	JNB	XINPUT2
	JMP	Word Ptr [BX]		; Transfer to first routine
XINPUT2:
	INC	BX			; Next routine
	INC	BX
	LOOP	XINPUT1
	JMP	KEYIN			; No more - assume keyboard
;
;	Common character output routine
;
XOUTPUT:
	CLD
	MOV	BX,Offset OUTROUTINES
	MOV	CH,11			; Only 11 possible routines

XOUTPUT1:
	ROR	AX,1			; Is this it
	JNB	XOUTPUT2		; No
	PUSH	AX
	PUSH	BX
	PUSH	CX
	SUB	DX,DX
	CALL	Word Ptr [BX]		; Transfer to that routine
	POP	CX
	POP	BX
	POP	AX
XOUTPUT2:
	INC	BX			; Check for next
	INC	BX
	DEC	CH			; Any more?
	JNZ	XOUTPUT1		; Try again
	RET				; Now leave
;
;
; Common input status
;
; Routine to return status of CONSOLE or LIST device
;
XOSTATUS:
	MOV	BX,Offset OUTSTATUS
	MOV	CH,0Bh
	SUB	DX,DX
XOSTAT1:
	ROR	AL,1
	JB	XOSTAT2
	INC	BX
	INC	BX
	DEC	CH
	JNZ	XOSTAT1
	MOV	AL,0FFh
	RET
XOSTAT2:
	JMP	Word Ptr [BX]
;
;
;	Keyboard status
;	Exit:	AL = 0ffh if char ready
;		AL = 0 if not ready
;
KEYSTAT:
	PUSH	SI
	MOV	SI,Word Ptr KEYPTR	; Check PFK buffer
	MOV	AL,[SI]			; Char from buffer
	POP	SI
	TEST	AL,AL			; Valid if not zero
	JNZ	KEYSTAT1		; Skip if readys
;
	MOV	AH,01h			; Read keyboard status
	INT	16h			; PC ROM BIOS Keyboard Service
	MOV	AL,0			; Assume not readys
	JZ	KEYSTAT2
KEYSTAT1:
	MOV	AL,0FFh			; Character readys
KEYSTAT2:
	RET
;
;
;	Keyboard input
;	Exit:	AL = char
;
KEYIN:	PUSH	SI

KEYIN1:	MOV	SI,Word Ptr KEYPTR	; Check key buffer
	LODSB				; Get the character
	TEST	AL,AL			; Return buf char
	JZ	KEYIN3			; Else read keyboard
	MOV	Word Ptr KEYPTR,SI
KEYIN2:	POP	SI
	RET

KEYIN3:	SUB	AH,AH			; Read char from keyboard
	INT	16h			; PC ROM BIOS Keyboard Service
	AND	AL,7Fh
	JNZ	KEYIN2			; Return if ascii
;
;	Check for programmable function keys
;
	PUSH	CX
	PUSH	DI
	PUSH	ES
	MOV	AL,AH			; Get key code
	CALL	GETPFK			; AX -> pfk entry
	JC	KEYIN4			; C set if illegal
;
	MOV	SI,AX			; Table pointer
	MOV	DI,Offset KEYBUF
	MOV	Word Ptr KEYPTR,DI	; Checked at entry
	REP	MOVSB			; Move to keybuffer
KEYIN4:	POP	ES
	POP	DI
	POP	CX
	JMP	KEYIN1			; Start it
;
;	Point to PFK entry
;	Entry:	AL = key code
;	Exit:	AX = pointer to table entry
;		CX = Length of table entry
;		Carry = not a legal PFK
;
GETPFK:	MOV	DI,CS
	MOV	ES,DI
	MOV	DI,Offset PFKSCAN	; Function key scancode table ?
	MOV	CX,20
	REPNE	SCASB  
	STC				; Assume illegal
	JNZ	GETPFK2			; Exit if not found
;
	MOV	AX,20
	MUL	CL
	ADD	AX,Offset PFKTABLE	; Offset to table entry
	MOV	CX,20			; Length of table entry
GETPFK2:
	RET
;
;
;	Video output routine
;
VIDOUT:	JMP	Word Ptr STATE
;
;
;	Null output routine
;
NULLST:
	MOV	AL,0FFh
	RET
;
;	Normal ascii video vector
;	Character is in the AL register (CL on entry)
;
VIDNORM:
	MOV	AL,CL
VIDNORM1:
	MOV	DX,Word Ptr CURSOR	; Collect cursor position

	CMP	AL,BS
	JZ	VIDBS			; Back space
	CMP	AL,LF
	JZ	VIDLF			; Line feed
	CMP	AL,CR
	JZ	VIDCR			; Carriage return

	CMP	AL,ESC
	JZ	VIDESC			; Escape
	CMP	AL,BEL
	JZ	VIDBEL			; Beep
;
;	Printable ascii characters fall through
;
VIDASCII:
	MOV	AH,09h			; Write code
	MOV	BH,0			; Page 0
	MOV	CX,1			; One copy of char
	MOV	BL,Byte Ptr ATTRIB	; Attribute
	CALL	VIDCOM			; Write it
;
	INC	DL			; Next column
	CMP	DL,Byte Ptr CRTCOL	; Past the edge ?
	JB	SETCURSOR		; If not, skip
;
;	Test for end of line wrap around
;
	TEST	Byte Ptr WRAPFLAG,TRUE
	JZ	VIDRETURN		; If no wrap, done
;
	CALL	VIDCR			; Else, do a return
					; Fall into a line feed
;
;	Video line feed
;
VIDLF:	INC	DH			; Next line
	CMP	DH,18h			; At page end?
	JB	SETCURSOR		; If not, skip

	MOV	AX,0601h		; Scroll one line
	SUB	CX,CX			; Upper left corner
	JMP	VDASLIN2		; Do it
;
;	Video carriage return
;
VIDCR:	MOV	DL,0			; First column
;
SETCURSOR:
	MOV	Word Ptr CURSOR,DX	; Update our copy
	MOV	AH,2			; Set cursor code
	MOV	BH,0			; Page 0
	CALL	VIDCOM
	RET
;
;	Video back space
;
VIDBS:	DEC	DL			; Back one column
	JNS	SETCURSOR		; If pos, no prob
;
	TEST	Byte Ptr WRAPFLAG,TRUE	; If no wrap around
	JZ	VIDRETURN		;  Then forget it
;
	DEC	DH			; If top row,
	JS	VIDRETURN		;  Then forget it
	MOV	DL,Byte Ptr CRTCOL	; Else wrap around
	DEC	DL
	JMPS	SETCURSOR

VIDBEL:	MOV	SI,Offset SNDTBL1
	JMP	BEEP
;
;	Video escape - set up state vector
;
VIDESC:	MOV	Word Ptr STATE,Offset VIDESC1
;
VIDRETURN:
	RET
;
;	Character after escape comes here
;
VIDESC1:
	CALL	VPCOMP			; Clear the STATE incase
DISPATCH:
	MOV	DX,Word Ptr CURSOR	; Setup the cursor
	PUSH	DI
	PUSH	ES
	MOV	DI,CS
	MOV	ES,DI
	MOV	DI,Offset ESCTBL	; Point to search table
	MOV	AL,CL
	MOV	CX,0029h
	REPNE	SCASB			; Search ESC char table
	POP	ES
	POP	DI
	JZ	DISPATCH1		; Found
	JMP	VIDASCII		; No, must be printable
DISPATCH1:
	MOV	BX,Offset ESCJMP	; Point to routine table
	ADD	CX,CX
	SUB	BX,CX
	JMP	Word Ptr [BX]		; Transfer to it
;
;	ESC H - home cursor
;
VHOME:
	SUB	DX,DX			; 0,0 = Top left
SETCUR:	JMP	SETCURSOR		; For local short
;
;	ESC E - erase screen and home cursor
;
VEOSALL:
	CALL	VHOME			; Get to top left
	SUB	CX,CX
	MOV	DH,17h
	MOV	DL,Byte Ptr CRTCOL
	DEC	DL
	JMP	VEOLEND1		; Erase to end of screen
;
;	ESC A - cursor up
;
VUP:	DEC	DH			; Row = row - 1
	JNS	SETCUR			; If pos, set cursor
	RET				; Else forget it
;
;	ESC B - cursor down
;
VDOWN:	INC	DH			; Row = row + 1
	CMP	DH,18h			; Too far ?
	JB	SETCUR			; If not, set cursor
	RET				; Else ignore
;
;	ESC C - cursor forward (right)
;
VFORW:	INC	DL			; Col = col + 1
	CMP	DL,Byte Ptr CRTCOL	; Too far ?
	JB	SETCUR			; If not, set cursor
	RET				; Else ignore
;
;	ESC D - cursor back (left)
;
VBACK:	DEC	DL			; Col = col - 1
	JNS	SETCUR			; Not to far, set cursor
	RET				; Else ignore
;
;	ESC I - cursor up (scroll at top)
;
VUPSAT:	TEST	DH,DH			; Top of screen ?
	JNZ	VUP			; No, cursor up
	JMP	VIASLIN			; Yes, insert line and scroll down
;
;	ESC j - save cursor position
;
VSAVCUR:
	MOV	Word Ptr SAVECURSOR,DX	; Stash it
	RET
;
;	ESC k - restore (saved) cursor position
;
VRSTCUR:
	MOV	DX,Word Ptr SAVECURSOR	; Get it back
	JMP	SETCUR			; and set it
;
;	ESC Y - set cursor position
;
VSETCUR:
	MOV	Word Ptr STATE,Offset VSET1
	RET				; Look for next char
;
;	ESC Y part 1 - set cursor row
;
VSET1:	SUB	CL,20h			; Correct
	JNS	VSET1A			; Skip if positive
	MOV	CL,0			; Else top row
VSET1A:	CMP	CL,18h
	JB	VSET1B			; Skip if ok
	MOV	CL,17h			; Else page bottom
VSET1B:	MOV	Byte Ptr CURROW,CL	; Save cursor row
	MOV	Word Ptr STATE,Offset VSET2
	RET				; Ready for column
;
;	ESC Y part 2 - set cursor column
;
VSET2:	SUB	CL,20h			; Correct
	JNS	VSET2A			; Skip if positive
	MOV	CL,0			; Else first col
VSET2A:	CMP	CL,Byte Ptr CRTCOL
	JB	VSET2B			; Skip if ok
	MOV	CL,Byte Ptr CRTCOL	; Else last col
	DEC	CL
VSET2B:	MOV	DX,Word Ptr CURSOR	; Get the value
	MOV	DL,CL			; Update row
	CALL	VPCOMP			; Reset STATE
	JMP	SETCURSOR		; Move cursor now
;
;	ESC J - erase to end of screen (from cursor)
;
VEOSEND:
	PUSH	DX			; Save cursor
	CALL	VEOLEND			; Erase to end of line
	POP	DX
	CMP	DH,17h
	JB	VEOSEND1
	RET
;
VEOSEND1:
	INC	DH
	MOV	CH,DH
	MOV	CL,0
	MOV	DH,17h
	MOV	DL,Byte Ptr CRTCOL
	DEC	DL
	JMPS	VEOLEND1		; Erase to end of screen
;
;	ESC l - Erase current line (all characters)
;
VECLIN:
	MOV	CH,DH
	MOV	CL,0
	MOV	DL,Byte Ptr CRTCOL
	DEC	DL
	JMPS	VEOLEND1		; Erase to end of line
;
;	ESC o - erase beginning of line to cursor
;
VEOLBEG:
	MOV	CH,DH
	MOV	CL,0
	DEC	DL
	JNS	VEOLEND1		; Erase to end of screen
	RET
;
;	ESC K - erase to end of line
;
VEOLEND:
	MOV	CX,DX
	MOV	DL,Byte Ptr CRTCOL
	DEC	DL

VEOLEND1:
	MOV	BH,Byte Ptr ATTRIB
	AND	BH,Byte Ptr X36B8
	MOV	AX,0700h
	JMP	VIDCOM
;
;	ESC L - insert line and scroll down
;
VIASLIN:
	MOV	AX,0701h
	JMPS	VDASLIN1
;
;	ESC M - delete line and scroll up
;
VDASLIN:
	MOV	AX,0601h
;
VDASLIN1:
	CMP	DH,17h
	JZ	VECLIN			; Erase current line (all characters)
	MOV	CH,DH
	MOV	CL,0
;
VDASLIN2:
	MOV	DH,17h
	MOV	DL,Byte Ptr CRTCOL
	DEC	DL
	MOV	BH,Byte Ptr ATTRIB
	AND	BH,Byte Ptr X36B8
	JMP	VIDCOM
;
;	ESC N - delete character at cursor (moving other charcters to fill gap)
;
VDELCHR:
	MOV	AL,Byte Ptr CRTCOL
	SUB	AL,DL
	CBW
	MOV	DI,AX
	CALL	VSETCUR3
	MOV	DL,Byte Ptr CRTCOL
	DEC	DL
	MOV	SI,0720h
	MOV	BH,0
	MOV	CX,0001h
VDELCHR1:
	MOV	AH,02h
	CALL	VIDCOM
	MOV	AH,08h
	CALL	VIDCOM
	XCHG	AX,SI
	MOV	BL,AH
	MOV	AH,09h
	CALL	VIDCOM
	DEC	DL
	DEC	DI
	JNZ	VDELCHR1
	JMP	VRSTCPT			; Restore cursor posn and type
;
;	ESC a - set video mode
;
VSETMODE:
	MOV	Word Ptr STATE,Offset VSETMODE1
	RET				; Get next char
;
;	ESC a (mode) - set video mode
;
VSETMODE1:
	MOV	AL,CL			; Required mode
	CALL	SETCURCRT		; Set current video details
	CALL	VSETCUR3
	PUSH	ES
	MOV	AX,0040h
	MOV	ES,AX
	MOV	AL,ES:Byte Ptr BIOSHDW
	AND	AL,0CFh
	OR	AL,Byte Ptr CRTBVM	; Set BIOS video mode
	MOV	ES:Byte Ptr BIOSHDW,AL	; Store BIOS equipment work
	POP	ES
	MOV	AL,Byte Ptr CRTMOD	; Mode
	MOV	AH,0
	CALL	VIDCOM
	CALL	VEOSALL			; Erase screen and home cursor
	CALL	CLRBOS1			; Clear status line
	CALL	VRSTCPT			; Restore cursor posn and type
	JMP	VPCOMP			; Reset STATE
;
;	ESC b - set foreground colour
;
VFRGND:	MOV	Word Ptr STATE,Offset VFRGND1
	RET				; Get next char
;
;	ESC b (colour) - set foreground colour
;
VFRGND1:
	MOV	AL,CL
	MOV	AH,Byte Ptr ATTRIB
	JMPS	VFGBG			; Common routine
;
;	ESC c - set background colour
;
VBKGND:	MOV	Word Ptr STATE,Offset VBKGND1
	RET				; Get next char
;
;	ESC c (colour) - set background colour
;
VBKGND1:
	MOV	AH,CL
	MOV	CL,04h
	ROL	AH,CL			; Move to MSB's
	MOV	AL,Byte Ptr ATTRIB

VFGBG:	AND	AL,0Fh			; LSB's only
	AND	AH,0F0h			; MSB's only
	OR	AL,AH			; Mash 'em
	MOV	Byte Ptr ATTRIB,AL
	JMP	VPCOMP			; Reset STATE
;
;	ESC d - redirect console input
;
VNEWCONI:
	MOV	AX,Offset SYS_CONI
	JMPS	VNEW
;
;	ESC e - redirect console output
;
VNEWCONO:
	MOV	AX,Offset SYS_CONO
	JMPS	VNEW
;
;	ESC f - redirect auxilary input
;
VNEWAUXI:
	MOV	AX,Offset SYS_AUXI
	JMPS	VNEW
;
;	ESC g - redirect auxilary output
;
VNEWAUXO:
	MOV	AX,Offset SYS_AUXO
	JMPS	VNEW
;
;	ESC h - redirect list output
;
VNEWLST:
	MOV	AX,Offset SYS_LST
;	;JMPS	VNEW			; Fall through
;
;	Redirect any i/o device vector
;	Entry: AX -> a particular vector
;
VNEW:	MOV	Word Ptr DELTAVEC,AX	; Vector to change
	MOV	Word Ptr STATE,Offset VNEW1
	RET				; Wait for next char
;
;	Next state
;
VNEW1:	MOV	Byte Ptr DELTALO,CL
	MOV	Word Ptr STATE,Offset VNEW2	; Next time
	RET				; Wait for one more
;
;	Last state
;
VNEW2:	MOV	BX,Word Ptr DELTAVEC	; Get the vector to change
	MOV	AH,CL
	MOV	AL,Byte Ptr DELTALO
	AND	AX,7F7Fh		; Mask off
	MOV	Word Ptr [BX],AX	; Set vector
	JMP	VPCOMP			; Reset STATE
;
;	ESC m - turn cursor on
;
VCURON:	MOV	CX,Word Ptr CURLON	; Normal lines (on)
	JMPS	VSETCUR1
;
;	ESC n - turn cursor off
;
VCUROFF:
	MOV	CX,Word Ptr CURLOFF	; Cursor lines (off)
;
VSETCUR1:
	MOV	Word Ptr CURCUR,CX	; Save current cursor type
VSETCUR2:
	MOV	AH,1			; Set cursor type
	JMP	VIDCOM			; Do it
;
VSETCUR3:
	MOV	CX,Word Ptr CURLOFF
	JMPS	VSETCUR2
;
;	Restore cursor position and type
;
VRSTCPT:
	MOV	DX,Word Ptr CURSOR
	CALL	SETCURSOR
	MOV	CX,Word Ptr CURCUR	; Current (in use) cursor type
	JMPS	VSETCUR2
;
;	ESC p - reverse video on
;
VREVON:	AND	Byte Ptr ATTRIB,0F8h	; Black foreground
	OR	Byte Ptr ATTRIB,070h	; White background
	RET
;
;	ESC q - reverse video off
;
VREVOFF:
	AND	Byte Ptr ATTRIB,8Fh
	OR	Byte Ptr ATTRIB,07h
	RET
;
;	ESC s - turn blinking text on
;
VBLINKON:
	OR	Byte Ptr ATTRIB,80h
	RET
;
;	ESC t - turn blinking text off
;
VBLINKOFF:
	AND	Byte Ptr ATTRIB,7Fh
	RET
;
;	ESC r - turn high intensity text on
;
VINTON:	MOV	AL,0Eh
	JMPS	VINTCHG
;
;	ESC u - turn high intensity text off
;
VINTOFF:
	MOV	AL,07h

VINTCHG:
	AND	Byte Ptr ATTRIB,0F0h
	OR	Byte Ptr ATTRIB,AL
	RET
;
;	ESC v - text wrap at end of line
;
VWRAP:	MOV	Byte Ptr WRAPFLAG,TRUE
	RET
;
;	ESC w - no text wrap at end of line
;
VNOWRAP:
	MOV	Byte Ptr WRAPFLAG,0
	RET
;
;	ESC x  - set MONO80 mode
;
VMON80:
	MOV	CL,03h
	JMP	VSETMODE1
;
;	ESC y - set COL80 mode
;
VCOL80:
	MOV	CL,07h
	JMP	VSETMODE1
;
;	ESC / - perform INT 10h function 0Bh
;
VSETPAL:
	MOV	Word Ptr STATE,Offset VSETPAL1
	RET				; Ready for next char

VSETPAL1:
	MOV	Byte Ptr VSETPALBH,CL	; Save BH value
	MOV	Word Ptr STATE,Offset VSETPAL2
	RET

VSETPAL2:
	MOV	BL,CL			; Get BL value
	MOV	BH,Byte Ptr VSETPALBH	; and BH value
	AND	BX,7F7Fh
	MOV	AH,0Bh
	CALL	VIDCOM
	JMP	VPCOMP			; Reset STATE and return
;
;	ESC : - set programmable function keys
;
;
VPFK:	MOV	Word Ptr STATE,Offset EPFK1
	RET				; Ready for key code
;
;	Get key scan code
;
EPFK1:	CALL	VPCOMP			; Reset STATE (default)
	PUSH	DI
	PUSH	ES
	MOV	AL,CL
	CALL	GETPFK			; AX -> pfk entry
	JC	EPFK1A			; C set if illegal
;
	MOV	Word Ptr EPFKPTR,AX	; Save pointer
	MOV	Byte Ptr EPFKCOUNT,CL	; Save char count
	MOV	Word Ptr STATE,Offset EPFK2
EPFK1A:	POP	ES
	POP	DI
	RET
;
;	Get the string
;
EPFK2:	MOV	BX,Word Ptr EPFKPTR	; Point to entry
	MOV	Byte Ptr [BX],CL	; Store char
;
	DEC	Byte Ptr EPFKCOUNT
	JZ	EPFK2A			; Skip if failed
;
	TEST	CL,CL			; Or if char = 0
	JZ	EPFK2A
;
	INC	BX			; Bump the pointer for next time
	MOV	Word Ptr EPFKPTR,BX
	RET				; Keep VIDSTATE
;
;	Process complete
;
EPFK2A:	JMPS	VPCOMP			; Reset STATE to normalcy
;
;	ESC 1 - TURN ON/CLEAR STATUS LINE AT BOTTOM OF SCREEN
;
VBOSON:
	MOV	AL,Byte Ptr CRTMOD	; Current mode
	MUL	Byte Ptr CRTTEL
	XCHG	AX,BX			; Calc offset into SCRMODTBL
	MOV	AL,Byte Ptr SCRMODTBL+4[BX]	; Get STATLINE available flag
	MOV	Byte Ptr BOSFLAG,AL		; set as current
	RET
;
;	ESC 0 - TURN OFF/CLEAR STATUS LINE AT BOTTOM OF SCREEN
;
VBOSOFF:
	MOV	Byte Ptr BOSFLAG,FALSE
	JMP	CLRBOS2			; Clear status line
;
;	ESC process complete
;
VPCOMP:	MOV	Word Ptr STATE,Offset VIDNORM
        RET                             ; To normalacy

;----------------------------------------
;
;	Common video i/o routine
;
VIDCOM:
	MOV	Byte Ptr VIDBSY,TRUE	; Flag Video Busy
	INT	10h			; PC ROM BIOS Video Driver Service
	CLI
	MOV	Byte Ptr VIDBSY,FALSE	; Clear Busy Flag
	TEST	Word Ptr X36F3,8000h
	JZ	VIDCOM1
	INC	Word Ptr X36F3
	INT	1Ch			; PC ROM BIOS System timer
VIDCOM1:
	STI
	RET

;----------------------------------------
;
;	Printer output - set the device
PRNOUT2:
	INC	DX
PRNOUT1:
	INC	DX
PRNOUT0:

;----------------------------------------
;
;	Printer character out
;	Entry:	CL = char
;		DX = printer number
;
PRNOUT:
	MOV	CH,2			; Retry count
PRNO1:	MOV	AH,0			; Write char
	MOV	AL,CL
	INT	17h			; PC ROM BIOS - Printer service
	TEST	AH,08h			; Not busy ?
	JZ	PRNOX
;
	TEST	AH,01h			; Timeout ?
	JZ	PRNO2
	DEC	CH
	JNZ	PRNO1			; Try again
;
PRNO2:	PUSH	CX
	PUSH	DX
	MOV	SI,Offset PERR		; 'Printer error'
	MOV	DX,Offset PERRPO	; 'Power off'
	TEST	AH,80h
	JNZ	PRNO3
	MOV	DX,Offset PERROP	; 'Out of paper'
	TEST	AH,20h
	JNZ	PRNO3
	MOV	DX,Offset PERRTO	; 'Time out'
	TEST	AH,01h
	JNZ	PRNO3
	MOV	DX,Offset PERRSP	; space
PRNO3:	CALL	DSKERR3			; print errors SI and DX ?
	POP	DX
	POP	CX
	JZ	PRNOUT
	JNB	PRNOX
	JMP	ABORT			; We're gone
PRNOX:	RET

;----------------------------------------
;
;	Printer status - set the device number
;
PRNSTAT2:
	INC	DX
PRNSTAT1:
	INC	DX

PRNSTAT0:
;
;----------------------------------------
;	Return printer status
;	Entry:	DX = printer number
;	Exit:	AL = 0ffh if printer ready
;		AL = 0 if printer busy

PRNSTAT:
	MOV	AH,2			; Get printr status
	INT	17h			; PC ROM BIOS - Printer service
	MOV	AL,AH
	AND	AL,0B9h			; Interresting bits
	CMP	AL,90h			; Ready pattern
	MOV	AL,0FFh			; Ready response code
	JZ	PRSRET			; Skip if readys
	MOV	AL,0			; not ready
PRSRET:	RET

;----------------------------------------
;
;	Serial port output
;
SPOUT1:
	INC	DX
SPOUT0:
;----------------------------------------
;	Serial port output
;	Entry:	CL = char
;		DX = serial port number
;
SERIALOUT:
	PUSH	CX
SERIALOUT1:
	MOV	CL,20h
	CALL	SOSTAT
	TEST	AL,AL
	JZ	SERIALOUT1
	POP	CX
SERIALOUT2:
	MOV	AH,01h			; Write char to port
	MOV	AL,CL			; char to write
	INT	14h			; PC ROM BIOS - Serial Port Service
	TEST	AH,90h
	JNZ	SERIALOUT2
	RET

;----------------------------------------
;
;	Serial in for ports #1 and #0
;
SPIN1:
	INC	DX
SPIN0:
;
;----------------------------------------
;	Serial port input
;	Entry:	DX = serial port number
;	Exit:	AL = received char
;
SERIALIN:
	MOV	AH,02h			; Read char from port
	INT	14h			; PC ROM BIOS - Serial Port Service
	ROL	AH,1h			; Timed out ?
	JC	SERIALIN		; Yes, try again
	ROR	AH,1h			; Reset status bits
	RET

;----------------------------------------
;
;	Serial output status
SOSTAT1:
	INC	DX
SOSTAT0:
;----------------------------------------
;	Serial output port status check
;	Entry:	DX = serial port number
;		AH = status check mask
;	Exit:	AL = 0ffh if ready
;		AL = 0 if not
;
SOSTAT:
	CMP	CL,20h
	JNZ	SOSTATC
;
	MOV	CL,Byte Ptr SERBUFSTS
	MOV	AH,03h			; Get port status
	INT	14h			; PC ROM BIOS - Serial Port Service
;
	TEST	AL,20h			; Data-set ready ?
	JZ	SOSTATD			; Yes
;
	TEST	AH,01h			; data ready ?
	JZ	SOSTATC			; Yes
;
	CALL	SERIALIN		; Read char from serial port
	CMP	AL,13h
	JNZ	SOSTATA
;
	MOV	CL,0
SOSTATA:
	CMP	AL,11h
	JNZ	SOSTATB
;
	MOV	CL,20h
SOSTATB:
	MOV	Byte Ptr SERBUFSTS,CL
SOSTATC:
	MOV	AH,03h			; Get Port Status
	INT	14h			; PC ROM BIOS - Serial Port Service
	MOV	AL,TRUE
	TEST	CL,AH
	JNZ	SORET
SOSTATD:
	MOV	AL,FALSE
SORET:	RET

;----------------------------------------
;	Light pen input
;
PENIN:
	MOV	SI,Word Ptr PENPTR	; Buffer pointer
	CMP	SI,PENBUFEND		; Reached end?
	JNZ	PENIN1			; No, get byte
;
	CALL	PENRDB			; Read/buffer status info
	JZ	PENIN2			; Not ready
;
	MOV	SI,Word Ptr PENPTR
PENIN1:	LODSB				; Get next byte
	MOV	Word Ptr PENPTR,SI	; Save pointer pointer
	ADD	AL,20h
	RET

PENIN2:	MOV	Word Ptr BOSMSGPTR,Offset PENERR; 'Waiting for light pen'
	CALL	CLRBOS1			; Clear status line
	CALL	PRNBOS			; Display status line
	CALL	VRSTCPT			; Restore cursor posn and type
	MOV	SI,Offset SNDTBL2
	CALL	BEEP1
;
PENIN3:	CALL	PENRDB			; Read/buffer status info
	JNZ	PENIN4			; Ok
;
	MOV	AH,1			; Read keyboard status
	INT	16h			; PC ROM BIOS Keyboard Service
	JZ	PENIN3
	MOV	AH,0			; Read char from keyboard
	INT	16h			; PC ROM BIOS Keyboard Service
	CMP	AL,CTRLC		; CNTRL-C pressed ?
	JNZ	PENIN3			; No, try again
;
	MOV	Word Ptr SYS_CONI,1	; Reset to keyboard input
	CALL	CLRBOS			; Clear status line
	JMP	ABORT			; We're gone
;
PENIN4:	CALL	CLRBOS			; Clear status line
	JMP	PENIN			; Return input
;
;	Read/Buffer light pen status info
;
PENRDB:	CALL	PENST			; Get status info
	JZ	PENRDBX			; Not ready
;
	MOV	SI,Offset PENBUF
	MOV	Word Ptr PENPTR,SI	; Reset buffer pointer
	MOV	[SI],DH			; buff+0 = char row
	INC	SI
	MOV	[DI],DL			; buff+1 = char col	(BUG DI ???)
	INC	SI
	MOV	[DI],CH			; buff+2 = pixel row	(BUG DI ???)
	INC	SI
	SHL	BX,1h
	SHL	BX,1h
	SHR	BL,1h
	SHR	BL,1h
	MOV	[SI],BH			; buff+3 = 
	INC	SI
	MOV	[SI],BL			; buff+4 =
PENRDBX:
	RET
;----------------------------------------
;	Light pen status (and position information)
;
;	Exit:	AL = 0ffh if ready
;		AL = 0 if not
;
PENST:	MOV	AH,4			; Read light pen position
	INT	10h			; PC ROM BIOS Video Driver Service
;
;	AH = 0 = pen not down/triggered
;	AH = 1 = pen down/triggered
;		 DH,DL = row,column of character light pen is on
;		 CH = pixel row (graphics modes 04h-06h)
;		 CX = pixel row (graphics modes with >200 rows)
;		 BX = pixel column
;
	RCR	AH,1			; Result: AH=0 -> AL=0	  + ZF
	SBB	AL,AL			; 	  AH=1 -> AL=0FFH + NZF
	RET

;----------------------------------------
; Select disk for next rd/write
;
SELDSK:					; select disk given by register CL
;
	MOV	Byte Ptr DISK,CL
	SUB	AX,AX			; 0 = Select disk
	JMPS	XDSKDRV			; Common disk routine

;----------------------------------------
;*********************************************
;*                                           *
;*  All disk I/O parameters are setup:       *
;*     DISK     is disk number      (SELDSK) *
;*     TRK      is track number     (SETTRK) *
;*     SECT     is sector number    (SETSEC) *
;*     DMA_ADR  is the DMA offset   (SETDMA) *
;*     DMA_SEG  is the DMA segment  (SETDMAB)*
;*  READ reads the selected sector to the DMA*
;*  address, and WRITE writes the data from  *
;*  the DMA address to the selected sector   *
;*  (return 00 if successful, 01 if perm err)*
;*                                           *
;*********************************************
;
; Read a 128 byte sector
;
READ:
;
	MOV	AX,2			; 2 = Read
	JMPS	XDSKDRV			; Common disk routine

;----------------------------------------
; Write a 128 byte sector
;
WRITE:
;
	MOV	AX,4			; 4 = Write
	JMPS	XDSKDRV			; Common disk routine

;----------------------------------------
; Move to trk 00 on cur sel drive
;
HOME:
;
	MOV	Word Ptr TRK,0
	MOV	AX,6			; 6 = Home current disk
;	JMPS	XDSKDRV			; Common disk routine (fall through)
;
;	Common disk routine
;
;	Disk routine dependant on disk drive
;
XDSKDRV:
	CLD
	MOV	BL,Byte Ptr DISK
	MOV	BH,0			; Select routine
	SHL	BX,1h			;  according
	JMP	Word Ptr DSKDRVRTN[BX]	;   to required disk
;
;	Select appropriate disk routine
;
;	Entry	AX = Function
;		BX = Disk number * 2
;
DSKRTNSEL:
	XCHG	AX,BX
	JMP	Word Ptr DSKFUNRTN[BX]
;
;	Select appropriate memory disk routine
;
;	Entry	AX = Function
;		BX = Disk number * 2
;
MEMRTNSEL:
	XCHG	AX,BX
	JMP	Word Ptr MEMFUNRTN[BX]
;----------------------------------------
;
; Routine for invalid disk drives
;
;	Entry	AX = function	0 = SELDSK
;				1 = READ
;				2 = WRITE
;				3 = HOME
;		CL = deblock flag
;
DSKRTNINV:
	MOV	Byte Ptr DBK_FLG,CL	; deblock flag
	MOV	DX,DS
	MOV	BX,Offset DRV_NUM	; unknown drive number
	PUSH	AX
	MOV	AL,Byte Ptr DISK
	SUB	AL,Byte Ptr X3A6C
	MOV	Byte Ptr [BX],AL
	POP	AX
	INT	0E6h			; unknown disk drive
	MOV	BX,AX
	RET
;----------------------------------------
;
; Unknown disk drive interrupt
;
UNDSKINT:
	SUB	AX,AX			; not supported at present
	IRET   
;----------------------------------------
;
; Select physical disk
;
;	Entry	AX = disk number * 2
;
SELECT_PHYDSK:
	MOV	SI,AX
	SHR	SI,1			; Divide by 2
	XCHG	AX,BX
	MOV	BX,Word Ptr DPHTBL[BX]
	TEST	Byte Ptr X3AB2[SI],80h	; Hard disk drive ?
	JNZ	SELECT_PHYDSKX		; Yes, skip disk reset
;
	TEST	DL,1			; 1st select ?
	JNZ	SELECT_PHYDSKX		; No, skip disk reset
;
	CALL	RESET_FLOPPY
;
SELECT_PHYDSKX:
	RET
;
; Select memory disk
;
SELECT_MEMDSK:
	MOV	BX,Offset DPH4
	RET

;----------------------------------------
;
; Home current disk [physical or memory disk]
;
HOME_PHYDSK:
HOME_MEMDSK:
	RET				; Not supported

;----------------------------------------
;	Set track for next rd/write
;
SETTRK:
;
	MOV	Word Ptr TRK,CX
	RET
;
;	Set sector for next rd/write
;
SETSEC:
;
	MOV	Word Ptr SECT,CX
	RET
;
;	Xlate logical->physical sector
;
SECTRAN:
;
	MOV	BX,CX		; we assume logical = physical sector
	RET
;
;	Set offset for user buff (DMA)
;
SETDMA:
;
	MOV	Word Ptr DMA_ADR,CX
	RET
;
;	Set seg base for buff (DMA)
;
SETDMAB:
;
	MOV	Word Ptr DMA_SEG,CX
	RET
;
;	Return offset of Mem Desc Table
;
GETSEGT:
;
	MOV	BX,Offset MRT
	RET
;
;	Return I/O map byte (IOBYTE)
;
;	   Input: none
;	   Output: AL:=IOBYTE
;
GETIOB:
;
	SUB	AL,AL			; Not implemented
	RET
;
;	Set I/O map byte (IOBYTE)
;
;	   Input: CL:=IOBYTE
;	   Output: none
;
SETIOB:
;
	RET				; Not implemented
;----------------------------------------
;
; Read physical disk track
;
READ_PHYTRK:
	CALL	X2C13
	JB	RPT_RET			; Failed
	CALL	X2BE5
	JMPS	DBA_2_DMA		; Disk buffer -> DMA
RPT_RET:
	RET
;
; Read memory disk track
;
READ_MEMTRK:
	CALL	X2BF7
;
; Copy from disk buffer address to dma buffer address
;
DBA_2_DMA:
	PUSH	DS
	PUSH	ES
	LES	DI,DWord Ptr DMAADDR	; Destination	[ES:DI]
	LDS	SI,DWord Ptr DSKBUFADDR	; Source	[DS:SI]
	REP	MOVSW  
	POP	ES
	POP	DS
	MOV	AL,0			; OK return
	RET
;
; Write physical disk track
;
WRITE_PHYTRK:
	MOV	Byte Ptr X3A71,CL
	CALL	X2C13
	JB	WPT_RET
	CALL	X2BE5
	CALL	DMA_2_DBA		; DMA -> disk buffer
	MOV	Byte Ptr X3A70,0FFh
	DEC	Byte Ptr X3A71
	JNZ	WPT_RET
	CALL	X2CA3			; Write disk sector(s) ?
	MOV	Byte Ptr X3A70,0
WPT_RET:
	RET
;
;
; Write memory disk track
;
WRITE_MEMTRK:
	CALL	X2BF7
;
; Copy from dma buffer address to disk buffer address 
;
DMA_2_DBA:
	PUSH	DS
	PUSH	ES
	LES	DI,DWord Ptr DSKBUFADDR	; Destination	[ES:DI]
	LDS	SI,DWord Ptr DMAADDR	; Source 	[DS:SI]
	REP	MOVSW  
	POP	ES
	POP	DS
	MOV	AL,0
	RET
;
X2BE5:	MOV	AX,Word Ptr X3AA8
	CALL	X2C08
	MOV	AX,Offset DSKBUF
	ADD	Word Ptr DSKBUF_ADR,AX
	MOV	Word Ptr DSKBUF_SEG,DS
	RET
;
X2BF7:	MOV	AX,Word Ptr TRK
	MOV	CL,8
	SHL	AX,CL
	ADD	AX,Word Ptr MDSKSEG
	MOV	Word Ptr DSKBUF_SEG,AX
	MOV	AX,Word Ptr SECT
X2C08:	MOV	CL,7
	SHL	AX,CL
	MOV	Word Ptr DSKBUF_ADR,AX
	MOV	CX,64			; Length of memory disk track
	RET

X2C13:	CALL	X2C4D
	MOV	AX,Word Ptr X3AA6
	JZ	X2C2A
	CMP	AX,Word Ptr X3A6E
	JNZ	X2C2A
	MOV	AL,Byte Ptr DISK
	CMP	AL,Byte Ptr X3A6D	; Disk error or change ???
	JZ	X2C4C
X2C2A:	TEST	Byte Ptr X3A70,0FFh
	JZ	X2C36
	CALL	X2CA3			; Write disk sector(s) ?
	JB	X2C4C
X2C36:	CALL	X2CE3			; Read disk sector(s) ?
	JB	X2C4C
	MOV	AX,Word Ptr X3AA6
	MOV	Word Ptr X3A6E,AX
	MOV	AL,Byte Ptr DISK
	MOV	Byte Ptr X3A6D,AL	; Disk error or change ???
	MOV	Byte Ptr X3A70,0
X2C4C:	RET
;
X2C4D:	PUSH	BX
	MOV	AX,Word Ptr SECT
	MOV	Word Ptr X3AA8,AX
	MOV	AL,Byte Ptr DISK
	CBW
	XCHG	AX,SI
	MOV	AX,Word Ptr TRK
	TEST	Byte Ptr X3AB2[SI],80h	; Hard disk drive ?
	JNZ	X2C69			; Yes
	MOV	Word Ptr X3AA6,AX
	DEC	AX
	JMPS	X2C9D
X2C69:	SHL	SI,1h
	MOV	BX,Word Ptr X3AB6[SI]
	SUB	AX,15h[BX]
	MUL	Word Ptr 8[BX]
	ADD	AX,Word Ptr SECT
	ADC	DX,0000h
	DIV	Word Ptr 1Fh[BX]
	MOV	Word Ptr X3AA8,DX
	LEA	DI,27h[BX]
	MOV	CX,23h[BX]
	JCXZ	X2C9A
	PUSH	CS
	POP	ES
	REPNE	SCASW  
	JNZ	X2C9A
	MOV	AX,25h[BX]
	SUB	AX,23h[BX]
	ADD	AX,CX
	INC	AX
X2C9A:	MOV	Word Ptr X3AA6,AX
X2C9D:	OR	AX,Word Ptr X3AA8
	POP	BX
	RET

;----------------------------------------
;
; Routine to write disk sectors ?
;
X2CA3:	PUSH	BX
	PUSH	DX

X2CA5:	MOV	CX,4			; Number of retrys
X2CA8:	PUSH	CX
	MOV	AX,Word Ptr X3A6E
	MOV	DL,Byte Ptr X3A6D	; Disk error or change ???
	CALL	X2D44
	MOV	Byte Ptr NOS2WRT,AL	; Number of sectors
	MOV	AH,03h			; Write disk sector(s)
	INT	13h			; PC ROM BIOS Floppy Disk Services
	JC	X2CCA			; Failed ?
;
	TEST	Byte Ptr VFLAG,TRUE	; Verify enabled ?
	JZ	X2CCA			; No, skip
;
	MOV	AL,Byte Ptr NOS2WRT	; Number of sectors
	MOV	AH,04h			; Verify disk sector(s)
	INT	13h			; PC ROM BIOS Floppy Disk Services
;
X2CCA:	POP	CX			; Retry count
	JNC	X2CDE			; Disk operation ok - exit ok
;
	PUSH	AX
	MOV	AH,0			; Reset disk system
	INT	13h			; PC ROM BIOS Floppy Disk Services
	POP	AX
	LOOP	X2CA8			; Loop - Retry
	CALL	DISKERROR		; Report it
	JZ	X2CA5			; Try again
	MOV	AL,01h			; Error - Exit
	JC	X2CE0
X2CDE:	MOV	AL,0			; Ok - Exit
X2CE0:	POP	DX
	POP	BX
	RET

;----------------------------------------
; Routine to read disk sectors ?
;
X2CE3:	PUSH	BX
	PUSH	DX
X2CE5:	MOV	CX,5			; retry count
X2CE8:	PUSH	CX
	MOV	AX,Word Ptr X3AA6
	MOV	DL,Byte Ptr DISK	; Drive number
	CALL	X2D44
	MOV	AH,02h			; Read sector(s) into memory
	INT	13h			; PC ROM BIOS Floppy Disk Services
	POP	CX
	JNC	X2D13			; Ok - exit ok
;
	PUSH	AX
	MOV	AH,0			; Reset disk system
	INT	13h			; PC ROM BIOS Floppy Disk Services
	POP	AX
;
	CMP	AH,80h			; Timeout (not ready) ?
	LOOPNE	X2CE8			; No, loop - retry
;
	CALL	DISKERROR		; Inform other error
	JZ	X2CE5			; Operator 'R'etry
;
	MOV	Byte Ptr X3A6D,0FFh	; Disk error or change ???
	MOV	AL,01h			; Error - exit
	JB	X2D15
X2D13:	MOV	AL,0			; Ok - exit
X2D15:	POP	DX
	POP	BX
	RET

;----------------------------------------

RESET_FLOPPY:
	PUSH	Word Ptr TRK		; Save current track
	MOV	Word Ptr TRK,0		; Home
	CALL	X2C13
	JNB	RESET_FLOPPY1
	JMP	ABORT			; We're gone
;
RESET_FLOPPY1:
	MOV	Word Ptr 0Ah[BX],Offset DPBK1
	PUSH	BX
	MOV	BX,Offset DSKBUF
	DEC	Byte Ptr 01FFh[BX]
	POP	BX
	JNZ	RESET_FLOPPY2
	MOV	Word Ptr 0Ah[BX],Offset DPBK2
;
RESET_FLOPPY2:
	POP	Word Ptr TRK		; Restore current track
	RET

;----------------------------------------

X2D44:	MOV	DH,0
	MOV	SI,DX
	TEST	Byte Ptr X3AB2[SI],80h	; Hard disk drive ?
	JNZ	X2D62			; Yes
	MOV	CH,AL
	CMP	CH,28h
	JB	X2D5C
	MOV	CH,4Fh
	SUB	CH,AL
	MOV	DH,01h
X2D5C:	MOV	CL,01h
	MOV	AL,08h
	JMPS	X2D8F
X2D62:	SHL	SI,1h
	MOV	BX,Word Ptr X3AB6[SI]
	SHR	SI,1h
	MOV	CX,AX
	MOV	AX,Word Ptr 1Fh[BX]
	DIV	Byte Ptr 7h[BX]
	CBW
	MUL	CX
	MOV	CL,05h[BX]
	MOV	CH,0
	DIV	CX
	INC	DX
	PUSH	DX
	ADD	AX,15h[BX]
	CWD
	MOV	CL,04h[BX]
	DIV	CX
	POP	CX
	CALL	X2D99
	MOV	DH,DL
	MOV	AL,04h
X2D8F:	MOV	DL,Byte Ptr X3AB2[SI]
	PUSH	CS
	POP	ES
	MOV	BX,Offset DSKBUF
	RET

;----------------------------------------
;
X2D99:	MOV	CH,AL
	SHR	AX,1
	SHR	AX,1
	AND	AL,0C0h
	OR	CL,AL
	RET

;----------------------------------------
;
;	Musical beeps
;	Entry	SI = sound table offset
;		(overridden if sound flag set true)
;
BEEP:	TEST	Byte Ptr SNDFLG,TRUE	; Use default sound ?
	JNZ	BEEP1
	MOV	SI,Offset SNDTBL1	; Yes
;
BEEP1:
	LODSW
	TEST	AX,AX			; End of table
	JZ	BEEPX			; Yes, exit
;
	XCHG	AX,BX
	MOV	AL,0B6h
	OUT	43h,AL
	LODSB
	OUT	42h,AL
	LODSB
	OUT	42h,AL
	IN	AL,61h
	MOV	AH,AL
	OR	AL,03h
	OUT	61h,AL
BEEP2:	MOV	CX,2000h
BEEP3:	LOOP	BEEP3
	DEC	BX
	JNZ	BEEP2
	MOV	AL,AH
	OUT	61h,AL
	MOV	CX,1000h
BEEP4:	LOOP	BEEP4
	JMPS	BEEP1
BEEPX:	RET
;----------------------------------------
;	DISK ERROR HANDLING
;	ENTRY:	AH = ERROR CODE
;	EXIT:	ZF = 1 FOR RETRY
;		ZF = 0, CF = 0 FOR IGNORE
;		ZF = 0, CF = 1 FOR ACCEPT
;
DISKERROR:
	PUSH	DI
	PUSH	ES
	PUSH	AX
	MOV	AL,Byte Ptr DISK
	ADD	AL,'A'			; Make drive name
	MOV	Byte Ptr EDRIVE,AL	; In error message

	MOV	AX,Word Ptr TRK
	MOV	DI,Offset ETRACK	; Make track number
	PUSH	DS
	POP	ES
	CALL	X2E8D			; In error message
	XCHG	AX,DI
	SUB	AX,Offset ERRDISK
	MOV	Byte Ptr ERRDISKLEN,AL
	POP	AX
	MOV	SI,Offset EDSKTBL
	MOV	CX,EDSK_CNT		; Number of codes
DSKERR1:
	LODSB				; Get the code
	CMP	AL,AH			; Is this it ?
	JZ	DSKERR2			; Skip when found

	INC	SI			; Past message offset
	INC	SI
	LOOP	DSKERR1			; Unknown fall through
DSKERR2:
	MOV	DX,[SI]			; DX -> sub message
	MOV	SI,Offset ERRDISKLEN
	JMPS	DSKERR4
;
;	ERROR HANDLING ROUTINE
;	PRINTS MESSAGE, SUB MESSAGE, AND QUESTION
;	ON THE BOTTOM LINE, AND GETS USER RESPONSE.
;	ENTRY:	SI -> ERROR MESSAGE
;		DX -> SUB MESSAGE
;	EXIT:	ZF = 1 FOR RETRY
;		ZF = 0 FOR IGNORE OR ACCEPT
;
DSKERR3:
	PUSH	DI			; Save
	PUSH	ES

DSKERR4:
	PUSH	Word Ptr CURCUR		; Current (in use) cursor type
	PUSH	Word Ptr CURSOR
	CLD
	PUSH	DS
	POP	ES
	MOV	DI,Offset DSKMSG+1
	MOV	CX,40
	MOV	AX,'  '
	REP	STOSW			; Blank out error message buffer
	MOV	DI,Offset DSKMSG+1
	CALL	X2E87
	MOV	SI,DX
	CALL	X2E82
	MOV	SI,Offset ERRQUEST	; 'Retry, ignore, accept?'
	CALL	X2E82
	XCHG	AX,DI
	MOV	DI,Offset DSKMSG
	SUB	AX,DI
	DEC	AX
	MOV	[DI],AL
	MOV	Word Ptr BOSMSGPTR,DI	; Status line text pointer
	CALL	PRN_STATUS
	MOV	SI,Offset SNDTBL3

DSKERR5:
	CALL	BEEP
;
	MOV	AH,0			; Read char from keyboard
	INT	16h			; PC ROM BIOS Keyboard Service
	CMP	AL,3			; Control-C ?
	JZ	DSKERR_B		; Back to BDOS
	AND	AL,0DFh			; Convert upper case
	CMP	AL,'R'			; Retry ?
	JZ	DSKERR_R
	CMP	AL,'I'			; Ignore ?
	JZ	DSKERR_I
	CMP	AL,'A'			; Accept
	MOV	SI,Offset SNDTBL1
	JNZ	DSKERR5			; Invalid response
	STC				; For accept
DSKERR_I:
	INC	AX			; ZF=0, ignore, accept
DSKERR_R:
	POP	Word Ptr CURSOR
	POP	Word Ptr CURCUR		; Current (in use) cursor type
	PUSHF				; Save response flags
	CALL	CLRBOS			; Clear status line
	CALL	VRSTCPT			; Restore cursor posn and type
	POPF
	POP	ES
	POP	DI			; Restore pointer
	RET
;
;	Abort back to BDOS
;
DSKERR_B:
	POP	Word Ptr CURSOR
	JMP	ABORT			; We're gone

;----------------------------------------

X2E82:	MOV	AX,'  '
	STOSW
	STOSB				; Store 3 space chars

X2E87:	LODSB
	CBW
	XCHG	AX,CX
	REP	MOVSB  
	RET

;----------------------------------------

X2E8D:	SUB	DX,DX
	DIV	Word Ptr TEN
	PUSH	DX
	TEST	AX,AX
	JZ	X2E9B
	CALL	X2E8D
X2E9B:	POP	AX
	OR	AL,30h
	STOSB
	RET
;----------------------------------------
;
;	***  Interrupt handling routines  ***
;
;	User break interrupt routine
;	Entered when user hits CONTROL-BREAK at keyboard
;
BREAKINT:
	TEST	CS:Byte Ptr BRKFLAG,TRUE; Here already?
	JZ	BRKINT1			; Skip if not
	IRET				; Do not reenter

BRKINT1:
	PUSH	AX
	PUSH	DS
	MOV	AX,CS			; Get current segment
	MOV	DS,AX
	MOV	Word Ptr SPBREAK,SP	; Save stack pointer
	MOV	Word Ptr SSBREAK,SS	; and segment
	MOV	SP,Offset BRKSTK	; Our own stack
	MOV	SS,AX			; In this segment
;
	PUSH	BX			; now push the works
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	ES
	PUSH	Word Ptr BOSMSGPTR	; Status line text pointer
	PUSH	Word Ptr CURCUR		; Current (in use) cursor type
	PUSH	Word Ptr CURSOR
;
	CLI
	MOV	AL,20h			; EOI command
	OUT	20h,AL			; To int control port
	MOV	Byte Ptr BRKFLAG,TRUE	; Where here now
	STI				; Ok to interrupt
;
	MOV	Word Ptr BOSMSGPTR,Offset BREAKMES	; 'User program break'
	CALL	PRN_STATUS
	MOV	SI,Offset SNDTBL4
BRKINT2:
	CALL	BEEP
;
	MOV	AH,0		; Read char from keyboard
	INT	16h		; PC ROM BIOS Keyboard Service
;
	CMP	AL,CTRLC	; CNTRL-C ?
	JZ	BRKINT3
	AND	AL,0DFh		; Force upper case
	CMP	AL,'Y'		; Back to CP/M ?
	JZ	BRKINT3
	CMP	AL,'N'		; Or back to user ?
	MOV	SI,Offset SNDTBL1
	JNZ	BRKINT2		; Try again
;
	POP	Word Ptr CURSOR
	POP	Word Ptr CURCUR		; Current (in use) cursor type
	POP	Word Ptr BOSMSGPTR	; Status line text pointer
	CALL	CLRBOS1			; Clear status line
	CALL	VRSTCPT			; Restore cursor posn and type
	POP	ES			; Restore registers
	POP	DI
	POP	SI
	POP	DX
	POP	CX
	POP	BX
	CLI				; Hold the int's
	MOV	Byte Ptr BRKFLAG,0	; You may reenter
	MOV	SP,Word Ptr SPBREAK
	MOV	SS,Word Ptr SSBREAK
	POP	DS
	POP	AX
	IRET				; Back to caller

BRKINT3:
	MOV	Word Ptr SYS_CONI,1
	MOV	Word Ptr SYS_CONO,2
	POP	Word Ptr CURSOR
	MOV	Byte Ptr BRKFLAG,0	; You may come back
	JMP	ABORT			; We're gone
;----------------------------------------
;
TIMINTA:
	TEST	CS:Byte Ptr BDOSMODABT,TRUE	; BDOS abort mode set ?
	JZ	TIMINTB				; No
	INT	0E2h
TIMINTB:
	IRET
;
;	Timer interrupt routine
;	Vector set up in signon
;	Consumes about 6 sec per hour
;
TIMERINT:
	DEC	CS:Word Ptr X36F3
	JNS	TIMINTA
	MOV	AL,TRUE
	XCHG	AL,CS:Byte Ptr VIDBSY	; Set video busy
	TEST	AL,AL			; Already busy ?
	JNZ	TIMINTA			; Yes
;
;	Come here once every second
;
	MOV	AX,CS			; Get this segment
	MOV	DS,AX			; for stack save
	MOV	Word Ptr SPSAVE,SP	; Save current stack
	MOV	Word Ptr SSSAVE,SS	;   and segment
	MOV	SS,AX
	MOV	SP,Offset LCLSTK	; Set up local stack
	STI
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	ES			; And promptly use it
	MOV	ES,AX
	CLD				; For later

	MOV	AX,0012h
;	ADD	Word Ptr X36F5,34DC
	ADD	Word Ptr X36F5,34DCh
	ADC	AX,0000h
	ADD	Word Ptr X36F3,AX
;
;	Increment ram clocks
;
	MOV	BX,Offset DSECOND
	MOV	AH,60h			; For overflow
	CALL	ADDECIMAL
	JC	TIMINT2			; Skip if no overflow
	CALL	ADDECIMAL
	JC	TIMINT2
	MOV	AH,24h			; For hours
	CALL	ADDECIMAL
TIMINT2:
	MOV	CX,'00'			; Overflow reset value
	MOV	BX,Offset ASECOND
	MOV	DX,'60'			; For overflow check
	CALL	INCASCII
;
	CALL	INCASCII		; Increment minutes
;
	MOV	DX,'24'			; 24 hours
	CALL	INCASCII
;
	CALL	LEAP			; Next day, check year
	MOV	CX,'01'			; Overflow reset value
	MOV	AX,Word Ptr AMONTH
	CALL	ASCIINUM		; Convert to number
	DEC	AX
	ADD	AX,AX
	XCHG	AX,BX
	MOV	DX,MONTHLENGTH[BX]
	MOV	BX,Offset ADAY
	CALL	INCASCII		; Next day
;
	MOV	DX,'13'			; Month too high
	CALL	INCASCII		; Next month
;
	MOV	CX,'00'			; Overflow reset value
	MOV	DX,':0'			; Ascii 100
	MOV	BX,Offset AYEAR
	CALL	INCASCII
;
	MOV	BX,Offset BCENT
	CALL	INCASCII		; Into the next century
;
;	Now set up bottom line display
;
TIMINT3:
	MOV	AX,Word Ptr ASECOND	; Simple ones first
	MOV	Word Ptr BSECOND,AX
	MOV	AX,Word Ptr AMINUTE
	MOV	Word Ptr BMINUTE,AX
	MOV	AX,Word Ptr AYEAR
	MOV	Word Ptr BYEAR,AX
;
	MOV	AX,Word Ptr AMONTH
	CMP	AX,Word Ptr CMONTH	; If new month
	JNZ	TIMINT4			;   then fix it
;
	MOV	AX,Word Ptr ADAY
	CMP	AX,Word Ptr CDAY	; If day and month same
	JZ	TIMINT5			;   then skip it
;
TIMINT4:
	MOV	AX,Word Ptr ADAY
	MOV	Word Ptr CDAY,AX	; Update check copy
	CALL	LZBLANK			; Blank leading zero
	MOV	Word Ptr BDAY,AX	; Ascii day
;
	MOV	DI,Offset BMONTH
	ADC	DI,0000
	MOV	AX,Word Ptr AMONTH
	MOV	Word Ptr CMONTH,AX
;
	RCR	AL,1			; Check msb
	SBB	AL,AL			; All ones if carry
	AND	AX,0F0Ah		; Mask AH, AL = 0 or 10
	ADD	AL,AH			; AL = month number
;
	MOV	DL,'.'			; Assume abbreviated month
	CMP	AL,5			; May ?
	JNZ	TIMINT4A
	MOV	DL,' '			; Yes, remove dot
;
TIMINT4A:
	MOV	AH,AL
	ADD	AL,AL
	ADD	AL,AH			; AL = 3 * month
	MOV	AH,0
	MOV	SI,Offset MONTHNAMES -3
	ADD	SI,AX
	MOV	AL,' '
	STOSB
	MOV	CX,3
	REP	MOVSB			; Month abbrev
	MOV	[DI],DL
	INC	DI
	STOSB

TIMINT5:
	MOV	AX,Word Ptr AHOUR
	CMP	AX,Word Ptr CHOUR	; If same skip
	JZ	TIMINT8
;
	MOV	Word Ptr CHOUR,AX	; Update check copy
	MOV	DX,'00'
	MOV	CX,'12'
	XCHG	AH,AL			; Switch AH = msb
	CMP	AX,CX			; Check am/pm
	JB	TIMINT6			; Skip if am
;
	MOV	Word Ptr BAMPM,'mp'	; 'pm'
	JZ	TIMINT7			; If 12, skip minus
	SUB	AX,CX			; American time
	AAS
	OR	AX,DX			; Back to ascii
	JMPS	TIMINT7			; Done
;
TIMINT6:
	MOV	Word Ptr BAMPM,'ma'	; 'am'
	CMP	AX,DX
	JNZ	TIMINT7			; Skip if not zero
	MOV	AX,CX			; If zero, make 12
;
TIMINT7:
	XCHG	AL,AH			; Back to backwards
	CALL	LZBLANK			; Blank leading zero
	MOV	Word Ptr BHOUR,AX
;
; Get user number
;
TIMINT8:
	MOV	AL,Byte Ptr BDOSUSER
	CMP	AL,Byte Ptr CUSER
	JZ	TIMINT10		; Skip if same

	MOV	Byte Ptr CUSER,AL	; Update check copy
	SUB	AH,AH
	AAA
	OR	AX,'00'			; Back to ascii
	XCHG	AL,AH			; Back to backwards
	CALL	LZBLANK			; Blank leading zero
	JNB	TIMINT9			; Skip if no blank
	XCHG	AL,AH			; Else switch back
TIMINT9:
	MOV	Word Ptr BUSER,AX	; Status line
;
; Copy system block message up
;
TIMINT10:
	MOV	SI,Offset SYSMESSAGE
	MOV	DI,Offset BSYSMES
	MOV	CX,14			; Message length
	REP	MOVSB  
;
; Finally update the bottom line
;
	CMP	Word Ptr BOSMSGPTR,Offset TIMEMSG	; Using standard text ?
	JNZ	TIMINT11				; No, skip
	TEST	Byte Ptr BOSFLAG,TRUE	; Is the line tied up ?
	JZ	TIMINT11		; Yes, skip
;
	MOV	CX,Word Ptr CURLOFF	; Cursor off
	MOV	AH,1			; Set cursor type
	INT	10h			; PC ROM BIOS Video Driver Service
;
	CALL	PRNBOS			; Display status line
;
	MOV	DX,Word Ptr CURSOR
	MOV	BH,0
	MOV	AH,02h			; Set cursor position
	INT	10h			; PC ROM BIOS Video Driver Service
;
	MOV	CX,Word Ptr CURCUR	; Current (in use) cursor type
	MOV	AH,01h			; Set cursor type
	INT	10h			; PC ROM BIOS Video Driver Service

TIMINT11:
	POP	ES
	POP	DI
	POP	SI
	POP	DX
	POP	CX
	POP	BX
	CLI				; For stack switch
	MOV	Byte Ptr VIDBSY,FALSE
	MOV	SP,Word Ptr SPSAVE
	MOV	SS,Word Ptr SSSAVE
	IRET
;----------------------------------------
;
;	Add one to a decimal number
;	Overflow and set carry if no overflow
;
ADDECIMAL:
	MOV	AL,[BX]
	INC	AL
	DAA				; +1 decimal
	CMP	AL,AH
	JC	ADDEC1			; Skip no overflow
	SUB	AL,AL			; Else reset, clc
ADDEC1:
	MOV	[BX],AL			; Restore count
	DEC	BX			; For next time
	RET
;----------------------------------------
;
;	Add one to an ascii number
;	Entry:	BX -> ascii value
;		CX = overflow reset value
;		DX = one too many
;	If no overflow, then routine skips its return
;	    and jumps straight to TIMINT3
;
INCASCII:
	MOV	AX,[BX]
	XCHG	AL,AH			; So that AH = msh
	INC	AL
	AAA				; Inc AH on carry
	OR	AX,'00'			; Back to ascii
	CMP	AX,DX			; Check overflow
	JC	INCA1			; Skip if none
;
	XCHG	CL,CH
	MOV	[BX],CX			; Else reset value
	XCHG	CL,CH			; Restore CX
	SUB	BX,3			; for next time
	RET
INCA1:
	XCHG	AL,AH			; Back to backwards
	MOV	[BX],AX			; Restore count
	POP	AX			; Clear the return
	JMP	TIMINT3			; Straight to update
;----------------------------------------
;
;	Adjust month length table for leap year
;
LEAP:
	MOV	AX,Word Ptr AYEAR	; Fetch ascii year
	CALL	ASCIINUM		; Convert to number
	TEST	AL,AL			; Check for 0
	JZ	LEAP1
;
	TEST	AL,03h			; Divisable by 4 ?
	JNZ	LEAP1			; If not, skip
;
	MOV	Word Ptr FEBLENGTH,'30'	; Feb leap year +1
	RET
LEAP1:
	MOV	Word Ptr FEBLENGTH,'29'	; Not leap year +1
	RET
;----------------------------------------
;
;	Convert an ASCII number to binary
;
ASCIINUM:
	AND	AX,0F0Fh		; Mask the top nibbles
	XCHG	AL,AH			; AL=lsb
	AAD				; Ascii adjust
	RET
;----------------------------------------
;
;	Blank ASCII leading zero in AL
;	Exit:	carry and zero set if blanked
;
LZBLANK:
	CMP	AL,'0'
	JNZ	LZB1
	MOV	AL,' '
	STC
LZB1:	RET
;----------------------------------------
;
;	Divide by zero error trap interrupt
;	Displays error and returns to BDOS
;
ZEROINT:
	STI
	POP	SI
	POP	ES
	PUSH	CS
	POP	DS
	MOV	DI,Offset ZEROMESADR	; seg:addr
	CALL	PUTHEXLONG		; Make ascii
	PUSH	Word Ptr CURSOR
	MOV	Word Ptr BOSMSGPTR,Offset ZEROMES	; 'Divide by zero'
	CALL	PRN_STATUS
	MOV	SI,Offset SNDTBL5
	CALL	BEEP
	JMPS	INTKEY			; Wait for keypress

;	Unexpected interrupt error trap interrupt
;	Displays error and returns to BDOS
;
UNEXPINT:
	STI
	POP	SI
	POP	ES
	PUSH	CS
	POP	DS
	DEC	SI
	MOV	AL,ES:[SI]
	INC	SI
	MOV	DI,Offset INTERMESNUM	; Interrupt no
	CALL	PUTHEXBYTE
	MOV	DI,Offset INTERMESADR	; Interrupt Seg:Addr
	CALL	PUTHEXLONG
	PUSH	Word Ptr CURSOR
	MOV	Word Ptr BOSMSGPTR,Offset INTERMES	; 'Unexpected Interrupt'
	CALL	PRN_STATUS
	MOV	SI,Offset SNDTBL6
	CALL	BEEP
;
INTKEY:	MOV	AH,0		; Read char from keyboard
	INT	16h		; PC ROM BIOS Keyboard Service
	POP	Word Ptr CURSOR
;
ABORT:	MOV	Byte Ptr WRAPFLAG,TRUE	; Default wrapflag
	MOV	Byte Ptr ATTRIB,07h	; Default attribute
	MOV	Byte Ptr VIDBSY,FALSE
	CALL	CLRBOS			; Clear status line
	CALL	RESETIO			; Reset disks and I/O ports
	MOV	CL,0			; Reset code
	MOV	DL,0			; Free mem
	INT	0E0h			; Back to bdos
;----------------------------------------
;
;	Put ascii segment:address into string
;	  Entry	ES - segment
;		SI - address
;		DS:DI - string address
;
;	Put hex long word (ES:SI) at DS:DI
;
PUTHEXLONG:
	MOV	AX,ES		; First ES
	CALL	PUTHEXWORD
	INC	DI		; Skip
	MOV	AX,SI		; Next SI
;
;	Put hex word AX at DS:DI
;
PUTHEXWORD:
	PUSH	AX
	MOV	AL,AH
	CALL	PUTHEXBYTE
	POP	AX
;
;	Put hex byte AL at DS:DI
;
PUTHEXBYTE:
	PUSH	AX
	MOV	CL,4
	SHR	AL,CL			; Shift upper to lower nybble
	CALL	PUTHEXDIGIT
	POP	AX
;
;	Put hex digit AL (lower nybble) at DS:DI
;
PUTHEXDIGIT:
	AND	AL,0Fh			; Mask off upper nybble
	MOV	BX,Offset HEXCHARTBL
	XLAT	AL			; Lookup ascii character
	MOV	Byte Ptr [DI],AL	; Put in string
	INC	DI			; Bump string pointer
	RET
;----------------------------------------
;
;	Display status text
;		If not on the bottom of the screen, on the current line
;
PRN_STATUS:
	TEST	Byte Ptr BOSFLAG,TRUE	; Status line available for use?
	JNZ	PRN_STATUS3		; Yes, print at bottom of screen
;
	CALL	PRN_STATUS1		; CR + LF
	MOV	SI,Word Ptr BOSMSGPTR	; Status line text pointer
	LODSB				; Get message length
	CBW
	XCHG	AX,CX			;  into CX
	CALL	PRN_STATUS2		; Display CX characters
;
PRN_STATUS1:
	MOV	SI,Offset CRLF		; CR + LF
	MOV	CX,2			; 2 characters
;
PRN_STATUS2:
	LODSB				; Next char
	PUSH	SI
	PUSH	CX
	MOV	CL,AL
	CALL	VIDNORM1		; Display char
	POP	CX
	POP	SI
	LOOP	PRN_STATUS2		; Loop
	RET
;
PRN_STATUS3:
	CALL	CLRBOS1			; Clear status line
	CALL	PRNBOS			; Display status line
	MOV	SI,Word Ptr BOSMSGPTR	; Status line text pointer
	MOV	DL,[SI]
	MOV	DH,18h
	CALL	SETCURSOR
	JMP	VCURON
;----------------------------------------
;
;	Print a message on the status line
;
PRNBOS:	PUSH	DS
	POP	ES
	MOV	DI,Offset BOSLINE
	MOV	SI,Word Ptr BOSMSGPTR	; Status line text pointer
	LODSB				; Get message length
	CBW
	XCHG	AX,CX			;  into CX
	MOV	DX,1800h		; Cursor position [Row:24,Col:00]
	MOV	BX,000Bh		; Display page 0, char attrib 0b
;
PRNBOS1:
	LODSB				; Get character
	CMP	AL,[DI]
	STOSB				; Store in copy status line
	JZ	PRNBOS2			; skip display if same
;
	PUSH	AX
	MOV	AH,2			; Set cursor position
	INT	10h			; PC ROM BIOS Video Driver Service
	POP	AX
;
	PUSH	CX
	MOV	CX,1			; only 1 char
	MOV	AH,9			; Write attribute and char at cursor
	INT	10h			; PC ROM BIOS Video Driver Service
	POP	CX
;
PRNBOS2:
	INC	DL			; Bump column position
	LOOP	PRNBOS1			; Next character
	RET

;----------------------------------------
;
;	Clear the message line
;
CLRBOS:	MOV	Word Ptr BOSMSGPTR,Offset TIMEMSG	; Status line

CLRBOS1:
	TEST	Byte Ptr BOSFLAG,TRUE	; Line tied up ?
	JZ	CLRBOSX			; Yes, skip
CLRBOS2:
	MOV	CX,1800h		; start of line 24
	MOV	DX,184Fh		; end of line 24
	MOV	BH,0Bh			; attribute
	AND	BH,Byte Ptr X36B8
	MOV	AX,0700h		; Initialize windows
	INT	10h			; PC ROM BIOS Video Driver Service
	PUSH	DS
	POP	ES
	MOV	DI,Offset BOSLINE
	MOV	CX,80
	MOV	AL,' '
	CLD
	REP	STOSB
CLRBOSX:
	RET
;----------------------------------------
;
;	Set current video details
;
;	Entry = AL = video mode (8 modes as per table SCRMODTBL)
;
;		0 = 40 Column text without status line
;		1 = 40 Column text without status line
;		2 = 80 Column text with status line
;		3 = 80 Column text with status line
;		4 = 40 Column graphics without status line
;		5 = 40 Column graphics without status line
;		6 = 80 Column graphics without status line
;		7 = 80 Column text with status line
;
SETCURCRT:
	AND	AL,07h
	MOV	Byte Ptr CRTMOD,AL	; Current mode
	MUL	Byte Ptr CRTTEL
	ADD	AX,Offset SCRMODTBL
	XCHG	AX,SI			; Calc offset into SCRMODTBL
	MOV	DI,Offset CRTCOL	; Offset of current video details
;
	LODSW				; [table  ] width and mode
	MOV	Byte Ptr BDOSCONWID,AL	; Set CPM screen width
	STOSW				; [current] width and mode
	LODSW				; [table  ] "cursor on" shape
	STOSW				; [current] "current cursor" shape
	STOSW				; [current] "normal cursor" shape
	OR	AH,20h
	STOSW				; [current] "cursor off" shape
	LODSW				; [table  ] Status line available flag
	STOSW				; [current] Status line available flag
	RET
;----------------------------------------
;
;	Reset disks and I/O ports
;
RESETIO:
	MOV	DL,0			; Drive 0 (A:)
	MOV	AH,0			; Reset disk system
	INT	13h			; PC ROM BIOS Floppy Disk Services

	MOV	DL,80h			; Hard disks and floppy disks
	MOV	AH,0			; Reset disk system
	INT	13h			; PC ROM BIOS Floppy Disk Services

	MOV	DX,2			; Printer number 2
RSTIO1:	MOV	AH,1			; Initialise printer port
	INT	17h			; PC ROM BIOS - Printer service
	DEC	DX
	JNS	RSTIO1			; Previous one
;
	INC	DX
	MOV	AH,0			; Init port
	MOV	AL,Byte Ptr SER1	; Initialisation parameter (port 1)
	INT	14h			; PC ROM BIOS - Serial Port Service
	INC	DX
	MOV	AH,0			; Init port
	MOV	AL,Byte Ptr SER2	; Initialisation parameter (port 2)
	INT	14h			; PC ROM BIOS - Serial Port Service
;
	PUSH	DS
	MOV	CX,2			; Only reset 2 serial i/o
	LDS	SI,DWord Ptr SERIOBASE	; -> 1st serial i/o base i/o addr
RSTIO2:	LODSW				; Get base i/o address
	TEST	AX,AX
	JZ	RSTIOX			; No more serial i/o, exit
;
	ADD	AX,4			; point to modem control register
	XCHG	AX,DX
	MOV	AL,3
	OUT	DX,AL			; send it
	LOOP	RSTIO2			; Next one
RSTIOX:	POP	DS
	RET

;*********************************************
;*                                           *
;*               Data Areas                  *
;*                                           *
;*********************************************
data_offset	EQU offset $

		DSEG
		ORG	data_offset	; contiguous with code

X32CF		RS	75		; ?

SYS_CONI	DW	1		; Console input
SYS_CONO	DW	2		; Console output
SYS_AUXI	DW	1		; Aux input
SYS_AUXO	DW	2		; Aux output
SYS_LST		DW	2		; List input/output
DELTAVEC	RW	1		; For changing vectors
DELTALO		RB	1		; 

X3316		DB	0DBh		; ?
;
;	Character i/o branch tables
;
INROUTINES	DW	Offset KEYIN	; Keyboard
		DW	Offset KEYIN	; Cover screen
		DW	Offset SPIN0	; Serial port #0
		DW	Offset SPIN1	; Serial port #0
		DW	Offset KEYIN	; Printer #0
		DW	Offset KEYIN	; Printer #1
		DW	Offset KEYIN	; Printer #2
		DW	Offset KEYIN	; Cassette
		DW	Offset PENIN	; Light pen
		DW	Offset KEYIN	; Game
		DW	Offset KEYIN	; Dummy
;
OUTROUTINES	DW	Offset VIDOUT	; Normal console output
		DW	Offset VIDOUT	; Screen (video)
		DW	Offset SPOUT0	; Serial port #0 output
		DW	Offset SPOUT1	; Serial port #1 output
		DW	Offset PRNOUT0	; Printer #0 output
		DW	Offset PRNOUT1	; Printer #1 output
		DW	Offset PRNOUT2	; Printer #2 output
		DW	Offset VIDOUT	; Cassette
		DW	Offset VIDOUT	; Light pen
		DW	Offset VIDOUT	; Game
		DW	Offset VIDOUT	; Dummy

OUTSTATUS	DW	Offset KEYSTAT	; Keyboard
		DW	Offset NULLST	; Always ready
		DW	Offset SOSTAT0	; Serial port #0
		DW	Offset SOSTAT1	; Serial port #1
		DW	Offset PRNSTAT0	; Printer #0
		DW	Offset PRNSTAT1	; Printer #1
		DW	Offset PRNSTAT2	; Printer #2
		DW	Offset NULLST	; Cassette
		DW	Offset PENST	; Light pen
		DW	Offset NULLST	; Game
		DW	Offset NULLST	; Dummy

;----------------------------------------

KEYPTR	DW	Offset KEYBUF
KEYBUF	RS	20			; 20 bytes long
	DB	0			; Ends with 0

X3377	DB	0DBh			; ?

PFKSCAN	DB	'SRQPOMKIHGDCBA@?>=<;'


PFKTABLE	DB	'DIR A:',CR,0,'............'	; F1
		DB	'DIR B:',CR,0,'............'	; F2
		DB	'STAT',CR,0,'..............'	; F3
		DB	'STAT ',0,'..............'	; F4
		DB	'STAT A:*.*',CR,0,'........'	; F5
		DB	'STAT B:*.*',CR,0,'........'	; F6
		DB	'ASSIGN',CR,0,'............'	; F7
		DB	'CONFIG',CR,0,'............'	; F8
		DB	'DSKMAINT',CR,0,'..........'	; F9
		DB	'FUNCTION',CR,0,'..........'	; F10
		DB	ESC,'H',0,'.................'	; HOME
		DB	ESC,'A',0,'.................'	; UP ARROW
		DB	ESC,'I',0,'.................'	; PAGE UP
		DB	ESC,'D',0,'.................'	; CURSOR LEFT
		DB	ESC,'C',0,'.................'	; CURSOR RIGHT
		DB	1Ah,0,'..................'	; END
		DB	ESC,'B',0,'.................'	; CURSOR DOWN
		DB	0Ah,0,'..................'	; PAGE DOWN
		DB	ESC,'L',0,'.................'	; INSERT
		DB	7Fh,0,'..................'	; DELETE

STATE		DW	Offset VIDNORM	; Current state

CURCOL		DB	0		; Cursor column
CURROW		DB	0		; Cursor row
CURSOR		EQU	Word Ptr CURCOL

SAVECURSOR	DW	0		; For esc codes

ATTRIB		DB	07h		; Default attribute
WRAPFLAG	DB	TRUE		; End of line wrap
EPFKPTR		DW	0
EPFKCOUNT	DB	0
VSETPALBH	DB	0		; Set colour palet - BH value

;
;	Escape lookup table for IBM CP/M codes subset
;
ESCTBL	DB	'HABCDIjkYEJloKLMNabcdefghmnpqstruvwxy/:10'
;
;	Escape jump table for IBM CP/M codes subset
;
	DW	Offset VHOME		; ESC H - home cursor
	DW	Offset VUP		; ESC A - cursor up
	DW	Offset VDOWN		; ESC B - cursor down
	DW	Offset VFORW		; ESC C - cursor forward (right)
	DW	Offset VBACK		; ESC D - cursor back (left)
	DW	Offset VUPSAT		; ESC I - cursor up (scroll at top)
	DW	Offset VSAVCUR		; ESC j - save cursor position
	DW	Offset VRSTCUR		; ESC k - restore cursor position
	DW	Offset VSETCUR		; ESC Y - set cursor position
	DW	Offset VEOSALL		; ESC E - erase screen and home cursor
	DW	Offset VEOSEND		; ESC J - erase to end of screen
	DW	Offset VECLIN		; ESC l - erase current line
	DW	Offset VEOLBEG		; ESC o - erase beginning of line to cursor
	DW	Offset VEOLEND		; ESC K - erase to end of line
	DW	Offset VIASLIN		; ESC L - insert line and scroll down
	DW	Offset VDASLIN		; ESC M - delete line and scroll up
	DW	Offset VDELCHR		; ESC N - delete character at cursor
	DW	Offset VSETMODE		; ESC a - set video mode
	DW	Offset VFRGND		; ESC b - set foreground colour
	DW	Offset VBKGND		; ESC c - set background colour
	DW	Offset VNEWCONI		; ESC d - redirect console input
	DW	Offset VNEWCONO		; ESC e - redirect console output
	DW	Offset VNEWAUXI		; ESC f - redirect auxilary input
	DW	Offset VNEWAUXO		; ESC g - redirect auxilary output
	DW	Offset VNEWLST		; ESC h - redirect list output
	DW	Offset VCURON		; ESC m - turn cursor on
	DW	Offset VCUROFF		; ESC n - turn cursor off
	DW	Offset VREVON		; ESC p - reverse video on
	DW	Offset VREVOFF		; ESC q - reverse video off
	DW	Offset VBLINKON		; ESC s - turn blink text on
	DW	Offset VBLINKOFF	; ESC t - turn blink text off
	DW	Offset VINTON		; ESC r - turn high intensity text on
	DW	Offset VINTOFF		; ESC u - turn high intensity text off
	DW	Offset VWRAP		; ESC v - wrap around at line end
	DW	Offset VNOWRAP		; ESC w - no wrap at line end
	DW	Offset VMON80		; ESC x - set MONO 80 mode
	DW	Offset VCOL80		; ESC y - set COL 80 mode
	DW	Offset VSETPAL		; ESC / - set colour palet
	DW	Offset VPFK		; ESC : - set programmable function keys
	DW	Offset VBOSON		; ESC 1 - turn status line on
ESCJMP	DW	Offset VBOSOFF		; ESC 0 - turn off/clear status line
;
X35A3	DB	0DBh			; ?
;
;	Memory region table
;
MRT		DB	1		; One region
MRTSTART	RW	1
MRTLEN		RW	1
;
		RS	28		; 

BRKFLAG		DB	0		;
X35C6		DB	0DBh		; ?
SPBREAK		DW	0		; stack pointer
SSBREAK		DW	0		; stack segment

BREAKMES	DB	39h		; length of following text
		DB	'    *** User Program Break ***      '
		DB	'Return to CP/M (Y/N)?'

ZEROMES		DB	49h		; length of following text
		DB	' *** Divide by Zero Error from '
ZEROMESADR	DB	'0000:0000'
		DB	 ' ***    Type any key to continue.'

INTERMES	DB	4Dh		; length of following text
		DB	' *** Unexpected Interrupt #'
INTERMESNUM	DB	'00'
		DB	' from '
INTERMESADR	DB	'0000:0000'
		DB	 ' ***    Type any key to continue.'
;
HEXCHARTBL	DB	'0123456789ABCDEF'

;
; Screen data
;
CRTMOD		DB	0		; Screen mode
CRTTEL		DB	6h		; Length of entries in SCRMODTBL
CRTCOL		DB	0		; Max column (width)
CRTBVM		DB	0		; BIOS video mode
CURCUR		DW	0		; Current cursor type
CURLON		DW	0		; Cursor lines normal mode
CURLOFF		DW	0		; Cursor lines off code
BOSFLAG		DB	0		; Bottom of screen line flag

X36B8		DB	0			; ?
;
; Screen mode table detail
;	+0 (byte) = Screen width
;	+1 (byte) = Video mode for BIOS equipment word
;	+2 (word) = "Cursor on" shape
;	+4 (byte) = Status line available (0FFh=Yes, 00h=No)
;	+5 (byte) = Mode (0FFh=Text, 00h=Graphics)
;
SCRMODTBL	DB	28h,10h,07h,06h,00h,0FFh	; 40 Column text
		DB	28h,10h,07h,06h,00h,0FFh	; 40 Column text
		DB	50h,20h,07h,06h,0FFh,0FFh	; 80 Column text
		DB	50h,20h,07h,06h,0FFh,0FFH	; 80 Column text
		DB	28h,20h,07h,06h,00h,00h		; 40 Column graphics
		DB	28h,20h,07h,06h,00h,00h		; 40 Column graphics
		DB	50h,20h,07h,06h,00h,00h		; 80 Column graphics
		DB	50h,30h,0Ch,0Bh,0FFh,0FFh	; 80 Column text
;
CRLF		DB	CR,LF
;
BOSMSGPTR	DW	Offset TIMEMSG	; pointer to status line text
					; (defaults to time message)
;
SPSAVE		RW	1		; Save stack pointer
SSSAVE		RW	1		; Save stack segment
;
X36F1		DB	0DBh		; ?
;
VIDBSY		DB	0		; Video busy flag
X36F3		DB	12h,00h
X36F5		DW	0
CMONTH		DW	'99'		; Initially impossible
CDAY		DW	'99'
CHOUR		DW	'00'
CUSER		DB	99
;
X36FE		DB	0DBh		; ?
;
TIMEMSG		DB	42h
		DB	'     User '
BUSER		RW	1
		DB	'     '
BHOUR		DB	' 0'
		DB	':'
BMINUTE		RW	1
		DB	':'
BSECOND		RW	1
		DB	' '
BAMPM		DW	'  '
		DB	'     '
BMONTH		DB	' Dec. '
BDAY		RW	1
		DB	', '
BCENT		DB	'19'
BYEAR		RW	1
		DB	'     '
;
BSYSMES		RB	14
;
		DB	0DBh		; ?
;
BOSLINE		RS	80
;
MONTHNAMES	DB	'JanFebMarAprMayJun'
		DB	'JulAugSepOctNovDec'
;
MONTHLENGTH	DW	'32'
FEBLENGTH	DW	'29','32','31','32','31'
		DW	'32','32','31','32','31','32'
;
;
;	Error messages
;
ERRDISKLEN	RB	1
ERRDISK		DB	'Disk Error  Drive '
EDRIVE		RB	1
		DB	':  Track '
ETRACK		RB	4
		DB	0


TEN		DW	10

DEFF		DB	0Fh,'Sense Op Failed'
DEBB		DB	0Fh,'Undefined Error'
DE80		DB	11h,'Failed to Respond'
DE40		DB	0Bh,'Seek Failed'
DE20		DB	11h,'Controller Failed'
DE11		DB	0Eh,'Data Corrected'
DE10		DB	0Ah,'Data Error'
DE0B		DB	10h,'Track Flag Error'
DE09		DB	11h,'DMA Address Error'
DE08		DB	10h,'DMA Chip Failure'
DE07		DB	10h,'Drive Parameters'
DE05		DB	0Ch,'Reset Failed'
DE04		DB	10h,'Sector Not Found'
DE03		DB	0Fh,'Write-Protected'
DE02		DB	11h,'Addr Mark Missing'
DE01		DB	0Fh,'Illegal Command'
DE00		DB	0Dh,'Unknown Error'
;
;	Table matching the return code with message
;
EDSKTBL		DB	0FFh
		DW	Offset DEFF	; 'Sense Op Failed'
		DB	0BBh
		DW	Offset DEBB	; 'Undefined Error'
		DB	080h
		DW	Offset DE80	; 'Failed to Respond'
		DB	040h
		DW	Offset DE40	; 'Seek Failed'
		DB	020h
		DW	Offset DE20	; 'Controller Failed'
		DB	011h
		DW	Offset DE11	; 'Data Corrected'
		DB	010h
		DW	Offset DE10	; 'Data Error'
		DB	0Bh
		DW	Offset DE0B		; 'Track Flag Error'
		DB	09h
		DW	Offset DE09		; 'DMA Address Error'
		DB	08h
		DW	Offset DE08		; 'DMA Chip Failure'
		DB	07h
		DW	Offset DE07		; 'Drive Parameters'
		DB	05h
		DW	Offset DE05		; 'Reset Failed'
		DB	04h
		DW	Offset DE04		; 'Sector Not Found'
		DB	03h
		DW	Offset DE03		; 'Write-Protected'
		DB	02h
		DW	Offset DE02		; 'Addr Mark Missing'
		DB	01h
		DW	Offset DE01		; 'Illegal Command'
EDSK_CNT	EQU	(Offset $ - Offset EDSKTBL)/3
		DW	Offset DE00		; 'Unknown Error'


PERR		DB	11h,'    Printer Error'
PERRTO		DB	08h,'Time Out'
PERROP		DB	0Ch,'Out of Paper'
PERRPO		DB	09h,'Power Off'
PERRSP		DB	01h,' '

ERRQUEST	DB	16h,'Retry, Ignore, Accept?'

DSKMSG		DB	80		; Message length
		RS	80		; Disk error message

SNDFLG		DB	0FFh
SNDTBL1		DB	04h,00h,74h,04h,00h,00h
SNDTBL2		DB	02h,00h,74h,04h,00h,00h
SNDTBL3		DB	04h,00h,12h,07h,05h,00h,0E8h,08h
		DB	04h,00h,4ch,05h,05h,00h,0F2h,05h,00h,00h
SNDTBL4		DB	04h,00h,12h,07h,05h,00h,0E8h,08h,00h,00h
SNDTBL5		DB	0Ch,00h,0E8h,08h,04h,00h,0ACh,06h
		DB	04h,00h,0E8h,08h,04h,00h,0ACh,06h,0Ah
		DB	00h,4Ch,05h,0Ch,00h,0ACh,06h,00h,00h
SNDTBL6		DB	06h,00h,0E8h,08h,06h,00h,12h,07h
		DB	06h,00h,0E8h,08h,06h,00h,0F2h,05h,06h
		DB	00h,0E8h,08h,06h,00h,74h,04h,00h,00h

;----------------------------------------
; Serial Port init parm
;
;BITS	7,6,5		4,3		2		1,0
;	Baud rate	parity		stop bits	word length (bits)
;	000 = 110	x0 = none	0 = 1		10 = 7
;	001 = 150	01 = odd	1 = 2		11 = 8
;	010 = 300	11 = even
;	100 = 1200
;	101 = 2400
;	110 = 4800
;	111 = 9600
;
SER1		DB	43h		; Serial port 1 init parms
;					; 300 baud
;                                       ; parity none
;					; 1 stop bit
;					; 8 bits
SER2		DB	43h		; Serial port 2 init parms
;					; 300 baud
;                                       ; parity none
;					; 1 stop bit
;					; 8 bits
;
SERBUFSTS	DB	20h		; tx hold reg empty code ?
SERIOBASE	DW	0000h,0040h	; Segment [40] and offset [0]
;					;  of 1st serial i/o port
;					;  base i/o address
;					;  (filled in by POST)
;
;----------------------------------------
;
PENBUF		RS	6
PENBUFEND	EQU	(Offset $)
PENPTR		DW	Offset PENBUFEND
PENERR		DB	1Fh,'    Waiting for Light Pen Input'
;
X3A5A		DB	0DBh		; ?
;
;--------------------------------------
;
; Disk parameters
;
DISK		DB	0		; Current/required disk
;
; INT 0E6 (unknown disk drive) parameter table
;
DRV_NUM		DB	0		; drive number (0=1st, 1=2nd...)
DBK_FLG		DB	0		; deblock flag
					; 	0=normal write
					;	1=directory write
					;	?=write with no preread)
TRK		DW	0		; Required track
SECT		DW	0		; Required sector
DMA_ADR		DW	0		; Required DMA address
DMA_SEG		DW	0		; Required DMA segment
DMAADDR		EQU	DWord Ptr DMA_ADR
VFLAG		DB	FALSE		; Verify (non-zero if verify writes)
;
;
;
X3A67		DB	0DBh		; ?
;
DSKBUF_ADR	DW	0		; Disk buffer address
DSKBUF_SEG	DW	0		; Disk buffer segment
DSKBUFADDR	EQU	DWord Ptr DSKBUF_ADR
;
X3A6C		DB	04h		; ?
X3A6D		DB	0FFh		; Disk error or change ???
X3A6E		DW	0000h		; ?
X3A70		DB	00h		; ?
X3A71		DB	00h		; ?
MDSKSEG		DW	0D000h		; Mdisk segment
MDSKSIZ		DW	0000h		; Mdisk size (Kb)
;
; Disk routines - selected by disk
;
DSKDRVRTN	DW	Offset DSKRTNSEL	; Disk	0  - A
		DW	Offset DSKRTNSEL	;	1  - B
		DW	Offset DSKRTNSEL	;	2  - C
		DW	Offset DSKRTNSEL	;	3  - D
		DW	Offset DSKRTNINV	;	4  - E
		DW	Offset DSKRTNINV	;	5  - F
		DW	Offset DSKRTNINV	;	6  - G
		DW	Offset DSKRTNINV	;	7  - H
		DW	Offset DSKRTNINV	;	8  - I
		DW	Offset DSKRTNINV	;	9  - J
		DW	Offset DSKRTNINV	;	10 - K
		DW	Offset DSKRTNINV	;	11 - L
DSKDRVRTNM	DW	Offset DSKRTNINV	;	12 - M
		DW	Offset DSKRTNINV	;	13 - N
		DW	Offset DSKRTNINV	;	14 - O
		DW	Offset DSKRTNINV	;	15 - P
;
; Disk Function Routines
;
DSKFUNRTN	DW	Offset SELECT_PHYDSK	; Select disk
		DW	Offset READ_PHYTRK	; Read track
		DW	Offset WRITE_PHYTRK	; Write track
		DW	Offset HOME_PHYDSK	; Home current disk
;
; Memory Disk Function Routines
;
MEMFUNRTN	DW	Offset SELECT_MEMDSK	; Select disk
		DW	Offset READ_MEMTRK	; Read track
		DW	Offset WRITE_MEMTRK	; Write track
		DW	Offset HOME_MEMDSK	; Home current disk

X3AA6		DW	0000h
X3AA8		DW	0000h

DPHTBL		DW	Offset DPH0
		DW	Offset DPH1
		DW	Offset DPH2
		DW	Offset DPH3

X3AB2		DB	0,0,0,0		; Valid drives table [only 4 supported]
X3AB6		DW	0,0,0,0
NOS2WRT		DB	0		; No. sectors to write (ROM BIOS CALL)

X3ABF		DB	0DBh		; ?
;
FIDDSMEM	DW	0		; FIDDS (kb) memory
FIDDSSEG	DW	0		; FIDDS (kb) memory segment ?
X3AC4		DW	Offset DIRBUF
;
; IBM PC Diskette parameters - used by floppy disk controller
;
DSKPRM		DB	0CFh	; bits 7-4 :step rate - Ch=8ms
				; bits 3-0 :head unload time - Fh = 240ms
		DB	02h	; bits 7-1 :head load time - 2h = 8ms
				; bit  0   :non-DMA mode (always 0)
		DB	25h	; delay until motor turned off (in clock ticks)
		DB	02h	; bytes per sector
				; (00h=128, 01h=256, 02h=512, 03h=1024)
		DB	08h	; sectors per track
		DB	2Ah	; length of gap between sectors
				; (2Ah for 5.25", 1Bh for 3.5")
		DB	0FFh	; data length
				; (ignored if bytes-per-sector field non zero)
		DB	50h	; gap length when formatting
				; (50h for 5.25", 6Ch for 3.5")
		DB	0E5h	; format filler byte
		DB	00h	; head settle time in milliseconds
		DB	04h	; motor start time in 1/8 second
;
X3AD1		DB	0DBh		; ?

DPBASE		EQU	$
DPH0		DW	0		; No translation table
		DW	0,0,0		; reserved
		DW	Offset DIRBUF	; Directory scratch pad
DPB0		DW	0
		DW	0		; Check vector 0
		DW	0		; Allocation vector 0
;
DPH1		DW	0		; No translation table
		DW	0,0,0		; reserved
		DW	Offset DIRBUF	; Directory scratch pad
DPB1		DW	0
		DW	0		; Check vector 0
		DW	0		; Allocation vector 0
;
DPH2		DW	0		; No translation table
		DW	0,0,0		; reserved
		DW	Offset DIRBUF	; Directory scratch pad
DPB2		DW	0
		DW	0		; Check vector 0
		DW	0		; Allocation vector 0
;
DPH3		DW	0		; No translation table
		DW	0,0,0		; reserved
		DW	Offset DIRBUF	; Directory scratch pad
DPB3		DW	0
		DW	0		; Check vector 0
		DW	0		; Allocation vector 0
;
DPH4		DW	0		; No translation table
		DW	0,0,0		; reserved
		DW	Offset DIRBUF	; Directory scratch pad
DPB4		DW	0
		DW	0		; Check vector 0
		DW	CSV14		; Allocation vector 4
;
; DPBK1 and DPBK2 Physical disks
;
DPBK1	DW	0020h	; 0032 		; CP/M Sectors per track	(SPT)
	DB	03h	; 03 		; Block shift factor		(BSH)
	DB	07h	; 07 		; Block mask			(BLM)
	DB	00h	; 00 		; Null mask			(EXM)
	DW	009Bh	; 0155 		; Disk size - 1			(DSM)
	DW	003Fh	; 0063 		; Directory max			(DRM)
	DB	0C0h	; 11000000b	; Alloc 0			(AL0)
	DB	00h	; 00000000b	; Alloc 1			(AL1)
	DW	0010h	; 0016		; Check size			(CKS)
	DW	0001h	; 0001		; Track offset			(OFF)
;
	DB	0DBh			; ?
;
DPBK2	DW	0020h	; 0032 		; CP/M Sectors per track	(SPT)
	DB	04h	; 04 		; Block shift factor		(BSH)
	DB	0Fh	; 15 		; Block mask			(BLM)
	DB	01h	; 01 		; Null mask			(EXM)
	DW	009Dh	; 0157 		; Disk size - 1			(DSM)
	DW	003Fh	; 0063 		; Directory max			(DRM)
	DB	080h	; 10000000b	; Alloc 0			(AL0)
	DB	00h	; 00000000b	; Alloc 1			(AL1)
	DW	0010h	; 0016		; Check size			(CKS)
	DW	0001h	; 0001		; Track offset			(OFF)
;
	DB	0DBh			; ?
;
; DPBK3 and DPBK4 Memory disks

DPBK3	DW	0020h	; 0032 		; CP/M Sectors per track	(SPT)
	DB	03h	; 03 		; Block shift factor		(BSH)
	DB	07h	; 07 		; Block mask			(BLM)
	DB	00h	; 00 		; Null mask			(EXM)
	DW	0000	; 0000 		; Disk size - 1			(DSM)
	DW	003Fh	; 0063 		; Directory max			(DRM)
	DB	0C0h	; 11000000b	; Alloc 0			(AL0)
	DB	00h	; 00000000b	; Alloc 1			(AL1)
	DW	0000h	; 0000		; Check size			(CKS)
	DW	0000h	; 0000		; Track offset			(OFF)
;
	DB	0DBh			; ?
;
DPBK4	DW	0020h	; 0032 		; CP/M Sectors per track	(SPT)
	DB	04h	; 04 		; Block shift factor		(BSH)
	DB	0Fh	; 15 		; Block mask			(BLM)
	DB	01h	; 01 		; Null mask			(EXM)
	DW	0000h	; 0000 		; Disk size - 1			(DSM)
	DW	003Fh	; 0063 		; Directory max			(DRM)
	DB	080h	; 10000000b	; Alloc 0			(AL0)
	DB	00h	; 00000000b	; Alloc 1			(AL1)
	DW	0000h	; 0000		; Check size			(CKS)
	DW	0000h	; 0000		; Track offset			(OFF)
;
	DB	0DBh			; ?
;
	RS	64			; Break interrupt stack
BRKSTK	EQU	Offset $
;
	RS	64			; Local stack
LCLSTK	EQU	offset $		; (for init and timer routines)
;
DSKBUF	EQU	offset $		; Disk buffer
;
	CSEG
	ORG	DSKBUF			; code contiguous with data
;
;----------------------------------------
;
; Enter from BOOT ROM or LOADER
;
; Once only INIT code/data occupies the disk buffer.
;
INIT:
	MOV	AX,CS
	MOV	SS,AX
	MOV	SP,Offset LCLSTK
	CALL	SETISR			; Setup interrupt vectors
	MOV	AX,CS
	MOV	DS,AX
	MOV	ES,AX
	MOV	Byte Ptr IPLDSK,DL	; Initial program load disk
;
	MOV	SI,Offset SIGNON	; 'CPM-86 for the PC..'
	CALL	DISP_STR		; Display string
;
	INT	11h			; PC ROM BIOS - Get Equipment List
	ADD	AX,0040h
	AND	AX,0FFF3h
	PUSH	AX
	CALL	SETINTVID		; Set initial video mode
	MOV	AH,08h			; Get drive parameters
	MOV	DL,80h
	INT	13h			; PC ROM BIOS Floppy Disk Services
	MOV	AL,DL			; Number of drives
	POP	DX
	JC	INIT1			; Error ?
;
	SHL	AL,1h
	SHL	AL,1h
	OR	DL,AL
INIT1:	MOV	SI,Offset SIGNDSK	; 'Diskette Drive(s)'
	MOV	CX,0706h		; mask + shift factor
	CALL	DISP_HDWNUM		; Display string and number supported
	MOV	Byte Ptr X3A6C,AL
	MOV	Byte Ptr FDDCNT,AL
;
	MOV	SI,Offset SIGNHDD	; 'Hard Disk Drive(s)'
	MOV	CX,0302h		; mask + shift factor
	CALL	DISP_HDWNUM		; Display string and number supported
	MOV	Byte Ptr HDDCNT,AL	; Save number hard drives
;
	PUSH	DX
	CALL	X3EB2
	CALL	X3D52
	POP	DX
	MOV	SI,Offset SIGNPRT	; 'Parallel Printer(s)'
	MOV	CX,030Eh		; mask + shift factor
	CALL	DISP_HDWNUM		; Display string and number supported
;
	MOV	SI,Offset SYS_LST
	MOV	CX,0010h
	CALL	X3FEF
	MOV	SI,Offset SIGNSER	; 'Serial Port(s)'
	MOV	CX,0709h		; mask + shift factor
	CALL	DISP_HDWNUM		; Display string and number supported
;
	MOV	SI,Offset SYS_AUXI
	MOV	CX,0004h
	CALL	X3FEF
	MOV	SI,Offset SYS_AUXO
	CALL	X3FEF
;
	CALL	INITMDISK		; Initialize memory disk
	CALL	SETMDSKDP		; Setup memory disk parameters
	MOV	SI,Offset SIGNMEM	; 'Memory (Kb)'
	CALL	DISP_STR8
	INT	12h			; PC ROM BIOS - Get Memory Size
;					; Returns AX=Number of kilobytes
;
	MOV	CL,6			; 2^6 * 16 =1024
	SHL	AX,CL			; AX = num paragraphs
	MOV	DX,Word Ptr MDSKSEG
	TEST	DX,DX
	JZ	INIT2
	CMP	AX,DX
	JA	INIT3
INIT2:	XCHG	AX,DX
INIT3:	MOV	AX,DX
	MOV	BX,CS
	ADD	BX,Word Ptr X420E
	MOV	Word Ptr MRTSTART,BX
	SUB	DX,BX
	MOV	Word Ptr MRTLEN,DX
	MOV	CL,06h
	SHR	AX,CL
	CALL	DISP_LRGNUM		; Display large number (AX)
;
	MOV	CX,Word Ptr MDSKSIZ	; Zero Kb Memory disk ?
	JCXZ	INIT4			; Yes, skip it
;
	MOV	SI,Offset SIGNMDK	; 'M:Disk (Kb)'
	CALL	DISP_STR8
	MOV	AX,Word Ptr MDSKSIZ
	CALL	DISP_LRGNUM		; Display large number (AX)
;
INIT4:	CALL	SETFIDMEM		; Setup FIDDS memory
	CALL	DISP_CRLF
	MOV	AH,03h			; Read cursor position
	MOV	BH,0			; Page 0
	INT	10h			; PC ROM BIOS Video Driver Service
	CMP	DH,18h
	JB	INIT5
	MOV	DH,17h
	MOV	AH,02h			; Set cursor position
	INT	10h			; PC ROM BIOS Video Driver Service
INIT5:	MOV	Word Ptr CURSOR,DX
	CLD
	MOV	SI,Offset DEFTOD	; '08/01/83,00:00:00'
	MOV	DI,Offset AMONTH
	MOV	CX,0011h
	REP	MOVSB  
	INC	DI
	MOV	AL,' '
	MOV	CX,000Eh
	REP	STOSB  
	CALL	RESETIO			; Reset disk and I/O ports
	CLI
	SUB	AX,AX
	MOV	DS,AX			; To address zero page
;
	MOV	ZEROOFF,Offset ZEROINT	; Divide by zero interrupt
	MOV	ZEROSEG,CS
;
	MOV	BREAKOFF,Offset BREAKINT; User Break interrupt
	MOV	BREAKSEG,CS
;
	MOV	TIMEROFF,Offset TIMERINT; Timer interrupt
	MOV	TIMERSEG,CS
;
	MOV	UNDSKOFF,Offset UNDSKINT; Unknown disk drive interrupt
	MOV	UNDSKSEG,CS
;
	PUSH	CS
	POP	DS
	MOV	CL,Byte Ptr DEFDRV	; Default drive
	MOV	Byte Ptr BDOSCURDRV,CL
	STI
	JMP	CCP			; Enter as for warm boot
;
;----------------------------------------
;
;	Setup Interrupt Service Routine vectors
;
SETISR:	SUB	AX,AX
	MOV	ES,AX
	MOV	DS,AX
	MOV	DI,0080h
	MOV	AX,Offset UNEXPINT	; Unexpected interrupt routine
	MOV	BX,CS			; and segment
	CLD
	MOV	CX,0040h
SETISR1:
	INC	DI		; AT PATCH - Originally STOSW instruction
	XCHG	AX,BX
	LOOP	SETISR1
;
	ADD	DI,0008h
	MOV	CX,017Ch
SETISR2:
	INC	DI		; AT PATCH - Originally STOSW instruction
	XCHG	AX,BX
	LOOP	SETISR2
;
;	Set BDOS offset to proper interrupt
;
	MOV	BDOSOFF,Offset BDOS_OFST
	MOV	BDOSSEG,CS
;
;	Set diskette parameters (pointer at INT-1E vector)
;
	MOV	I1EOFF,Offset DSKPRM
	MOV	I1ESEG,CS
	RET

;----------------------------------------

X3D52:	MOV	DI,Offset X3AB2
	MOV	CX,4
	MOV	AL,Byte Ptr IPLDSK
	MOV	DL,AL
	REPNE	SCASB  
	NEG	CX
	ADD	CX,3
	MOV	Byte Ptr DEFDRV,CL	; Set default disk drive
	MOV	SI,CX
	MOV	CX,2
	TEST	AL,80h
	JZ	X3D7E
	SHL	SI,1
	MOV	BX,Word Ptr X3AB6[SI]
	MOV	AX,[BX]
	MOV	CL,3
	CALL	X2D99
X3D7E:	MOV	DH,0			; Head 0
	MOV	BX,Offset X4780		; ES:BX - data buffer
	MOV	SI,BX
	MOV	AX,0201h		; Read Sector(s) - 1 sector
	INT	13h			; PC ROM BIOS Floppy Disk Services
	CLD
	LODSW
	CMP	AX,0DDB2h
	JZ	X3D92
	RET
X3D92:	LODSW
	MOV	Word Ptr MDSKSEG,AX
	LODSB
	MOV	Byte Ptr VFLAG,AL	; Verify enabled ?
	LODSB
	MOV	Byte Ptr DSKPRM+0,AL	; Update diskette parameters
	MOV	SI,Offset X4790
	MOV	DI,Offset KEYBUF
	MOV	CX,0014h
	REP	MOVSB  
	MOV	AX,Word Ptr X47B0
	MOV	Word Ptr FIDDSMEM,AX
	MOV	AL,Byte Ptr X47C0
	MOV	Byte Ptr SNDFLG,AL
	TEST	Byte Ptr X47E0,TRUE
	JZ	X3DC2
	MOV	AX,Word Ptr X47E4
	MOV	Word Ptr SER1,AX	; Serial ports init parms (1 and 2)
X3DC2:	TEST	Byte Ptr X47E1,TRUE
	JZ	X3DD4
	MOV	SI,Offset X47E6
	MOV	DI,Offset SYS_CONI
	MOV	CX,5
	REP	MOVSW  
X3DD4:	TEST	Byte Ptr X47E2,TRUE
	JZ	X3DE6
	MOV	SI,Offset X47F0
	MOV	DI,Offset PFKTABLE	; Function key definition table
	MOV	CX,200
	REP	MOVSW  
X3DE6:	RET

;----------------------------------------
;
;	Initialize memory disk (M:)
;
;	   Validates and initilises memory at
;	   required segment address
;
INITMDISK:
	PUSH	DS
	PUSH	ES
	MOV	AX,Word Ptr MDSKSEG	; Memory disk segment address
	TEST	AX,AX			; Specified ?
	JZ	INITMDSK2		; No, exit
;
	MOV	BX,0F5Ah		; Memory test pattern
;
INITMDSK1:
	MOV	DS,AX
	MOV	ES,AX			; Select memory disk segment
	SUB	DI,DI			; Offset zero
;
	MOV	[DI],BL
	CMP	[DI],BL			; Byte set correctly ?
	JNZ	INITMDSK2		; No, exit
;
	MOV	[DI],BH
	CMP	[DI],BH			; Byte set correctly ?
	JNZ	INITMDSK2		; No, exit
;
	MOV	CX,800h			; 2Kb
	MOV	AX,0E5E5h		; Empty fcb character
	REP	STOSW			; Fill it
;
	ADD	CS:Word Ptr MDSKSIZ,4
;
	MOV	AX,DS
	ADD	AX,0100h		; Next segment
	CMP	AX,0A000h		; Upto video RAM ?
	JB	INITMDSK1		; No, test and initialise
;
	CMP	AX,0C000h		; Video RAM ?
	JB	INITMDSK2		; Yes, exit
;
	CMP	AX,0F000h		; Upto BIOS ROM ?
	JB	INITMDSK1		; No, test and initialise
;
INITMDSK2:
	POP	ES
	POP	DS
	RET
;
;----------------------------------------
;
;	Setup memory disk parameters
;
SETMDSKDP:
	CMP	Word Ptr MDSKSIZ,0	; Zero Kb memory disk ?
	JZ	SETMDSKDPX		; Yes, exit
;
	MOV	AX,Word Ptr MDSKSIZ
	MOV	BX,Offset DPBK3
	CMP	AX,0100h
	JBE	SETMDSKDP2
	MOV	BX,Offset DPBK4
	CMP	AX,0200h
	JBE	SETMDSKDP1
	MOV	Byte Ptr 4[BX],0
;
SETMDSKDP1:
	SHR	AX,1h
;
SETMDSKDP2:
	DEC	AX
	MOV	05h[BX],AX
	MOV	Word Ptr DPB4,BX
	MOV	Word Ptr DSKDRVRTNM,Offset MEMRTNSEL
;
SETMDSKDPX:
	RET
;
;----------------------------------------
;
;	Setup FIDDS memory segment pointer
;
SETFIDMEM:
	MOV	AX,Word Ptr FIDDSMEM
	TEST	AX,AX
	JZ	SETFIDMEMX		; Zero FIDDS memory required
;
	MOV	CL,6
	SHL	AX,CL
	MOV	DX,Word Ptr MRTLEN
	SUB	DX,AX
	JB	SETFIDMEM4
	CMP	DX,0400h
	JB	SETFIDMEM4
;
	MOV	Word Ptr MRTLEN,DX
	ADD	Word Ptr MRTSTART,AX
	MOV	AX,CS
	ADD	AX,Word Ptr X420E
	MOV	Word Ptr FIDDSSEG,AX
	MOV	SI,Offset SIGNFID	; 'FIDDS (Kb)'
	CALL	DISP_STR8
	MOV	AX,Word Ptr FIDDSMEM
	CALL	DISP_LRGNUM		; Display large number (AX)
;
SETFIDMEMX:
	RET
;
SETFIDMEM4:
	MOV	Word Ptr FIDDSMEM,0
	MOV	SI,Offset ERRFD2BIG	; 'FIDDS Mem Request too large'
	JMP	DISP_CHAR		; Display string
;
;----------------------------------------
;
;	Set initial video mode
;
;		Entry AL = BIOS equipment value
;
SETINTVID:
	AND	AL,30h
	MOV	SI,7			; 80 col text with status line
	CMP	AL,30h
	JZ	SETIV2
	MOV	SI,3			; 80 col text with status line
	CMP	AL,20h
	JZ	SETIV2
	SUB	SI,SI			; 40 col text without status line
SETIV2:
	XCHG	AX,SI
	JMP	SETCURCRT		; Set current video details
;
;----------------------------------------
;
X3EB2:	CALL	X3EDD
	CALL	CHKHDDPAR		; Check HDD's for CPM partitions
	MOV	AX,Word Ptr X420A
	MOV	CL,4
	SHR	AX,CL			; x 16
	INC	AX
	MOV	Word Ptr X420E,AX
	MOV	AL,Byte Ptr X3A6C
	CBW
	MOV	CX,4
	SUB	CX,AX
	JZ	X3EDC
	SHL	AX,1h
	ADD	AX,Offset DSKDRVRTN
	XCHG	AX,DI
	PUSH	CS
	POP	ES
	MOV	AX,Offset DSKRTNINV
	CLD
	REP	STOSW  
X3EDC:	RET
;
;----------------------------------------
;
X3EDD:	MOV	AL,Byte Ptr FDDCNT	; Number of physical
	CBW				;  diskette drives.
	XCHG	AX,CX
	SUB	BX,BX
	JCXZ	X3EF3
X3EE6:	MOV	Byte Ptr X3AB2[BX],BL
	MOV	SI,Offset DPBK1
	CALL	CHKCP4
	INC	BX
	LOOP	X3EE6
X3EF3:	RET

;----------------------------------------
;
;	Check each hard drive for a single CPM partition
;
CHKHDDPAR:
	TEST	Byte Ptr HDDCNT,0FFh	; Hard disk(s) present ?
	JZ	CHKHPX			; No, skip
;
	MOV	Byte Ptr HDDCHK,80h	; Set hard disk 0 (first)
CHKHP1:	MOV	DL,Byte Ptr HDDCHK	; Drive number
	MOV	DH,0			; Head 0
	MOV	CX,0001h		; Cylinder 0, sector 1
	MOV	BX,Offset HDDBUF	; ES:BX - data buffer
	MOV	AX,0201h		; Read sector(s) - 1 sector
	INT	13h			; PC ROM BIOS Floppy Disk Services
;
	CMP	Word Ptr HDDBUF+510,0AA55h	; VAlid Master-Boot-Record?
	JNZ	CHKHP4				; No, check next drive
;
	MOV	BX,Offset HDDBUF+446	; 1st Partition table entry
	MOV	CX,4			; 4 entries
;
CHKHP2:	CMP	Byte Ptr 4[BX],0DBh	; CPM partition ?
	JZ	CHKHP3			; Yes
	ADD	BX,16			; No, point to next entry
	LOOP	CHKHP2			;   and test
	JMPS	CHKHP4			; Check next drive
;
CHKHP3:	CALL	CHKCPMPAR		; Check CPM partition
	CMP	Byte Ptr X3A6C,04h
	JNB	CHKHPX
;
CHKHP4:	INC	Byte Ptr HDDCHK		; Next drive (to check)
	DEC	Byte Ptr HDDCNT
	JNZ	CHKHP1			; Test it
;
CHKHPX:	RET

;----------------------------------------
;
;	Check valid CPM partition
;
;	   entry - BX = Partition table entry
;
CHKCPMPAR:
	MOV	DL,Byte Ptr HDDCHK	; Drive number (to check)
	MOV	DH,0			; Head 0
	MOV	CX,02h[BX]		; CX: CH=Cylinder, CL=Sector (1 to 63)
	AND	CL,0C0h
	OR	CL,04h
	MOV	BX,Offset HDDBUF	; ES:BX - sector buffer
	MOV	AX,0201h		; Read sectors - 1 sector
	INT	13h			; PC ROM BIOS Floppy Disk Services
;
; Checksum 1st sector of partition
;   Result should be zero
;
	CMP	Word Ptr HDDBUF+46,0
	JZ	CHKCP1
	MOV	CX,256
	SUB	AX,AX
CHKCP1:	ADD	AX,[BX]
	INC	BX
	INC	BX
	LOOP	CHKCP1
	TEST	AX,AX			; Valid checksum ?
	JZ	CHKCP2			; Yes
;
	MOV	SI,Offset ERRCHKSUM	; 'Hard disk label checksum error'
	JMP	DISP_CHAR		; Display string
;
; Ensure partition not too big for CPM
;
CHKCP2:	MOV	AL,Byte Ptr HDDBUF+47
	MOV	AH,0
	MOV	CL,Byte Ptr HDDBUF+43
	SHL	AX,CL
	TEST	AH,AH			; <= 8192 MB ?
	JZ	CHKCP3			; Yes
;
	MOV	SI,Offset ERRHP2BIG	; 'Hard disk partition too big'
	CALL	DISP_CHAR		; Display string
	RET
;
CHKCP3:	AND	Word Ptr HDDBUF+52,7FFFh
	MOV	SI,Offset HDDBUF+33
	MOV	DI,Word Ptr X420A
	MOV	DX,DI
	MOV	CX,Word Ptr HDDBUF+68
	ADD	CX,CX
	ADD	CX,39
	REP	MOVSB  
	MOV	Word Ptr X420A,DI
	MOV	AL,Byte Ptr X3A6C
	CBW
	MOV	SI,AX
	MOV	AL,Byte Ptr HDDCHK	; HDD (to check)
	MOV	Byte Ptr X3AB2[SI],AL
	SHL	SI,1h
	MOV	Word Ptr X3AB6[SI],DX
	INC	Byte Ptr X3A6C
	MOV	SI,DX
	ADD	SI,0008h
;
CHKCP4:	PUSH	BX
	PUSH	CX
	MOV	BX,Word Ptr X420C
	MOV	0Ah[BX],SI
	MOV	AX,Word Ptr X420A
	MOV	0Ch[BX],AX
	ADD	AX,0Bh[SI]
	MOV	0Eh[BX],AX
	XCHG	AX,BX
	MOV	AX,05h[SI]
	MOV	CL,03h
	SHR	AX,CL
	INC	AX
	ADD	AX,BX
	MOV	Word Ptr X420A,AX
	ADD	Word Ptr X420C,0010h
	POP	CX
	POP	BX
	RET

;----------------------------------------

X3FEF:	TEST	AL,AL
	JZ	X3FFC
	CMP	Byte Ptr X47E1,TRUE
	JZ	X3FFC
	MOV	[SI],CX
X3FFC:	RET

;----------------------------------------
;
;	Display null terminated string followd by
;	number of hardware units supported
;
;	   Input: SI:=string offset
;		  DS:=string segment address
;		  DX:=hardware configuration
;		  CL:=shift factor
;		  CH:=mask
;	   Output: none
;
DISP_HDWNUM:
	MOV	AX,DX
	SHR	AX,CL
	AND	AL,CH			; Zero devices ?
	JZ	DISP_HDWNUMX		; Yes, exit
;
	PUSH	AX
	CALL	DISP_STR8		; Display spaces + string SI
	POP	AX
	PUSH	AX
	CALL	DISP_NUM		; Display AL as numeric char
	CALL	DISP_CRLF
	POP	AX
DISP_HDWNUMX:
	RET

;----------------------------------------
;
;	Display 8 spaces followed by null terminated string
;
;	   Input: SI:=string offset
;		  DS:=string segment address
;	   Output: none
;
DISP_STR8:
	MOV	CX,8
;
DISP_STR8A:
	MOV	AL,' '
	CALL	DISP_CHAR		; Display char
	LOOP	DISP_STR8A
;	JMP	DISP_STR		; Fall through

;----------------------------------------
;
;	Display null terminated string
;
;	   Input: SI:=string offset
;		  DS:=string segment address
;	   Output: none
;
DISP_STR:
	CLD
	LODSB				; Get char
;
DISP_CHAR1:
	CALL	DISP_CHAR		; Display it
	LODSB				; Get next char
	OR	AL,AL			; End of string ?
	JNZ	DISP_CHAR1
	RET				; Yes

;----------------------------------------
;
;	Display large number AX as numeric chars
;
;	   Input: AX:=number (largest number accepted = 999)
;	   Output: none
;
DISP_LRGNUM:
	CMP	AX,10			; Less than 10 ?
	JB	DISP_LRGNUM2		; Yes, display digit
;
	MOV	CL,100
	DIV	CL			; Divide by 100
	OR	AL,AL			; Any hundreds ?
	JZ	DISP_LRGNUM1		; No
;
	CALL	DISP_NUM		; Display hundreds digit
;
DISP_LRGNUM1:
	MOV	AL,AH
	SUB	AH,AH
	AAM				; Split AL into tens and units
	XCHG	AL,AH			; First tens
	CALL	DISP_NUM		; Display tens digit
	MOV	AL,AH			; Now units
;
DISP_LRGNUM2:
	CALL	DISP_NUM		; Display units digit
	JMPS	DISP_CRLF		; Display CR+Lf then return

;----------------------------------------
;
;	Display AL as numeric char
;
;	   Input: AL:=char	(preserved throughout call)
;	   Output: none
;
DISP_NUM:
	PUSH	AX
	OR	AL,30h			; Make numeric ASCII char
	CALL	DISP_CHAR		; Display char
	POP	AX
	RET
;----------------------------------------
;
;	Display carriage return and linefeed
;
DISP_CRLF:
	MOV	AL,CR
	CALL	DISP_CHAR		; Display char
	MOV	AL,LF
;					; Fall through
;----------------------------------------
;
;	Display character in teletype mode
;
;	   Input: AL:=char
;	   Output: none
;
DISP_CHAR:
	PUSH	SI
	MOV	BX,0			; page zero, foreground colour 0
	MOV	AH,0Eh			; Write text in teletype mode
	INT	10h			; PC ROM BIOS Video Driver Service
	POP	SI
	RET
;
;*********************************************
;*                                           *
;*        INIT routine Data Areas            *
;*                                           *
;*********************************************

data_offst2	EQU offset $
;
		DSEG
		ORG	data_offst2	; contiguous with code
;
SIGNON		DB	cr,lf,lf
		DB	'CP/M-86 for the IBM PC and IBM PC XT  Version 1.1'
		DB	cr,lf
		DB	'Copyright (C) 1983, Digital Research'
		DB	cr,lf,lf,lf
		DB	'Hardware Supported :'
		DB	cr,lf,lf,0
;
SIGNDSK		DB	'  Diskette Drive(s) : ',0
SIGNHDD		DB	' Hard Disk Drive(s) : ',0
SIGNPRT		DB	'Parallel Printer(s) : ',0
SIGNSER		DB	'     Serial Port(s) : ',0
SIGNMEM		DB	'        Memory (Kb) : ',0
SIGNMDK		DB	'        M:Disk (Kb) : ',0
SIGNFID		DB	'         FIDDS (Kb) : ',0
;
DEFTOD		DB	'08/01/83,00:00:00'
;
ERRCHKSUM	DB	cr,lf
		DB	'Hard Disk Label Checksum Error'
		DB	cr,lf,lf,0
;
ERRHP2BIG	DB	cr,lf
		DB	'Hard Disk Partition is Too Big for CP/M-86'
		DB	cr,lf,lf,0
;
ERRFD2BIG	DB	cr,lf
		DB	'FIDDS memory request is too large'
		DB	cr,lf,0
;
HDDCHK		DB	0		; Hard disk drive (to check)
IPLDSK		DB	0		; Initial program load disk
DEFDRV		DB	0		; Default drive (for CCP)
FDDCNT		DB	0		; Number of diskette drives
HDDCNT		DB	0		; Number of Hard disk drives
;
X420A		DB	086h,04Ch	; 864C  4C86?
X420C		DW	Offset DPBASE	;
X420E		DW	0		;
;
		RS	1390
		DB	0DBh		; ?
		DB	00h
;
X4780		RS	16
X4790		RS	32
X47B0		RS	16
X47C0		RS	32
;
X47E0		DB	0
X47E1		DB	0
X47E2		DB	0
X47E3		DB	0
X47E4		DW	0
X47E6		DW	0,0,0,0,0
X47F0		RS	400
;
		ORG	4980h
HDDBUF		RS	512		; HDD sector buffer

		ORG	4BE2h
DIRBUF		RS	128		; Directory buffer
;
		ORG	4C62h
CSV14		EQU	$

;*********************************************

; End of PCBIOS.A86 file

	END

