;**************************************************************
;*
;*             C P / M   VERSION   2 . 2
;*
;*   RECONSTRUCTED FROM MEMORY IMAGE ON FEBRUARY 27, 1981
;*
;*                BY CLARK A. CALKINS
;*
;**************************************************************
;
;   SET MEMORY LIMIT HERE. THIS IS THE AMOUNT OF CONTIGEOUS
; RAM STARTING FROM 0000. CP/M WILL RESIDE AT THE END OF THIS SPACE.
; all instructions changed to .z80 mnemonics with XIZ.COM
; change dbgmonitor so it boots into cp/m directly
; changed 3 files on b drive 
;
; add conditional assembly instructions here either .equ true or .equ false
; all conditional assembly groups start with CONDxxxx:
; default is true ie the original instructions eg .IF TSR is the original code
; so to add a new group set the name to false else set it to true for the old code
FALSE:		.EQU 0
TRUE:		.EQU ~FALSE

; list of conditional assembly instructions

CONDSWAPAB:	.EQU	TRUE   ; true for original, false for new code=A=ram
CONDIDESOFT:	.EQU	TRUE	; if no ide drive, has a significant delay on soft boot (true) or quick (false)
CONDWBOOT:	.EQU	FALSE	; warm boot completely reloads cp/m (true) or doesn't (false)
CONDTSR		.EQU	TRUE	; true for original, false to add TSR routines
CONDSHORTMSG	.EQU	TRUE	; true for original warm boot signon, false for shorter one with less <CR>
CONDSUPERSUB	.EQU	TRUE	; true for no supersub autoexec, false to run supersub autoexec
CONDKEYBOARD	.EQU	TRUE	; true for original, false to scan PS2 keyboard in CONIN as well as serial
CONDCONOUT	.EQU	TRUE	; true for original, false to run echooff.com TSR to maintain radio silence and LCD
CONDUSRPATCH	.EQU	TRUE	; true for original, false to add A1> for user number as well as drive
CONDMASKCONIN 	.EQU    TRUE   ; true for original, false to add a >127 mask for conin
CONDABONLY    	.EQU    TRUE   ; true for original, false to only have drive A and B
CONDINVALIDDRV	.EQU    FALSE   ; true for original, false to go to A if an invalid drive selected

; 22nd March, 2009 - added AND 127 to the uart input (2 places) so ignores rubbish characters from an xmodem fail
; see conin

; ***************** START OF CP/M CODE ****************************

MEM:	.EQU 60			; FOR A 62K SYSTEM (TS802 TEST - WORKS OK).
;
IOBYTE:	.EQU 3			; I/O DEFINITION BYTE.
TDRIVE:	.EQU 4			; CURRENT DRIVE NAME AND USER NUMBER.
ENTRY:	.EQU 5			; ENTRY POINT FOR THE CP/M BDOS.
TFCB:	.EQU 5CH		; DEFAULT FILE CONTROL BLOCK.
TBUFF:	.EQU 80H		; I/O BUFFER AND COMMAND LINE STORAGE.
TBASE:	.EQU 100H		; TRANSIANT PROGRAM STORAGE AREA.
;
;   SET CONTROL CHARACTER EQUATES.
;
CNTRLC:	.EQU 3			; CONTROL-C
CNTRLE:	.EQU 05H		; CONTROL-E
BS:	.EQU 08H		; BACKSPACE
TAB:	.EQU 09H		; TAB
LF:	.EQU 0AH		; LINE FEED
FF:	.EQU 0CH		; FORM FEED
CR:	.EQU 0DH		; CARRIAGE RETURN
CNTRLP:	.EQU 10H		; CONTROL-P
CNTRLR:	.EQU 12H		; CONTROL-R
CNTRLS:	.EQU 13H		; CONTROL-S
CNTRLU:	.EQU 15H		; CONTROL-U
CNTRLX:	.EQU 18H		; CONTROL-X
CNTRLZ:	.EQU 1AH		; CONTROL-Z (END-OF-FILE MARK)
DEL:	.EQU 7FH		; RUBOUT
;
;   SET ORIGIN FOR CP/M
;
	.ORG (MEM-7)*1024
;
CBASE:	JP	COMMAND		; EXECUTE COMMAND PROCESSOR (CCP).
	JP	CLEARBUF	; ENTRY TO EMPTY INPUT BUFFER BEFORE STARTING CCP.

;
;   STANDARD CP/M CCP INPUT BUFFER. FORMAT IS (MAX LENGTH),
; (ACTUAL LENGTH), (CHAR #1), (CHAR #2), (CHAR #3), ETC.
;
; conditional assembly for supersub

	.IF 	CONDSUPERSUB

INBUFF:	.DB 127			; LENGTH OF INPUT BUFFER.
	.DB 0			; CURRENT LENGTH OF CONTENTS.
	.DB "COPYRIGHT"
	.DB " 1979 (C) BY "
	.DB "DIGITAL RESEARCH      "
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

	.ELSE
; modified code

INBUFF:	.DB 127			; LENGTH OF INPUT BUFFER.
; N8VEM - if add any text after this point, change db 0 below to length
; and put a 0 after the text, and delete the same number of zeros after the dig
; so that inpoint ends up at the same spot
; INBUFF+1 is cleared on the next warm boot, so only runs once.

	.DB 17			; Autoboot length of string
	.DB    "SUPERSUB AUTOEXEC"
	.DB 0			; zero at end
	.DB "COPYRIGHT"
	.DB " 1979 (C) BY "
	.DB "DIGITAL RESEARCH      "
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; delete 6 zeros N8VEM
	.DB 0,0,0		; N8VEM now 3 zeros instead of 15 - comment out line above

	.ENDIF

; back to unconditional assembly

INPOINT:.DW INBUFF+2		; INPUT LINE POINTER
NAMEPNT:.DW 0			; INPUT LINE POINTER USED FOR ERROR MESSAGE. POINTS TO
;			;START OF NAME IN ERROR.
;
;   ROUTINE TO PRINT (A) ON THE CONSOLE. ALL REGISTERS USED.
;
PRINT:	LD	E,A		; SETUP BDOS CALL.
	LD	C,2
	JP	ENTRY
;
;   ROUTINE TO PRINT (A) ON THE CONSOLE AND TO SAVE (BC).
;
PRINTB:	PUSH	BC
	CALL	PRINT
	POP	BC
	RET
;
;   ROUTINE TO SEND A CARRIAGE RETURN, LINE FEED COMBINATION
; TO THE CONSOLE.
;
CRLF:	LD	A,CR
	CALL	PRINTB
	LD	A,LF
	JP	PRINTB
;
;   ROUTINE TO SEND ONE SPACE TO THE CONSOLE AND SAVE (BC).
;
SPACE:	LD	A,' '
	JP	PRINTB
;
;   ROUTINE TO PRINT CHARACTER STRING POINTED TO BE (BC) ON THE
; CONSOLE. IT MUST TERMINATE WITH A NULL BYTE.
;
PLINE:	PUSH	BC
	CALL	CRLF
	POP	HL
PLINE2:	LD	A,(HL)
	OR	A
	RET	Z
	INC	HL
	PUSH	HL
	CALL	PRINT
	POP	HL
	JP	PLINE2
;
;   ROUTINE TO RESET THE DISK SYSTEM.
;
RESDSK:	LD	C,13
	JP	ENTRY
;
;   ROUTINE TO SELECT DISK (A).
;
DSKSEL:	LD	E,A
	LD	C,14
	JP	ENTRY
;
;   ROUTINE TO CALL BDOS AND SAVE THE RETURN CODE. THE ZERO
; FLAG IS SET ON A RETURN OF 0FFH.
;
ENTRY1:	CALL	ENTRY
	LD	(RTNCODE),A	; SAVE RETURN CODE.
	INC	A		; SET ZERO IF 0FFH RETURNED.
	RET
;
;   ROUTINE TO OPEN A FILE. (DE) MUST POINT TO THE FCB.
;
OPEN:	LD	C,15
	JP	ENTRY1
;
;   ROUTINE TO OPEN FILE AT (FCB).
;
OPENFCB:XOR	A		; CLEAR THE RECORD NUMBER BYTE AT FCB+32
	LD	(FCB+32),A
	LD	DE,FCB
	JP	OPEN
;
;   ROUTINE TO CLOSE A FILE. (DE) POINTS TO FCB.
;
CLOSE:	LD	C,16
	JP	ENTRY1
;
;   ROUTINE TO SEARCH FOR THE FIRST FILE WITH AMBIGUEOUS NAME
; (DE).
;
SRCHFST:LD	C,17
	JP	ENTRY1
;
;   SEARCH FOR THE NEXT AMBIGEOUS FILE NAME.
;
SRCHNXT:LD	C,18
	JP	ENTRY1
;
;   SEARCH FOR FILE AT (FCB).
;
SRCHFCB:LD	DE,FCB
	JP	SRCHFST
;
;   ROUTINE TO DELETE A FILE POINTED TO BY (DE).
;
DELETE:	LD	C,19
	JP	ENTRY
;
;   ROUTINE TO CALL THE BDOS AND SET THE ZERO FLAG IF A ZERO
; STATUS IS RETURNED.
;
ENTRY2:	CALL	ENTRY
	OR	A		; SET ZERO FLAG IF APPROPRIATE.
	RET
;
;   ROUTINE TO READ THE NEXT RECORD FROM A SEQUENTIAL FILE.
; (DE) POINTS TO THE FCB.
;
RDREC:	LD	C,20
	JP	ENTRY2
;
;   ROUTINE TO READ FILE AT (FCB).
;
READFCB:LD	DE,FCB
	JP	RDREC
;
;   ROUTINE TO WRITE THE NEXT RECORD OF A SEQUENTIAL FILE.
; (DE) POINTS TO THE FCB.
;
WRTREC:	LD	C,21
	JP	ENTRY2
;
;   ROUTINE TO CREATE THE FILE POINTED TO BY (DE).
;
CREATE:	LD	C,22
	JP	ENTRY1
;
;   ROUTINE TO RENAME THE FILE POINTED TO BY (DE). NOTE THAT
; THE NEW NAME STARTS AT (DE+16).
;
RENAM:	LD	C,23
	JP	ENTRY
;
;   GET THE CURRENT USER CODE.
;
GETUSR:	LD	E,0FFH
;
;   ROUTNE TO GET OR SET THE CURRENT USER CODE.
; IF (E) IS FF THEN THIS IS A GET, ELSE IT IS A SET.
;
GETSETUC:
	LD	C,32
	JP	ENTRY
;
;   ROUTINE TO SET THE CURRENT DRIVE BYTE AT (TDRIVE).
;
SETCDRV:CALL	GETUSR		; GET USER NUMBER
	ADD	A,A		; AND SHIFT INTO THE UPPER 4 BITS.
	ADD	A,A
	ADD	A,A
	ADD	A,A
	LD	HL,CDRIVE	; NOW ADD IN THE CURRENT DRIVE NUMBER.
	OR	(HL)
	LD	(TDRIVE),A	; AND SAVE.
	RET
;
;   MOVE CURRENTLY ACTIVE DRIVE DOWN TO (TDRIVE).
;
MOVECD:	LD	A,(CDRIVE)
	LD	(TDRIVE),A
	RET
;
;   ROUTINE TO CONVERT (A) INTO UPPER CASE ASCII. ONLY LETTERS
; ARE AFFECTED.
;
UPPER:	CP	'A'		; CHECK FOR LETTERS IN THE RANGE OF 'A' TO 'Z'.
	RET	C
	CP	'{'
	RET	NC
	AND	5FH		; CONVERT IT IF FOUND.
	RET
;
;   ROUTINE TO GET A LINE OF INPUT. WE MUST CHECK TO SEE IF THE
; USER IS IN (BATCH) MODE. IF SO, THEN READ THE INPUT FROM FILE
; ($$$.SUB). AT THE END, RESET TO CONSOLE INPUT.
;
GETINP:	LD	A,(BATCH)	; IF =0, THEN USE CONSOLE INPUT.
	OR	A
	JP	Z,GETINP1
;
;   USE THE SUBMIT FILE ($$$.SUB) WHICH IS PREPARED BY A
; SUBMIT RUN. IT MUST BE ON DRIVE (A) AND IT WILL BE DELETED
; IF AND ERROR OCCURES (LIKE EOF).
;
	LD	A,(CDRIVE)	; SELECT DRIVE 0 IF NEED BE.
	OR	A
	LD	A,0		; ALWAYS USE DRIVE A FOR SUBMIT.
	CALL	NZ,DSKSEL	; SELECT IT IF REQUIRED.
	LD	DE,BATCHFCB
	CALL	OPEN		; LOOK FOR IT.
	JP	Z,GETINP1	; IF NOT THERE, USE NORMAL INPUT.
	LD	A,(BATCHFCB+15)	; GET LAST RECORD NUMBER+1.
	DEC	A
	LD	(BATCHFCB+32),A
	LD	DE,BATCHFCB
	CALL	RDREC		; READ LAST RECORD.
	JP	NZ,GETINP1	; QUIT ON END OF FILE.
;
;   MOVE THIS RECORD INTO INPUT BUFFER.
;
	LD	DE,INBUFF+1
	LD	HL,TBUFF	; DATA WAS READ INTO BUFFER HERE.
	LD	B,128		; ALL 128 CHARACTERS MAY BE USED.
	CALL	HL2DE		; (HL) TO (DE), (B) BYTES.
	LD	HL,BATCHFCB+14
	LD	(HL),0		; ZERO OUT THE 'S2' BYTE.
	INC	HL		; AND DECREMENT THE RECORD COUNT.
	DEC	(HL)
	LD	DE,BATCHFCB	; CLOSE THE BATCH FILE NOW.
	CALL	CLOSE
	JP	Z,GETINP1	; QUIT ON AN ERROR.
	LD	A,(CDRIVE)	; RE-SELECT PREVIOUS DRIVE IF NEED BE.
	OR	A
	CALL	NZ,DSKSEL	; DON'T DO NEEDLESS SELECTS.
;
;   PRINT LINE JUST READ ON CONSOLE.
;
	LD	HL,INBUFF+2
	CALL	PLINE2
	CALL	CHKCON		; CHECK CONSOLE, QUIT ON A KEY.
	JP	Z,GETINP2	; JUMP IF NO KEY IS PRESSED.
;
;   TERMINATE THE SUBMIT JOB ON ANY KEYBOARD INPUT. DELETE THIS
; FILE SUCH THAT IT IS NOT RE-STARTED AND JUMP TO NORMAL KEYBOARD
; INPUT SECTION.
;
	CALL	DELBATCH	; DELETE THE BATCH FILE.
	JP	CMMND1		; AND RESTART COMMAND INPUT.
;
;   GET HERE FOR NORMAL KEYBOARD INPUT. DELETE THE SUBMIT FILE
; INCASE THERE WAS ONE.
;
GETINP1:CALL	DELBATCH	; DELETE FILE ($$$.SUB).
	CALL	SETCDRV		; RESET ACTIVE DISK.
	LD	C,10		; GET LINE FROM CONSOLE DEVICE.
	LD	DE,INBUFF
	CALL	ENTRY
	CALL	MOVECD		; RESET CURRENT DRIVE (AGAIN).
;
;   CONVERT INPUT LINE TO UPPER CASE.
;
GETINP2:LD	HL,INBUFF+1
	LD	B,(HL)		; (B)=CHARACTER COUNTER.
GETINP3:INC	HL
	LD	A,B		; END OF THE LINE?
	OR	A
	JP	Z,GETINP4
	LD	A,(HL)		; CONVERT TO UPPER CASE.
	CALL	UPPER
	LD	(HL),A
	DEC	B		; ADJUST CHARACTER COUNT.
	JP	GETINP3
GETINP4:LD	(HL),A		; ADD TRAILING NULL.
	LD	HL,INBUFF+2
	LD	(INPOINT),HL	; RESET INPUT LINE POINTER.
	RET
;
;   ROUTINE TO CHECK THE CONSOLE FOR A KEY PRESSED. THE ZERO
; FLAG IS SET IS NONE, ELSE THE CHARACTER IS RETURNED IN (A).
;
CHKCON:	LD	C,11		; CHECK CONSOLE.
	CALL	ENTRY
	OR	A
	RET	Z		; RETURN IF NOTHING.
	LD	C,1		; ELSE GET CHARACTER.
	CALL	ENTRY
	OR	A		; CLEAR ZERO FLAG AND RETURN.
	RET
;
;   ROUTINE TO GET THE CURRENTLY ACTIVE DRIVE NUMBER.
;
GETDSK:	LD	C,25
	JP	ENTRY
;
;   SET THE STABDARD DMA ADDRESS.
;
STDDMA:	LD	DE,TBUFF
;
;   ROUTINE TO SET THE DMA ADDRESS TO (DE).
;
DMASET:	LD	C,26
	JP	ENTRY
;
;  DELETE THE BATCH FILE CREATED BY SUBMIT.
;
DELBATCH:
	LD	HL,BATCH	; IS BATCH ACTIVE?
	LD	A,(HL)
	OR	A
	RET	Z
	LD	(HL),0		; YES, DE-ACTIVATE IT.
	XOR	A
	CALL	DSKSEL		; SELECT DRIVE 0 FOR SURE.
	LD	DE,BATCHFCB	; AND DELETE THIS FILE.
	CALL	DELETE
	LD	A,(CDRIVE)	; RESET CURRENT DRIVE.
	JP	DSKSEL
;
;   CHECK TO TWO STRINGS AT (PATTRN1) AND (PATTRN2). THEY MUST BE
; THE SAME OR WE HALT....
;
VERIFY:	LD	DE,PATTRN1	; THESE ARE THE SERIAL NUMBER BYTES.
	LD	HL,PATTRN2	; DITTO, BUT HOW COULD THEY BE DIFFERENT?
	LD	B,6		; 6 BYTES EACH.
VERIFY1:LD	A,(DE)
	CP	(HL)
	JP	NZ,HALT		; JUMP TO HALT ROUTINE.
	INC	DE
	INC	HL
	DEC	B
	JP	NZ,VERIFY1
	RET
;
;   PRINT BACK FILE NAME WITH A '?' TO INDICATE A SYNTAX ERROR.
;
SYNERR:	CALL	CRLF		; END CURRENT LINE.
	LD	HL,(NAMEPNT)	; THIS POINTS TO NAME IN ERROR.
SYNERR1:LD	A,(HL)		; PRINT IT UNTIL A SPACE OR NULL IS FOUND.
	CP	' '
	JP	Z,SYNERR2
	OR	A
	JP	Z,SYNERR2
	PUSH	HL
	CALL	PRINT
	POP	HL
	INC	HL
	JP	SYNERR1
SYNERR2:LD	A,'?'		; ADD TRAILING '?'.
	CALL	PRINT
	CALL	CRLF
	CALL	DELBATCH	; DELETE ANY BATCH FILE.
	JP	CMMND1		; AND RESTART FROM CONSOLE INPUT.
;
;   CHECK CHARACTER AT (DE) FOR LEGAL COMMAND INPUT. NOTE THAT THE
; ZERO FLAG IS SET IF THE CHARACTER IS A DELIMITER.
;
CHECK:	LD	A,(DE)
	OR	A
	RET	Z
	CP	' '		; CONTROL CHARACTERS ARE NOT LEGAL HERE.
	JP	C,SYNERR
	RET	Z		; CHECK FOR VALID DELIMITER.
	CP	'='
	RET	Z
	CP	'_'
	RET	Z
	CP	'.'
	RET	Z
	CP	':'
	RET	Z
	CP	$03B		; SEMICOLON ';'
	RET	Z
	CP	'<'
	RET	Z
	CP	'>'
	RET	Z
	RET
;
;   GET THE NEXT NON-BLANK CHARACTER FROM (DE).
;
NONBLANK:
	LD	A,(DE)
	OR	A		; STRING ENDS WITH A NULL.
	RET	Z
	CP	' '
	RET	NZ
	INC	DE
	JP	NONBLANK
;
;   ADD (HL)=(HL)+(A)
;
ADDHL:	ADD	A,L
	LD	L,A
	RET	NC		; TAKE CARE OF ANY CARRY.
	INC	H
	RET
;
;   CONVERT THE FIRST NAME IN (FCB).
;
CONVFST:LD	A,0
;
;   FORMAT A FILE NAME (CONVERT * TO '?', ETC.). ON RETURN,
; (A)=0 IS AN UNAMBIGEOUS NAME WAS SPECIFIED. ENTER WITH (A) EQUAL TO
; THE POSITION WITHIN THE FCB FOR THE NAME (EITHER 0 OR 16).
;
CONVERT:LD	HL,FCB
	CALL	ADDHL
	PUSH	HL
	PUSH	HL
	XOR	A
	LD	(CHGDRV),A	; INITIALIZE DRIVE CHANGE FLAG.
	LD	HL,(INPOINT)	; SET (HL) AS POINTER INTO INPUT LINE.
	EX	DE,HL
	CALL	NONBLANK	; GET NEXT NON-BLANK CHARACTER.
	EX	DE,HL
	LD	(NAMEPNT),HL	; SAVE POINTER HERE FOR ANY ERROR MESSAGE.
	EX	DE,HL
	POP	HL
	LD	A,(DE)		; GET FIRST CHARACTER.
	OR	A
	JP	Z,CONVRT1
	SBC	A,'A'-1		; MIGHT BE A DRIVE NAME, CONVERT TO BINARY.
	LD	B,A		; AND SAVE.
	INC	DE		; CHECK NEXT CHARACTER FOR A ':'.
	LD	A,(DE)
	CP	':'
	JP	Z,CONVRT2
	DEC	DE		; NOPE, MOVE POINTER BACK TO THE START OF THE LINE.
CONVRT1:LD	A,(CDRIVE)
	LD	(HL),A
	JP	CONVRT3
CONVRT2:LD	A,B
	LD	(CHGDRV),A	; SET CHANGE IN DRIVES FLAG.
	LD	(HL),B
	INC	DE
;
;   CONVERT THE BASIC FILE NAME.
;
CONVRT3:LD	B,08H
CONVRT4:CALL	CHECK
	JP	Z,CONVRT8
	INC	HL
	CP	'*'		; NOTE THAT AN '*' WILL FILL THE REMAINING
	JP	NZ,CONVRT5	; FIELD WITH '?'.
	LD	(HL),'?'
	JP	CONVRT6
CONVRT5:LD	(HL),A
	INC	DE
CONVRT6:DEC	B
	JP	NZ,CONVRT4
CONVRT7:CALL	CHECK		; GET NEXT DELIMITER.
	JP	Z,GETEXT
	INC	DE
	JP	CONVRT7
CONVRT8:INC	HL		; BLANK FILL THE FILE NAME.
	LD	(HL),' '
	DEC	B
	JP	NZ,CONVRT8
;
;   GET THE EXTENSION AND CONVERT IT.
;
GETEXT:	LD	B,03H
	CP	'.'
	JP	NZ,GETEXT5
	INC	DE
GETEXT1:CALL	CHECK
	JP	Z,GETEXT5
	INC	HL
	CP	'*'
	JP	NZ,GETEXT2
	LD	(HL),'?'
	JP	GETEXT3
GETEXT2:LD	(HL),A
	INC	DE
GETEXT3:DEC	B
	JP	NZ,GETEXT1
GETEXT4:CALL	CHECK
	JP	Z,GETEXT6
	INC	DE
	JP	GETEXT4
GETEXT5:INC	HL
	LD	(HL),' '
	DEC	B
	JP	NZ,GETEXT5
GETEXT6:LD	B,3
GETEXT7:INC	HL
	LD	(HL),0
	DEC	B
	JP	NZ,GETEXT7
	EX	DE,HL
	LD	(INPOINT),HL	; SAVE INPUT LINE POINTER.
	POP	HL
;
;   CHECK TO SEE IF THIS IS AN AMBIGEOUS FILE NAME SPECIFICATION.
; SET THE (A) REGISTER TO NON ZERO IF IT IS.
;
	LD	BC,11		; SET NAME LENGTH.
GETEXT8:INC	HL
	LD	A,(HL)
	CP	'?'		; ANY QUESTION MARKS?
	JP	NZ,GETEXT9
	INC	B		; COUNT THEM.
GETEXT9:DEC	C
	JP	NZ,GETEXT8
	LD	A,B
	OR	A
	RET
;
;   CP/M COMMAND TABLE. NOTE COMMANDS CAN BE EITHER 3 OR 4 CHARACTERS LONG.
;
NUMCMDS:.EQU 6			; NUMBER OF COMMANDS
CMDTBL:	.DB "DIR "
	.DB "ERA "
	.DB "TYPE"
	.DB "SAVE"
	.DB "REN "
	.DB "USER"
;
;   THE FOLLOWING SIX BYTES MUST AGREE WITH THOSE AT (PATTRN2)
; OR CP/M WILL HALT. WHY?
;
PATTRN1:.DB 0,22,0,0,0,0	; (* SERIAL NUMBER BYTES *).
;
;   SEARCH THE COMMAND TABLE FOR A MATCH WITH WHAT HAS JUST
; BEEN ENTERED. IF A MATCH IS FOUND, THEN WE JUMP TO THE
; PROPER SECTION. ELSE JUMP TO (UNKNOWN).
; ON RETURN, THE (C) REGISTER IS SET TO THE COMMAND NUMBER
; THAT MATCHED (OR NUMCMDS+1 IF NO MATCH).
;
SEARCH:	LD	HL,CMDTBL
	LD	C,0
SEARCH1:LD	A,C
	CP	NUMCMDS		; THIS COMMANDS EXISTS.
	RET	NC
	LD	DE,FCB+1	; CHECK THIS ONE.
	LD	B,4		; MAX COMMAND LENGTH.
SEARCH2:LD	A,(DE)
	CP	(HL)
	JP	NZ,SEARCH3	; NOT A MATCH.
	INC	DE
	INC	HL
	DEC	B
	JP	NZ,SEARCH2
	LD	A,(DE)		; ALLOW A 3 CHARACTER COMMAND TO MATCH.
	CP	' '
	JP	NZ,SEARCH4
	LD	A,C		; SET RETURN REGISTER FOR THIS COMMAND.
	RET
SEARCH3:INC	HL
	DEC	B
	JP	NZ,SEARCH3
SEARCH4:INC	C
	JP	SEARCH1
;
;   SET THE INPUT BUFFER TO EMPTY AND THEN START THE COMMAND
; PROCESSOR (CCP).
;
CLEARBUF:
	XOR	A
	LD	(INBUFF+1),A	; SECOND BYTE IS ACTUAL LENGTH.
;
;**************************************************************
;*
;*
;* C C P  -   C O N S O L E   C O M M A N D   P R O C E S S O R
;*
;**************************************************************
;*
COMMAND:LD	SP,CCPSTACK	; SETUP STACK AREA.
	PUSH	BC		; NOTE THAT (C) SHOULD BE EQUAL TO:
	LD	A,C		; (UUUUDDDD) WHERE 'UUUU' IS THE USER NUMBER
	RRA			; AND 'DDDD' IS THE DRIVE NUMBER.
	RRA
	RRA
	RRA
	AND	0FH		; ISOLATE THE USER NUMBER.
	LD	E,A
	CALL	GETSETUC	; AND SET IT.
	CALL	RESDSK		; RESET THE DISK SYSTEM.
	LD	(BATCH),A	; CLEAR BATCH MODE FLAG.
	POP	BC
	LD	A,C
	AND	0FH		; ISOLATE THE DRIVE NUMBER.
	LD	(CDRIVE),A	; AND SAVE.
	CALL	DSKSEL		; ...AND SELECT.
	LD	A,(INBUFF+1)
	OR	A		; ANYTHING IN INPUT BUFFER ALREADY?
	JP	NZ,CMMND2	; YES, WE JUST PROCESS IT.
;
;   ENTRY POINT TO GET A COMMAND LINE FROM THE CONSOLE.
;
CMMND1:	LD	SP,CCPSTACK	; SET STACK STRAIGHT.
	CALL	CRLF		; START A NEW LINE ON THE SCREEN.
	CALL	GETDSK		; GET CURRENT DRIVE.
	ADD	A,'A'
	CALL	PRINT		; PRINT CURRENT DRIVE.
	.IF 	CONDUSRPATCH
				; original code does not call this
	.ELSE	
	CALL	USRPAT		; call patch to print USER number
	.ENDIF
	LD	A,'>'
	CALL	PRINT		; AND ADD PROMPT.
	CALL	GETINP		; GET LINE FROM USER.
;
;   PROCESS COMMAND LINE HERE.
;
CMMND2:	LD	DE,TBUFF
	CALL	DMASET		; SET STANDARD DMA ADDRESS.
	CALL	GETDSK
	LD	(CDRIVE),A	; SET CURRENT DRIVE.
	CALL	CONVFST		; CONVERT NAME TYPED IN.
	CALL	NZ,SYNERR	; WILD CARDS ARE NOT ALLOWED.
	LD	A,(CHGDRV)	; IF A CHANGE IN DRIVES WAS INDICATED,
	OR	A		; THEN TREAT THIS AS AN UNKNOWN COMMAND
	JP	NZ,UNKNOWN	; WHICH GETS EXECUTED.
	CALL	SEARCH		; ELSE SEARCH COMMAND TABLE FOR A MATCH.
;
;   NOTE THAT AN UNKNOWN COMMAND RETURNS
; WITH (A) POINTING TO THE LAST ADDRESS
; IN OUR TABLE WHICH IS (UNKNOWN).
;
	LD	HL,CMDADR	; NOW, LOOK THRU OUR ADDRESS TABLE FOR COMMAND (A).
	LD	E,A		; SET (DE) TO COMMAND NUMBER.
	LD	D,0
	ADD	HL,DE
	ADD	HL,DE		; (HL)=(CMDADR)+2*(COMMAND NUMBER).
	LD	A,(HL)		; NOW PICK OUT THIS ADDRESS.
	INC	HL
	LD	H,(HL)
	LD	L,A
	JP	(HL)		; NOW EXECUTE IT.
;
;   CP/M COMMAND ADDRESS TABLE.
;
CMDADR:	.DW DIRECT,ERASE,TYPE,SAVE
	.DW RENAME,USER,UNKNOWN
;
;   HALT THE SYSTEM. REASON FOR THIS IS UNKNOWN AT PRESENT.
;
HALT:	LD	HL,76F3H	; 'DI HLT' INSTRUCTIONS.
	LD	(CBASE),HL
	LD	HL,CBASE
	JP	(HL)
;
;   READ ERROR WHILE TYPEING A FILE.
;
RDERROR:LD	BC,RDERR
	JP	PLINE
RDERR:	.DB "READ ERROR",0
;
;   REQUIRED FILE WAS NOT LOCATED.
;
NONE:	LD	BC,NOFILE
	JP	PLINE
NOFILE:	.DB "NO FILE",0
;
;   DECODE A COMMAND OF THE FORM 'A>FILENAME NUMBER{ FILENAME}.
; NOTE THAT A DRIVE SPECIFIER IS NOT ALLOWED ON THE FIRST FILE
; NAME. ON RETURN, THE NUMBER IS IN REGISTER (A). ANY ERROR
; CAUSES 'FILENAME?' TO BE PRINTED AND THE COMMAND IS ABORTED.
;
DECODE:	CALL	CONVFST		; CONVERT FILENAME.
	LD	A,(CHGDRV)	; DO NOT ALLOW A DRIVE TO BE SPECIFIED.
	OR	A
	JP	NZ,SYNERR
	LD	HL,FCB+1	; CONVERT NUMBER NOW.
	LD	BC,11		; (B)=SUM REGISTER, (C)=MAX DIGIT COUNT.
DECODE1:LD	A,(HL)
	CP	' '		; A SPACE TERMINATES THE NUMERAL.
	JP	Z,DECODE3
	INC	HL
	SUB	'0'		; MAKE BINARY FROM ASCII.
	CP	10		; LEGAL DIGIT?
	JP	NC,SYNERR
	LD	D,A		; YES, SAVE IT IN (D).
	LD	A,B		; COMPUTE (B)=(B)*10 AND CHECK FOR OVERFLOW.
	AND	0E0H
	JP	NZ,SYNERR
	LD	A,B
	RLCA
	RLCA
	RLCA			; (A)=(B)*8
	ADD	A,B		; .......*9
	JP	C,SYNERR
	ADD	A,B		; .......*10
	JP	C,SYNERR
	ADD	A,D		; ADD IN NEW DIGIT NOW.
DECODE2:JP	C,SYNERR
	LD	B,A		; AND SAVE RESULT.
	DEC	C		; ONLY LOOK AT 11 DIGITS.
	JP	NZ,DECODE1
	RET
DECODE3:LD	A,(HL)		; SPACES MUST FOLLOW (WHY?).
	CP	' '
	JP	NZ,SYNERR
	INC	HL
DECODE4:DEC	C
	JP	NZ,DECODE3
	LD	A,B		; SET (A)=THE NUMERIC VALUE ENTERED.
	RET
;
;   MOVE 3 BYTES FROM (HL) TO (DE). NOTE THAT THERE IS ONLY
; ONE REFERENCE TO THIS AT (A2D5H).
;
MOVE3:	LD	B,3
;
;   MOVE (B) BYTES FROM (HL) TO (DE).
;
HL2DE:	LD	A,(HL)
	LD	(DE),A
	INC	HL
	INC	DE
	DEC	B
	JP	NZ,HL2DE
	RET
;
;   COMPUTE (HL)=(TBUFF)+(A)+(C) AND GET THE BYTE THAT'S HERE.
;
EXTRACT:LD	HL,TBUFF
	ADD	A,C
	CALL	ADDHL
	LD	A,(HL)
	RET
;
;  CHECK DRIVE SPECIFIED. IF IT MEANS A CHANGE, THEN THE NEW
; DRIVE WILL BE SELECTED. IN ANY CASE, THE DRIVE BYTE OF THE
; FCB WILL BE SET TO NULL (MEANS USE CURRENT DRIVE).
;
DSELECT:XOR	A		; NULL OUT FIRST BYTE OF FCB.
	LD	(FCB),A
	LD	A,(CHGDRV)	; A DRIVE CHANGE INDICATED?
	OR	A
	RET	Z
	DEC	A		; YES, IS IT THE SAME AS THE CURRENT DRIVE?
	LD	HL,CDRIVE
	CP	(HL)
	RET	Z
	JP	DSKSEL		; NO. SELECT IT THEN.
;
;   CHECK THE DRIVE SELECTION AND RESET IT TO THE PREVIOUS
; DRIVE IF IT WAS CHANGED FOR THE PRECEEDING COMMAND.
;
RESETDR:LD	A,(CHGDRV)	; DRIVE CHANGE INDICATED?
	OR	A
	RET	Z
	DEC	A		; YES, WAS IT A DIFFERENT DRIVE?
	LD	HL,CDRIVE
	CP	(HL)
	RET	Z
	LD	A,(CDRIVE)	; YES, RE-SELECT OUR OLD DRIVE.
	JP	DSKSEL
;
;**************************************************************
;*
;*           D I R E C T O R Y   C O M M A N D
;*
;**************************************************************
;
DIRECT:	CALL	CONVFST		; CONVERT FILE NAME.
	CALL	DSELECT		; SELECT INDICATED DRIVE.
	LD	HL,FCB+1	; WAS ANY FILE INDICATED?
	LD	A,(HL)
	CP	' '
	JP	NZ,DIRECT2
	LD	B,11		; NO. FILL FIELD WITH '?' - SAME AS *.*.
DIRECT1:LD	(HL),'?'
	INC	HL
	DEC	B
	JP	NZ,DIRECT1
DIRECT2:LD	E,0		; SET INITIAL CURSOR POSITION.
	PUSH	DE
	CALL	SRCHFCB		; GET FIRST FILE NAME.
	CALL	Z,NONE		; NONE FOUND AT ALL?
DIRECT3:JP	Z,DIRECT9	; TERMINATE IF NO MORE NAMES.
	LD	A,(RTNCODE)	; GET FILE'S POSITION IN SEGMENT (0-3).
	RRCA
	RRCA
	RRCA
	AND	60H		; (A)=POSITION*32
	LD	C,A
	LD	A,10
	CALL	EXTRACT		; EXTRACT THE TENTH ENTRY IN FCB.
	RLA			; CHECK SYSTEM FILE STATUS BIT.
	JP	C,DIRECT8	; WE DON'T LIST THEM.
	POP	DE
	LD	A,E		; BUMP NAME COUNT.
	INC	E
	PUSH	DE
	AND	03H		; AT END OF LINE?
	PUSH	AF
	JP	NZ,DIRECT4
	CALL	CRLF		; YES, END THIS LINE AND START ANOTHER.
	PUSH	BC
	CALL	GETDSK		; START LINE WITH ('A:').
	POP	BC
	ADD	A,'A'
	CALL	PRINTB
	LD	A,':'
	CALL	PRINTB
	JP	DIRECT5
DIRECT4:CALL	SPACE		; ADD SEPERATOR BETWEEN FILE NAMES.
	LD	A,':'
	CALL	PRINTB
DIRECT5:CALL	SPACE
	LD	B,1		; 'EXTRACT' EACH FILE NAME CHARACTER AT A TIME.
DIRECT6:LD	A,B
	CALL	EXTRACT
	AND	7FH		; STRIP BIT 7 (STATUS BIT).
	CP	' '		; ARE WE AT THE END OF THE NAME?
	JP	NZ,DRECT65
	POP	AF		; YES, DON'T PRINT SPACES AT THE END OF A LINE.
	PUSH	AF
	CP	3
	JP	NZ,DRECT63
	LD	A,9		; FIRST CHECK FOR NO EXTENSION.
	CALL	EXTRACT
	AND	7FH
	CP	' '
	JP	Z,DIRECT7	; DON'T PRINT SPACES.
DRECT63:LD	A,' '		; ELSE PRINT THEM.
DRECT65:CALL	PRINTB
	INC	B		; BUMP TO NEXT CHARACTER PSOITION.
	LD	A,B
	CP	12		; END OF THE NAME?
	JP	NC,DIRECT7
	CP	9		; NOPE, STARTING EXTENSION?
	JP	NZ,DIRECT6
	CALL	SPACE		; YES, ADD SEPERATING SPACE.
	JP	DIRECT6
DIRECT7:POP	AF		; GET THE NEXT FILE NAME.
DIRECT8:CALL	CHKCON		; FIRST CHECK CONSOLE, QUIT ON ANYTHING.
	JP	NZ,DIRECT9
	CALL	SRCHNXT		; GET NEXT NAME.
	JP	DIRECT3		; AND CONTINUE WITH OUR LIST.
DIRECT9:POP	DE		; RESTORE THE STACK AND RETURN TO COMMAND LEVEL.
	JP	GETBACK
;
;**************************************************************
;*
;*                E R A S E   C O M M A N D
;*
;**************************************************************
;
ERASE:	CALL	CONVFST		; CONVERT FILE NAME.
	CP	11		; WAS '*.*' ENTERED?
	JP	NZ,ERASE1
	LD	BC,YESNO	; YES, ASK FOR CONFIRMATION.
	CALL	PLINE
	CALL	GETINP
	LD	HL,INBUFF+1
	DEC	(HL)		; MUST BE EXACTLY 'Y'.
	JP	NZ,CMMND1
	INC	HL
	LD	A,(HL)
	CP	'Y'
	JP	NZ,CMMND1
	INC	HL
	LD	(INPOINT),HL	; SAVE INPUT LINE POINTER.
ERASE1:	CALL	DSELECT		; SELECT DESIRED DISK.
	LD	DE,FCB
	CALL	DELETE		; DELETE THE FILE.
	INC	A
	CALL	Z,NONE		; NOT THERE?
	JP	GETBACK		; RETURN TO COMMAND LEVEL NOW.
YESNO:	.DB "ALL (Y/N)?",0
;
;**************************************************************
;*
;*            T Y P E   C O M M A N D
;*
;**************************************************************
;
TYPE:	CALL	CONVFST		; CONVERT FILE NAME.
	JP	NZ,SYNERR	; WILD CARDS NOT ALLOWED.
	CALL	DSELECT		; SELECT INDICATED DRIVE.
	CALL	OPENFCB		; OPEN THE FILE.
	JP	Z,TYPE5		; NOT THERE?
	CALL	CRLF		; OK, START A NEW LINE ON THE SCREEN.
	LD	HL,NBYTES	; INITIALIZE BYTE COUNTER.
	LD	(HL),0FFH	; SET TO READ FIRST SECTOR.
TYPE1:	LD	HL,NBYTES
TYPE2:	LD	A,(HL)		; HAVE WE WRITTEN THE ENTIRE SECTOR?
	CP	128
	JP	C,TYPE3
	PUSH	HL		; YES, READ IN THE NEXT ONE.
	CALL	READFCB
	POP	HL
	JP	NZ,TYPE4	; END OR ERROR?
	XOR	A		; OK, CLEAR BYTE COUNTER.
	LD	(HL),A
TYPE3:	INC	(HL)		; COUNT THIS BYTE.
	LD	HL,TBUFF	; AND GET THE (A)TH ONE FROM THE BUFFER (TBUFF).
	CALL	ADDHL
	LD	A,(HL)
	CP	CNTRLZ		; END OF FILE MARK?
	JP	Z,GETBACK
	CALL	PRINT		; NO, PRINT IT.
	CALL	CHKCON		; CHECK CONSOLE, QUIT IF ANYTHING READY.
	JP	NZ,GETBACK
	JP	TYPE1
;
;   GET HERE ON AN END OF FILE OR READ ERROR.
;
TYPE4:	DEC	A		; READ ERROR?
	JP	Z,GETBACK
	CALL	RDERROR		; YES, PRINT MESSAGE.
TYPE5:	CALL	RESETDR		; AND RESET PROPER DRIVE
	JP	SYNERR		; NOW PRINT FILE NAME WITH PROBLEM.
;
;**************************************************************
;*
;*            S A V E   C O M M A N D
;*
;**************************************************************
;
SAVE:	CALL	DECODE		; GET NUMERIC NUMBER THAT FOLLOWS SAVE.
	PUSH	AF		; SAVE NUMBER OF PAGES TO WRITE.
	CALL	CONVFST		; CONVERT FILE NAME.
	JP	NZ,SYNERR	; WILD CARDS NOT ALLOWED.
	CALL	DSELECT		; SELECT SPECIFIED DRIVE.
	LD	DE,FCB		; NOW DELETE THIS FILE.
	PUSH	DE
	CALL	DELETE
	POP	DE
	CALL	CREATE		; AND CREATE IT AGAIN.
	JP	Z,SAVE3		; CAN'T CREATE?
	XOR	A		; CLEAR RECORD NUMBER BYTE.
	LD	(FCB+32),A
	POP	AF		; CONVERT PAGES TO SECTORS.
	LD	L,A
	LD	H,0
	ADD	HL,HL		; (HL)=NUMBER OF SECTORS TO WRITE.
	LD	DE,TBASE	; AND WE START FROM HERE.
SAVE1:	LD	A,H		; DONE YET?
	OR	L
	JP	Z,SAVE2
	DEC	HL		; NOPE, COUNT THIS AND COMPUTE THE START
	PUSH	HL		; OF THE NEXT 128 BYTE SECTOR.
	LD	HL,128
	ADD	HL,DE
	PUSH	HL		; SAVE IT AND SET THE TRANSFER ADDRESS.
	CALL	DMASET
	LD	DE,FCB		; WRITE OUT THIS SECTOR NOW.
	CALL	WRTREC
	POP	DE		; RESET (DE) TO THE START OF THE LAST SECTOR.
	POP	HL		; RESTORE SECTOR COUNT.
	JP	NZ,SAVE3	; WRITE ERROR?
	JP	SAVE1
;
;   GET HERE AFTER WRITING ALL OF THE FILE.
;
SAVE2:	LD	DE,FCB		; NOW CLOSE THE FILE.
	CALL	CLOSE
	INC	A		; DID IT CLOSE OK?
	JP	NZ,SAVE4
;
;   PRINT OUT ERROR MESSAGE (NO SPACE).
;
SAVE3:	LD	BC,NOSPACE
	CALL	PLINE
SAVE4:	CALL	STDDMA		; RESET THE STANDARD DMA ADDRESS.
	JP	GETBACK
NOSPACE:.DB "NO SPACE",0
;
;**************************************************************
;*
;*           R E N A M E   C O M M A N D
;*
;**************************************************************
;
RENAME:	CALL	CONVFST		; CONVERT FIRST FILE NAME.
	JP	NZ,SYNERR	; WILD CARDS NOT ALLOWED.
	LD	A,(CHGDRV)	; REMEMBER ANY CHANGE IN DRIVES SPECIFIED.
	PUSH	AF
	CALL	DSELECT		; AND SELECT THIS DRIVE.
	CALL	SRCHFCB		; IS THIS FILE PRESENT?
	JP	NZ,RENAME6	; YES, PRINT ERROR MESSAGE.
	LD	HL,FCB		; YES, MOVE THIS NAME INTO SECOND SLOT.
	LD	DE,FCB+16
	LD	B,16
	CALL	HL2DE
	LD	HL,(INPOINT)	; GET INPUT POINTER.
	EX	DE,HL
	CALL	NONBLANK	; GET NEXT NON BLANK CHARACTER.
	CP	'='		; ONLY ALLOW AN '=' OR '_' SEPERATOR.
	JP	Z,RENAME1
	CP	'_'
	JP	NZ,RENAME5
RENAME1:EX	DE,HL
	INC	HL		; OK, SKIP SEPERATOR.
	LD	(INPOINT),HL	; SAVE INPUT LINE POINTER.
	CALL	CONVFST		; CONVERT THIS SECOND FILE NAME NOW.
	JP	NZ,RENAME5	; AGAIN, NO WILD CARDS.
	POP	AF		; IF A DRIVE WAS SPECIFIED, THEN IT
	LD	B,A		; MUST BE THE SAME AS BEFORE.
	LD	HL,CHGDRV
	LD	A,(HL)
	OR	A
	JP	Z,RENAME2
	CP	B
	LD	(HL),B
	JP	NZ,RENAME5	; THEY WERE DIFFERENT, ERROR.
RENAME2:LD	(HL),B		; 	RESET AS PER THE FIRST FILE SPECIFICATION.
	XOR	A
	LD	(FCB),A		; CLEAR THE DRIVE BYTE OF THE FCB.
RENAME3:CALL	SRCHFCB		; AND GO LOOK FOR SECOND FILE.
	JP	Z,RENAME4	; DOESN'T EXIST?
	LD	DE,FCB
	CALL	RENAM		; OK, RENAME THE FILE.
	JP	GETBACK
;
;   PROCESS RENAME ERRORS HERE.
;
RENAME4:CALL	NONE		; FILE NOT THERE.
	JP	GETBACK
RENAME5:CALL	RESETDR		; BAD COMMAND FORMAT.
	JP	SYNERR
RENAME6:LD	BC,EXISTS	; DESTINATION FILE ALREADY EXISTS.
	CALL	PLINE
	JP	GETBACK
EXISTS:	.DB "FILE EXISTS",0
;
;**************************************************************
;*
;*             U S E R   C O M M A N D
;*
;**************************************************************
;
USER:	CALL	DECODE		; GET NUMERIC VALUE FOLLOWING COMMAND.
	CP	16		; LEGAL USER NUMBER?
	JP	NC,SYNERR
	LD	E,A		; YES BUT IS THERE ANYTHING ELSE?
	LD	A,(FCB+1)
	CP	' '
	JP	Z,SYNERR	; YES, THAT IS NOT ALLOWED.
	CALL	GETSETUC	; OK, SET USER CODE.
	JP	GETBACK1
;
;**************************************************************
;*
;*        T R A N S I A N T   P R O G R A M   C O M M A N D
;*
;**************************************************************
;
UNKNOWN:CALL	VERIFY		; CHECK FOR VALID SYSTEM (WHY?).
	LD	A,(FCB+1)	; ANYTHING TO EXECUTE?
	CP	' '
	JP	NZ,UNKWN1
	LD	A,(CHGDRV)	; NOPE, ONLY A DRIVE CHANGE?
	OR	A
	JP	Z,GETBACK1	; NEITHER???
	DEC	A
	LD	(CDRIVE),A	; OK, STORE NEW DRIVE.
	CALL	MOVECD		; SET (TDRIVE) ALSO.
	CALL	DSKSEL		; AND SELECT THIS DRIVE.
	JP	GETBACK1	; THEN RETURN.
;
;   HERE A FILE NAME WAS TYPED. PREPARE TO EXECUTE IT.
;
UNKWN1:	LD	DE,FCB+9	; AN EXTENSION SPECIFIED?
	LD	A,(DE)
	CP	' '
	JP	NZ,SYNERR	; YES, NOT ALLOWED.
UNKWN2:	PUSH	DE
	CALL	DSELECT		; SELECT SPECIFIED DRIVE.
	POP	DE
	LD	HL,COMFILE	; SET THE EXTENSION TO 'COM'.
	CALL	MOVE3
	CALL	OPENFCB		; AND OPEN THIS FILE.
	JP	Z,UNKWN9	; NOT PRESENT?
;
;   LOAD IN THE PROGRAM.
;
	LD	HL,TBASE	; STORE THE PROGRAM STARTING HERE.
UNKWN3:	PUSH	HL
	EX	DE,HL
	CALL	DMASET		; SET TRANSFER ADDRESS.
	LD	DE,FCB		; AND READ THE NEXT RECORD.
	CALL	RDREC
	JP	NZ,UNKWN4	; END OF FILE OR READ ERROR?
	POP	HL		; NOPE, BUMP POINTER FOR NEXT SECTOR.
	LD	DE,128
	ADD	HL,DE
	LD	DE,CBASE	; ENOUGH ROOM FOR THE WHOLE FILE?
	LD	A,L
	SUB	E
	LD	A,H
	SBC	A,D
	JP	NC,UNKWN0	; NO, IT CAN'T FIT.
	JP	UNKWN3
;
;   GET HERE AFTER FINISHED READING.
;
UNKWN4:	POP	HL
	DEC	A		; NORMAL END OF FILE?
	JP	NZ,UNKWN0
	CALL	RESETDR		; YES, RESET PREVIOUS DRIVE.
	CALL	CONVFST		; CONVERT THE FIRST FILE NAME THAT FOLLOWS
	LD	HL,CHGDRV	; COMMAND NAME.
	PUSH	HL
	LD	A,(HL)		; SET DRIVE CODE IN DEFAULT FCB.
	LD	(FCB),A
	LD	A,16		; PUT SECOND NAME 16 BYTES LATER.
	CALL	CONVERT		; CONVERT SECOND FILE NAME.
	POP	HL
	LD	A,(HL)		; AND SET THE DRIVE FOR THIS SECOND FILE.
	LD	(FCB+16),A
	XOR	A		; CLEAR RECORD BYTE IN FCB.
	LD	(FCB+32),A
	LD	DE,TFCB		; MOVE IT INTO PLACE AT(005CH).
	LD	HL,FCB
	LD	B,33
	CALL	HL2DE
	LD	HL,INBUFF+2	; NOW MOVE THE REMAINDER OF THE INPUT
UNKWN5:	LD	A,(HL)		; LINE DOWN TO (0080H). LOOK FOR A NON BLANK.
	OR	A		; OR A NULL.
	JP	Z,UNKWN6
	CP	' '
	JP	Z,UNKWN6
	INC	HL
	JP	UNKWN5
;
;   DO THE LINE MOVE NOW. IT ENDS IN A NULL BYTE.
;
UNKWN6:	LD	B,0		; KEEP A CHARACTER COUNT.
	LD	DE,TBUFF+1	; DATA GETS PUT HERE.
UNKWN7:	LD	A,(HL)		; MOVE IT NOW.
	LD	(DE),A
	OR	A
	JP	Z,UNKWN8
	INC	B
	INC	HL
	INC	DE
	JP	UNKWN7
UNKWN8:	LD	A,B		; NOW STORE THE CHARACTER COUNT.
	LD	(TBUFF),A
	CALL	CRLF		; CLEAN UP THE SCREEN.
	CALL	STDDMA		; SET STANDARD TRANSFER ADDRESS.
	CALL	SETCDRV		; RESET CURRENT DRIVE.
	CALL	TBASE		; AND EXECUTE THE PROGRAM.
;
;   TRANSIANT PROGRAMS RETURN HERE (OR REBOOT).
;
	LD	SP,BATCH	; SET STACK FIRST OFF.
	CALL	MOVECD		; MOVE CURRENT DRIVE INTO PLACE (TDRIVE).
	CALL	DSKSEL		; AND RESELECT IT.
	JP	CMMND1		; BACK TO COMAND MODE.
;
;   GET HERE IF SOME ERROR OCCURED.
;
UNKWN9:	CALL	RESETDR		; INPROPER FORMAT.
	JP	SYNERR
UNKWN0:	LD	BC,BADLOAD	; READ ERROR OR WON'T FIT.
	CALL	PLINE
	JP	GETBACK
BADLOAD:.DB "BAD LOAD",0
COMFILE:.DB "COM"		; COMMAND FILE EXTENSION.
;
;   GET HERE TO RETURN TO COMMAND LEVEL. WE WILL RESET THE
; PREVIOUS ACTIVE DRIVE AND THEN EITHER RETURN TO COMMAND
; LEVEL DIRECTLY OR PRINT ERROR MESSAGE AND THEN RETURN.
;
GETBACK:CALL	RESETDR		; RESET PREVIOUS DRIVE.
GETBACK1:
	CALL	CONVFST		; CONVERT FIRST NAME IN (FCB).
	LD	A,(FCB+1)	; IF THIS WAS JUST A DRIVE CHANGE REQUEST,
	SUB	' '		; MAKE SURE IT WAS VALID.
	LD	HL,CHGDRV
	OR	(HL)
	JP	NZ,SYNERR
	JP	CMMND1		; OK, RETURN TO COMMAND LEVEL.
;
;   CCP STACK AREA.
;
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
CCPSTACK:	.EQU $			; END OF CCP STACK AREA.
;
;   BATCH (OR SUBMIT) PROCESSING INFORMATION STORAGE.
;
BATCH:	.DB 0			; BATCH MODE FLAG (0=NOT ACTIVE).
; BATCHFCB:DB	0,'$$$     SUB',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
BATCHFCB:
	.DB 0,"$$$     SUB"
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;
;   FILE CONTROL BLOCK SETUP BY THE CCP.
;
;FCB	DB	0,'           ',0,0,0,0,0,'           ',0,0,0,0,0
FCB:	.DB 0,"           ",0,0,0,0,0
	.DB "           ",0,0,0,0,0
RTNCODE:.DB 0			; STATUS RETURNED FROM BDOS CALL.
CDRIVE:	.DB 0			; CURRENTLY ACTIVE DRIVE.
CHGDRV:	.DB 0			; CHANGE IN DRIVES FLAG (0=NO CHANGE).
NBYTES:	.DW 0			; BYTE COUNTER USED BY TYPE.
;
;   ROOM FOR EXPANSION?
;
	.DB 0,0,0,0,0,0,0,0,0
;
;   NOTE THAT THE FOLLOWING SIX BYTES MUST MATCH THOSE AT
; (PATTRN1) OR CP/M WILL HALT. WHY?
;
PATTRN2:.DB 0,22,0,0,0,0	; (* SERIAL NUMBER BYTES *).
;
;**************************************************************
;*
;*                    B D O S   E N T R Y
;*
;**************************************************************
;
FBASE:	JP	FBASE1
;
;   BDOS ERROR TABLE.
;
BADSCTR:.DW ERROR1		; BAD SECTOR ON READ OR WRITE.
BADSLCT:.DW ERROR2		; BAD DISK SELECT.
RODISK:	.DW ERROR3		; DISK IS READ ONLY.
ROFILE:	.DW ERROR4		; FILE IS READ ONLY.
;
;   ENTRY INTO BDOS. (DE) OR (E) ARE THE PARAMETERS PASSED. THE
; FUNCTION NUMBER DESIRED IS IN REGISTER (C).
;
FBASE1:	EX	DE,HL		; SAVE THE (DE) PARAMETERS.
	LD	(PARAMS),HL
	EX	DE,HL
	LD	A,E		; AND SAVE REGISTER (E) IN PARTICULAR.
	LD	(EPARAM),A
	LD	HL,0
	LD	(STATUS),HL	; CLEAR RETURN STATUS.
	ADD	HL,SP
	LD	(USRSTACK),HL	; SAVE USERS STACK POINTER.
	LD	SP,STKAREA	; AND SET OUR OWN.
	XOR	A		; CLEAR AUTO SELECT STORAGE SPACE.
	LD	(AUTOFLAG),A
	LD	(AUTO),A
	LD	HL,GOBACK	; SET RETURN ADDRESS.
	PUSH	HL
	LD	A,C		; GET FUNCTION NUMBER.
	CP	NFUNCTS		; VALID FUNCTION NUMBER?
	RET	NC
	LD	C,E		; KEEP SINGLE REGISTER FUNCTION HERE.
	LD	HL,FUNCTNS	; NOW LOOK THRU THE FUNCTION TABLE.
	LD	E,A
	LD	D,0		; (DE)=FUNCTION NUMBER.
	ADD	HL,DE
	ADD	HL,DE		; (HL)=(START OF TABLE)+2*(FUNCTION NUMBER).
	LD	E,(HL)
	INC	HL
	LD	D,(HL)		; NOW (DE)=ADDRESS FOR THIS FUNCTION.
	LD	HL,(PARAMS)	; RETRIEVE PARAMETERS.
	EX	DE,HL		; NOW (DE) HAS THE ORIGINAL PARAMETERS.
	JP	(HL)		; EXECUTE DESIRED FUNCTION.
;
;   BDOS FUNCTION JUMP TABLE.
;
NFUNCTS:.EQU 41			; NUMBER OF FUNCTIONS IN FOLLOWIN TABLE.
;
FUNCTNS:.DW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB
	.DW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL
	.DW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE
	.DW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR
	.DW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN
	.DW RTN,WTSPECL
;
;   BDOS ERROR MESSAGE SECTION.
;
ERROR1:	LD	HL,BADSEC	; BAD SECTOR MESSAGE.
	CALL	PRTERR		; PRINT IT AND GET A 1 CHAR RESPONCE.
	CP	CNTRLC		; RE-BOOT REQUEST (CONTROL-C)?
	JP	Z,0		; YES.
	RET			; NO, RETURN TO RETRY I/O FUNCTION.
;
ERROR2:	LD	HL,BADSEL	; BAD DRIVE SELECTED.
	JP	ERROR5
;
ERROR3:	LD	HL,DISKRO	; DISK IS READ ONLY.
	JP	ERROR5
;
ERROR4:	LD	HL,FILERO	; FILE IS READ ONLY.
;
ERROR5:	CALL	PRTERR
	JP	0		; ALWAYS REBOOT ON THESE ERRORS.
;
BDOSERR:.DB "BDOS ERR ON "
BDOSDRV:.DB " : $"
BADSEC:	.DB "BAD SECTOR$"
BADSEL:	.DB "SELECT$"		; reboot if a bad drive select
FILERO:	.DB "FILE "
DISKRO:	.DB "R/O$"
;
;   PRINT BDOS ERROR MESSAGE.
;
PRTERR:	PUSH	HL		; SAVE SECOND MESSAGE POINTER.
	CALL	OUTCRLF		; SEND (CR)(LF).
	LD	A,(ACTIVE)	; GET ACTIVE DRIVE.
	ADD	A,'A'		; MAKE ASCII.
	LD	(BDOSDRV),A	; AND PUT IN MESSAGE.
	LD	BC,BDOSERR	; AND PRINT IT.
	CALL	PRTMESG
	POP	BC		; PRINT SECOND MESSAGE LINE NOW.
	CALL	PRTMESG

;
;   GET AN INPUT CHARACTER. WE WILL CHECK OUR 1 CHARACTER
; BUFFER FIRST. THIS MAY BE SET BY THE CONSOLE STATUS ROUTINE.
;
GETCHAR:LD	HL,CHARBUF	; CHECK CHARACTER BUFFER.
	LD	A,(HL)		; ANYTHING PRESENT ALREADY?
	LD	(HL),0		; ...EITHER CASE CLEAR IT.
	OR	A
	RET	NZ		; YES, USE IT.
	JP	CONIN		; NOPE, GO GET A CHARACTER RESPONCE.
;
;   INPUT AND ECHO A CHARACTER.
;
GETECHO:CALL	GETCHAR		; INPUT A CHARACTER.
	CALL	CHKCHAR		; CARRIAGE CONTROL?
	RET	C		; NO, A REGULAR CONTROL CHAR SO DON'T ECHO.
	PUSH	AF		; OK, SAVE CHARACTER NOW.
	LD	C,A
	CALL	OUTCON		; AND ECHO IT.
	POP	AF		; GET CHARACTER AND RETURN.
	RET
;
;   CHECK CHARACTER IN (A). SET THE ZERO FLAG ON A CARRIAGE
; CONTROL CHARACTER AND THE CARRY FLAG ON ANY OTHER CONTROL
; CHARACTER.
;
CHKCHAR:CP	CR		; CHECK FOR CARRIAGE RETURN, LINE FEED, BACKSPACE,
	RET	Z		; OR A TAB.
	CP	LF
	RET	Z
	CP	TAB
	RET	Z
	CP	BS
	RET	Z
	CP	' '		; OTHER CONTROL CHAR? SET CARRY FLAG.
	RET
;
;   CHECK THE CONSOLE DURING OUTPUT. HALT ON A CONTROL-S, THEN
; REBOOT ON A CONTROL-C. IF ANYTHING ELSE IS READY, CLEAR THE
; ZERO FLAG AND RETURN (THE CALLING ROUTINE MAY WANT TO DO
; SOMETHING).
;
CKCONSOL:
	LD	A,(CHARBUF)	; CHECK BUFFER.
	OR	A		; IF ANYTHING, JUST RETURN WITHOUT CHECKING.
	JP	NZ,CKCON2
	CALL	CONST		; NOTHING IN BUFFER. CHECK CONSOLE.
	AND	01H		; LOOK AT BIT 0.
	RET	Z		; RETURN IF NOTHING.
	CALL	CONIN		; OK, GET IT.
	CP	CNTRLS		; IF NOT CONTROL-S, RETURN WITH ZERO CLEARED.
	JP	NZ,CKCON1
	CALL	CONIN		; HALT PROCESSING UNTIL ANOTHER CHAR
	CP	CNTRLC		; IS TYPED. CONTROL-C?
	JP	Z,0		; YES, REBOOT NOW.
	XOR	A		; NO, JUST PRETEND NOTHING WAS EVER READY.
	RET
CKCON1:	LD	(CHARBUF),A	; SAVE CHARACTER IN BUFFER FOR LATER PROCESSING.
CKCON2:	LD	A,1		; SET (A) TO NON ZERO TO MEAN SOMETHING IS READY.
	RET
;
;   OUTPUT (C) TO THE SCREEN. IF THE PRINTER FLIP-FLOP FLAG
; IS SET, WE WILL SEND CHARACTER TO PRINTER ALSO. THE CONSOLE
; WILL BE CHECKED IN THE PROCESS.
;
OUTCHAR:LD	A,(OUTFLAG)	; CHECK OUTPUT FLAG.
	OR	A		; ANYTHING AND WE WON'T GENERATE OUTPUT.
	JP	NZ,OUTCHR1
	PUSH	BC
	CALL	CKCONSOL	; CHECK CONSOLE (WE DON'T CARE WHATS THERE).
	POP	BC
	PUSH	BC
	CALL	CONOUT		; OUTPUT (C) TO THE SCREEN.
	POP	BC
	PUSH	BC
	LD	A,(PRTFLAG)	; CHECK PRINTER FLIP-FLOP FLAG.
	OR	A
	CALL	NZ,LIST		; PRINT IT ALSO IF NON-ZERO.
	POP	BC
OUTCHR1:LD	A,C		; UPDATE CURSORS POSITION.
	LD	HL,CURPOS
	CP	DEL		; RUBOUTS DON'T DO ANYTHING HERE.
	RET	Z
	INC	(HL)		; BUMP LINE POINTER.
	CP	' '		; AND RETURN IF A NORMAL CHARACTER.
	RET	NC
	DEC	(HL)		; RESTORE AND CHECK FOR THE START OF THE LINE.
	LD	A,(HL)
	OR	A
	RET	Z		; INGNORE CONTROL CHARACTERS AT THE START OF THE LINE.
	LD	A,C
	CP	BS		; IS IT A BACKSPACE?
	JP	NZ,OUTCHR2
	DEC	(HL)		; YES, BACKUP POINTER.
	RET
OUTCHR2:CP	LF		; IS IT A LINE FEED?
	RET	NZ		; IGNORE ANYTHING ELSE.
	LD	(HL),0		; RESET POINTER TO START OF LINE.
	RET
;
;   OUTPUT (A) TO THE SCREEN. IF IT IS A CONTROL CHARACTER
; (OTHER THAN CARRIAGE CONTROL), USE ^X FORMAT.
;
SHOWIT:	LD	A,C
	CALL	CHKCHAR		; CHECK CHARACTER.
	JP	NC,OUTCON	; NOT A CONTROL, USE NORMAL OUTPUT.
	PUSH	AF
	LD	C,'^'		; FOR A CONTROL CHARACTER, PRECEED IT WITH '^'.
	CALL	OUTCHAR
	POP	AF
	OR	'@'		; AND THEN USE THE LETTER EQUIVELANT.
	LD	C,A
;
;   FUNCTION TO OUTPUT (C) TO THE CONSOLE DEVICE AND EXPAND TABS
; IF NECESSARY.
;
OUTCON:	LD	A,C
	CP	TAB		; IS IT A TAB?
	JP	NZ,OUTCHAR	; USE REGULAR OUTPUT.
OUTCON1:LD	C,' '		; YES IT IS, USE SPACES INSTEAD.
	CALL	OUTCHAR
	LD	A,(CURPOS)	; GO UNTIL THE CURSOR IS AT A MULTIPLE OF 8

	AND	07H		; POSITION.
	JP	NZ,OUTCON1
	RET
;
;   ECHO A BACKSPACE CHARACTER. ERASE THE PREVOIUS CHARACTER
; ON THE SCREEN.
;
BACKUP:	CALL	BACKUP1		; BACKUP THE SCREEN 1 PLACE.
	LD	C,' '		; THEN BLANK THAT CHARACTER.
	CALL	CONOUT
BACKUP1:LD	C,BS		; THEN BACK SPACE ONCE MORE.
	JP	CONOUT
;
;   SIGNAL A DELETED LINE. PRINT A '#' AT THE END AND START
; OVER.
;
NEWLINE:LD	C,'#'
	CALL	OUTCHAR		; PRINT THIS.
	CALL	OUTCRLF		; START NEW LINE.
NEWLN1:	LD	A,(CURPOS)	; MOVE THE CURSOR TO THE STARTING POSITION.
	LD	HL,STARTING
	CP	(HL)
	RET	NC		; THERE YET?
	LD	C,' '
	CALL	OUTCHAR		; NOPE, KEEP GOING.
	JP	NEWLN1
;
;   OUTPUT A (CR) (LF) TO THE CONSOLE DEVICE (SCREEN).
;
OUTCRLF:LD	C,CR
	CALL	OUTCHAR
	LD	C,LF
	JP	OUTCHAR
;
;   PRINT MESSAGE POINTED TO BY (BC). IT WILL END WITH A '$'.
;
PRTMESG:LD	A,(BC)		; CHECK FOR TERMINATING CHARACTER.
	CP	'$'
	RET	Z
	INC	BC
	PUSH	BC		; OTHERWISE, BUMP POINTER AND PRINT IT.
	LD	C,A
	CALL	OUTCON
	POP	BC
	JP	PRTMESG
;
;   FUNCTION TO EXECUTE A BUFFERED READ.
;
RDBUFF:	LD	A,(CURPOS)	; USE PRESENT LOCATION AS STARTING ONE.
	LD	(STARTING),A
	LD	HL,(PARAMS)	; GET THE MAXIMUM BUFFER SPACE.
	LD	C,(HL)
	INC	HL		; POINT TO FIRST AVAILABLE SPACE.
	PUSH	HL		; AND SAVE.
	LD	B,0		; KEEP A CHARACTER COUNT.
RDBUF1:	PUSH	BC
	PUSH	HL
RDBUF2:	CALL	GETCHAR		; GET THE NEXT INPUT CHARACTER.
	AND	7FH		; STRIP BIT 7.
	POP	HL		; RESET REGISTERS.
	POP	BC
	CP	CR		; EN OF THE LINE?
	JP	Z,RDBUF17
	CP	LF
	JP	Z,RDBUF17
	CP	BS		; HOW ABOUT A BACKSPACE?
	JP	NZ,RDBUF3
	LD	A,B		; YES, BUT IGNORE AT THE BEGINNING OF THE LINE.
	OR	A
	JP	Z,RDBUF1
	DEC	B		; OK, UPDATE COUNTER.
	LD	A,(CURPOS)	; IF WE BACKSPACE TO THE START OF THE LINE,
	LD	(OUTFLAG),A	; TREAT AS A CANCEL (CONTROL-X).
	JP	RDBUF10
RDBUF3:	CP	DEL		; USER TYPED A RUBOUT?
	JP	NZ,RDBUF4
	LD	A,B		; IGNORE AT THE START OF THE LINE.
	OR	A
	JP	Z,RDBUF1
	LD	A,(HL)		; OK, ECHO THE PREVOIUS CHARACTER.
	DEC	B		; AND RESET POINTERS (COUNTERS).
	DEC	HL
	JP	RDBUF15
RDBUF4:	CP	CNTRLE		; PHYSICAL END OF LINE?
	JP	NZ,RDBUF5
	PUSH	BC		; YES, DO IT.
	PUSH	HL
	CALL	OUTCRLF
	XOR	A		; AND UPDATE STARTING POSITION.
	LD	(STARTING),A
	JP	RDBUF2
RDBUF5:	CP	CNTRLP		; CONTROL-P?
	JP	NZ,RDBUF6
	PUSH	HL		; YES, FLIP THE PRINT FLAG FILP-FLOP BYTE.
	LD	HL,PRTFLAG
	LD	A,1		; PRTFLAG=1-PRTFLAG
	SUB	(HL)
	LD	(HL),A
	POP	HL
	JP	RDBUF1
RDBUF6:	CP	CNTRLX		; CONTROL-X (CANCEL)?
	JP	NZ,RDBUF8
	POP	HL
RDBUF7:	LD	A,(STARTING)	; YES, BACKUP THE CURSOR TO HERE.
	LD	HL,CURPOS
	CP	(HL)
	JP	NC,RDBUFF	; DONE YET?
	DEC	(HL)		; NO, DECREMENT POINTER AND OUTPUT BACK UP ONE SPACE.
	CALL	BACKUP
	JP	RDBUF7
RDBUF8:	CP	CNTRLU		; CNTROL-U (CANCEL LINE)?
	JP	NZ,RDBUF9
	CALL	NEWLINE		; START A NEW LINE.
	POP	HL
	JP	RDBUFF
RDBUF9:	CP	CNTRLR		; CONTROL-R?
	JP	NZ,RDBUF14
RDBUF10:PUSH	BC		; YES, START A NEW LINE AND RETYPE THE OLD ONE.
	CALL	NEWLINE
	POP	BC
	POP	HL
	PUSH	HL
	PUSH	BC
RDBUF11:LD	A,B		; DONE WHOLE LINE YET?
	OR	A
	JP	Z,RDBUF12
	INC	HL		; NOPE, GET NEXT CHARACTER.
	LD	C,(HL)
	DEC	B		; COUNT IT.
	PUSH	BC
	PUSH	HL
	CALL	SHOWIT		; AND DISPLAY IT.
	POP	HL
	POP	BC
	JP	RDBUF11
RDBUF12:PUSH	HL		; DONE WITH LINE. IF WE WERE DISPLAYING
	LD	A,(OUTFLAG)	; THEN UPDATE CURSOR POSITION.
	OR	A
	JP	Z,RDBUF2
	LD	HL,CURPOS	; BECAUSE THIS LINE IS SHORTER, WE MUST
	SUB	(HL)		; BACK UP THE CURSOR (NOT THE SCREEN HOWEVER)
	LD	(OUTFLAG),A	; SOME NUMBER OF POSITIONS.
RDBUF13:CALL	BACKUP		; NOTE THAT AS LONG AS (OUTFLAG) IS NON
	LD	HL,OUTFLAG	; ZERO, THE SCREEN WILL NOT BE CHANGED.
	DEC	(HL)
	JP	NZ,RDBUF13
	JP	RDBUF2		; NOW JUST GET THE NEXT CHARACTER.
;
;   JUST A NORMAL CHARACTER, PUT THIS IN OUR BUFFER AND ECHO.
;
RDBUF14:INC	HL
	LD	(HL),A		; STORE CHARACTER.
	INC	B		; AND COUNT IT.
RDBUF15:PUSH	BC
	PUSH	HL
	LD	C,A		; ECHO IT NOW.
	CALL	SHOWIT
	POP	HL
	POP	BC
	LD	A,(HL)		; WAS IT AN ABORT REQUEST?
	CP	CNTRLC		; CONTROL-C ABORT?
	LD	A,B
	JP	NZ,RDBUF16
	CP	1		; ONLY IF AT START OF LINE.
	JP	Z,0
RDBUF16:CP	C		; NOPE, HAVE WE FILLED THE BUFFER?
	JP	C,RDBUF1
RDBUF17:POP	HL		; YES END THE LINE AND RETURN.
	LD	(HL),B
	LD	C,CR
	JP	OUTCHAR		; OUTPUT (CR) AND RETURN.
;
;   FUNCTION TO GET A CHARACTER FROM THE CONSOLE DEVICE.
;
GETCON:	CALL	GETECHO		; GET AND ECHO.
	JP	SETSTAT		; SAVE STATUS AND RETURN.
;
;   FUNCTION TO GET A CHARACTER FROM THE TAPE READER DEVICE.
;
GETRDR:	CALL	READER		; GET A CHARACTER FROM READER, SET STATUS AND RETURN.
	JP	SETSTAT
;
;  FUNCTION TO PERFORM DIRECT CONSOLE I/O. IF (C) CONTAINS (FF)
; THEN THIS IS AN INPUT REQUEST. IF (C) CONTAINS (FE) THEN
; THIS IS A STATUS REQUEST. OTHERWISE WE ARE TO OUTPUT (C).
;
DIRCIO:	LD	A,C		; TEST FOR (FF).
	INC	A
	JP	Z,DIRC1
	INC	A		; TEST FOR (FE).
	JP	Z,CONST
	JP	CONOUT		; JUST OUTPUT (C).
DIRC1:	CALL	CONST		; THIS IS AN INPUT REQUEST.
	OR	A
	JP	Z,GOBACK1	; NOT READY? JUST RETURN (DIRECTLY).
	CALL	CONIN		; YES, GET CHARACTER.
	JP	SETSTAT		; SET STATUS AND RETURN.
;
;   FUNCTION TO RETURN THE I/O BYTE.
;
GETIOB:	LD	A,(IOBYTE)
	JP	SETSTAT
;
;   FUNCTION TO SET THE I/O BYTE.
;
SETIOB:	LD	HL,IOBYTE
	LD	(HL),C
	RET
;
;   FUNCTION TO PRINT THE CHARACTER STRING POINTED TO BY (DE)
; ON THE CONSOLE DEVICE. THE STRING ENDS WITH A '$'.
;
PRTSTR:	EX	DE,HL
	LD	C,L
	LD	B,H		; NOW (BC) POINTS TO IT.
	JP	PRTMESG
;
;   FUNCTION TO INTERIGATE THE CONSOLE DEVICE.
;
GETCSTS:CALL	CKCONSOL
;
;   GET HERE TO SET THE STATUS AND RETURN TO THE CLEANUP
; SECTION. THEN BACK TO THE USER.
;
SETSTAT:LD	(STATUS),A
RTN:	RET
;
;   SET THE STATUS TO 1 (READ OR WRITE ERROR CODE).
;
IOERR1:	LD	A,1
	JP	SETSTAT
;
OUTFLAG:.DB 0			; OUTPUT FLAG (NON ZERO MEANS NO OUTPUT).
STARTING:
	.DB 2			; STARTING POSITION FOR CURSOR.
CURPOS:	.DB 0			; CURSOR POSITION (0=START OF LINE).
PRTFLAG:.DB 0			; PRINTER FLAG (CONTROL-P TOGGLE). LIST IF NON ZERO.
CHARBUF:.DB 0			; SINGLE INPUT CHARACTER BUFFER.
;
;   STACK AREA FOR BDOS CALLS.
;
USRSTACK:
	.DW 0			; SAVE USERS STACK POINTER HERE.
;
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
STKAREA:.EQU $			; END OF STACK AREA.
;
USERNO:	.DB 0			; CURRENT USER NUMBER.
ACTIVE:	.DB 0			; CURRENTLY ACTIVE DRIVE.
PARAMS:	.DW 0			; SAVE (DE) PARAMETERS HERE ON ENTRY.
STATUS:	.DW 0			; STATUS RETURNED FROM BDOS FUNCTION.
;
;   SELECT ERROR OCCURED, JUMP TO ERROR ROUTINE.
;
SLCTERR:LD	HL,BADSLCT
;
;   JUMP TO (HL) INDIRECTLY.
;
JUMPHL:	LD	E,(HL)
	INC	HL
	LD	D,(HL)		; NOW (DE) CONTAIN THE DESIRED ADDRESS.
	EX	DE,HL
	JP	(HL)
;
;   BLOCK MOVE. (DE) TO (HL), (C) BYTES TOTAL.
;
DE2HL:	INC	C		; IS COUNT DOWN TO ZERO?
DE2HL1:	DEC	C
	RET	Z		; YES, WE ARE DONE.
	LD	A,(DE)		; NO, MOVE ONE MORE BYTE.
	LD	(HL),A
	INC	DE
	INC	HL
	JP	DE2HL1		; AND REPEAT.
;
;   SELECT THE DESIRED DRIVE.
;
SELECT:	LD	A,(ACTIVE)	; GET ACTIVE DISK.
	LD	C,A
	CALL	SELDSK		; SELECT IT.
	LD	A,H		; VALID DRIVE?
	OR	L		; VALID DRIVE?
	RET	Z		; RETURN IF NOT.
;
;   HERE, THE BIOS RETURNED THE ADDRESS OF THE PARAMETER BLOCK
; IN (HL). WE WILL EXTRACT THE NECESSARY POINTERS AND SAVE THEM.
;
	LD	E,(HL)		; YES, GET ADDRESS OF TRANSLATION TABLE INTO (DE).
	INC	HL
	LD	D,(HL)
	INC	HL
	LD	(SCRATCH1),HL	; SAVE POINTERS TO SCRATCH AREAS.
	INC	HL
	INC	HL
	LD	(SCRATCH2),HL	; DITTO.
	INC	HL
	INC	HL
	LD	(SCRATCH3),HL	; DITTO.
	INC	HL
	INC	HL
	EX	DE,HL		; NOW SAVE THE TRANSLATION TABLE ADDRESS.
	LD	(XLATE),HL
	LD	HL,DIRBUF	; PUT THE NEXT 8 BYTES HERE.
	LD	C,8		; THEY CONSIST OF THE DIRECTORY BUFFER
	CALL	DE2HL		; POINTER, PARAMETER BLOCK POINTER,
	LD	HL,(DISKPB)	; CHECK AND ALLOCATION VECTORS.
	EX	DE,HL
	LD	HL,SECTORS	; MOVE PARAMETER BLOCK INTO OUR RAM.
	LD	C,15		; IT IS 15 BYTES LONG.
	CALL	DE2HL
	LD	HL,(DSKSIZE)	; CHECK DISK SIZE.
	LD	A,H		; MORE THAN 256 BLOCKS ON THIS?
	LD	HL,BIGDISK
	LD	(HL),0FFH	; SET TO SAMLL.
	OR	A
	JP	Z,SELECT1
	LD	(HL),0		; WRONG, SET TO LARGE.
SELECT1:LD	A,0FFH		; CLEAR THE ZERO FLAG.
	OR	A
	RET
;
;   ROUTINE TO HOME THE DISK TRACK HEAD AND CLEAR POINTERS.
;
HOMEDRV:CALL	HOME		; HOME THE HEAD.
	XOR	A
	LD	HL,(SCRATCH2)	; SET OUR TRACK POINTER ALSO.
	LD	(HL),A
	INC	HL
	LD	(HL),A
	LD	HL,(SCRATCH3)	; AND OUR SECTOR POINTER.
	LD	(HL),A
	INC	HL
	LD	(HL),A
	RET
;
;   DO THE ACTUAL DISK READ AND CHECK THE ERROR RETURN STATUS.
;
DOREAD:	CALL	READ
	JP	IORET
;
;   DO THE ACTUAL DISK WRITE AND HANDLE ANY BIOS ERROR.
;
DOWRITE:CALL	WRITE
IORET:	OR	A
	RET	Z		; RETURN UNLESS AN ERROR OCCURED.
	LD	HL,BADSCTR	; BAD READ/WRITE ON THIS SECTOR.
	JP	JUMPHL
;
;   ROUTINE TO SELECT THE TRACK AND SECTOR THAT THE DESIRED
; BLOCK NUMBER FALLS IN.
;
TRKSEC:	LD	HL,(FILEPOS)	; GET POSITION OF LAST ACCESSED FILE
	LD	C,2		; IN DIRECTORY AND COMPUTE SECTOR #.
	CALL	SHIFTR		; SECTOR #=FILE-POSITION/4.
	LD	(BLKNMBR),HL	; SAVE THIS AS THE BLOCK NUMBER OF INTEREST.
	LD	(CKSUMTBL),HL	; WHAT'S IT DOING HERE TOO?
;
;   IF THE SECTOR NUMBER HAS ALREADY BEEN SET (BLKNMBR), ENTER
; AT THIS POINT.
;
TRKSEC1:LD	HL,BLKNMBR
	LD	C,(HL)		; MOVE SECTOR NUMBER INTO (BC).
	INC	HL
	LD	B,(HL)
	LD	HL,(SCRATCH3)	; GET CURRENT SECTOR NUMBER AND
	LD	E,(HL)		; MOVE THIS INTO (DE).
	INC	HL
	LD	D,(HL)
	LD	HL,(SCRATCH2)	; GET CURRENT TRACK NUMBER.
	LD	A,(HL)		; AND THIS INTO (HL).
	INC	HL
	LD	H,(HL)
	LD	L,A
TRKSEC2:LD	A,C		; IS DESIRED SECTOR BEFORE CURRENT ONE?
	SUB	E
	LD	A,B
	SBC	A,D
	JP	NC,TRKSEC3
	PUSH	HL		; YES, DECREMENT SECTORS BY ONE TRACK.
	LD	HL,(SECTORS)	; GET SECTORS PER TRACK.
	LD	A,E
	SUB	L
	LD	E,A
	LD	A,D
	SBC	A,H
	LD	D,A		; NOW WE HAVE BACKED UP ONE FULL TRACK.
	POP	HL
	DEC	HL		; ADJUST TRACK COUNTER.
	JP	TRKSEC2
TRKSEC3:PUSH	HL		; DESIRED SECTOR IS AFTER CURRENT ONE.
	LD	HL,(SECTORS)	; GET SECTORS PER TRACK.
	ADD	HL,DE		; BUMP SECTOR POINTER TO NEXT TRACK.
	JP	C,TRKSEC4
	LD	A,C		; IS DESIRED SECTOR NOW BEFORE CURRENT ONE?
	SUB	L
	LD	A,B
	SBC	A,H
	JP	C,TRKSEC4
	EX	DE,HL		; NOT YES, INCREMENT TRACK COUNTER
	POP	HL		; AND CONTINUE UNTIL IT IS.
	INC	HL
	JP	TRKSEC3
;
;   HERE WE HAVE DETERMINED THE TRACK NUMBER THAT CONTAINS THE
; DESIRED SECTOR.
;
TRKSEC4:POP	HL		; GET TRACK NUMBER (HL).
	PUSH	BC
	PUSH	DE
	PUSH	HL
	EX	DE,HL
	LD	HL,(OFFSET)	; ADJUST FOR FIRST TRACK OFFSET.
	ADD	HL,DE
	LD	B,H
	LD	C,L
	CALL	SETTRK		; SELECT THIS TRACK.
	POP	DE		; RESET CURRENT TRACK POINTER.
	LD	HL,(SCRATCH2)
	LD	(HL),E
	INC	HL
	LD	(HL),D
	POP	DE
	LD	HL,(SCRATCH3)	; RESET THE FIRST SECTOR ON THIS TRACK.
	LD	(HL),E
	INC	HL
	LD	(HL),D
	POP	BC
	LD	A,C		; NOW SUBTRACT THE DESIRED ONE.
	SUB	E		; TO MAKE IT RELATIVE (1-# SECTORS/TRACK).
	LD	C,A
	LD	A,B
	SBC	A,D
	LD	B,A
	LD	HL,(XLATE)	; TRANSLATE THIS SECTOR ACCORDING TO THIS TABLE.
	EX	DE,HL
	CALL	SECTRN		; LET THE BIOS TRANSLATE IT.
	LD	C,L
	LD	B,H
	JP	SETSEC		; AND SELECT IT.
;
;   COMPUTE BLOCK NUMBER FROM RECORD NUMBER (SAVNREC) AND
; EXTENT NUMBER (SAVEXT).
;
GETBLOCK:
	LD	HL,BLKSHFT	; GET LOGICAL TO PHYSICAL CONVERSION.
	LD	C,(HL)		; NOTE THAT THIS IS BASE 2 LOG OF RATIO.
	LD	A,(SAVNREC)	; GET RECORD NUMBER.
GETBLK1:OR	A		; COMPUTE (A)=(A)/2^BLKSHFT.
	RRA
	DEC	C
	JP	NZ,GETBLK1
	LD	B,A		; SAVE RESULT IN (B).
	LD	A,8
	SUB	(HL)
	LD	C,A		; COMPUTE (C)=8-BLKSHFT.
	LD	A,(SAVEXT)
GETBLK2:DEC	C		; COMPUTE (A)=SAVEXT*2^(8-BLKSHFT).
	JP	Z,GETBLK3
	OR	A
	RLA
	JP	GETBLK2
GETBLK3:ADD	A,B
	RET
;
;   ROUTINE TO EXTRACT THE (BC) BLOCK BYTE FROM THE FCB POINTED
; TO BY (PARAMS). IF THIS IS A BIG-DISK, THEN THESE ARE 16 BIT
; BLOCK NUMBERS, ELSE THEY ARE 8 BIT NUMBERS.
; NUMBER IS RETURNED IN (HL).
;
EXTBLK:	LD	HL,(PARAMS)	; GET FCB ADDRESS.
	LD	DE,16		; BLOCK NUMBERS START 16 BYTES INTO FCB.
	ADD	HL,DE
	ADD	HL,BC
	LD	A,(BIGDISK)	; ARE WE USING A BIG-DISK?
	OR	A
	JP	Z,EXTBLK1
	LD	L,(HL)		; NO, EXTRACT AN 8 BIT NUMBER FROM THE FCB.
	LD	H,0
	RET
EXTBLK1:ADD	HL,BC		; YES, EXTRACT A 16 BIT NUMBER.
	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	EX	DE,HL		; RETURN IN (HL).
	RET
;
;   COMPUTE BLOCK NUMBER.
;
COMBLK:	CALL	GETBLOCK
	LD	C,A
	LD	B,0
	CALL	EXTBLK
	LD	(BLKNMBR),HL
	RET
;
;   CHECK FOR A ZERO BLOCK NUMBER (UNUSED).
;
CHKBLK:	LD	HL,(BLKNMBR)
	LD	A,L		; IS IT ZERO?
	OR	H
	RET
;
;   ADJUST PHYSICAL BLOCK (BLKNMBR) AND CONVERT TO LOGICAL
; SECTOR (LOGSECT). THIS IS THE STARTING SECTOR OF THIS BLOCK.
; THE ACTUAL SECTOR OF INTEREST IS THEN ADDED TO THIS AND THE
; RESULTING SECTOR NUMBER IS STORED BACK IN (BLKNMBR). THIS
; WILL STILL HAVE TO BE ADJUSTED FOR THE TRACK NUMBER.
;
LOGICAL:LD	A,(BLKSHFT)	; GET LOG2(PHYSICAL/LOGICAL SECTORS).
	LD	HL,(BLKNMBR)	; GET PHYSICAL SECTOR DESIRED.
LOGICL1:ADD	HL,HL		; COMPUTE LOGICAL SECTOR NUMBER.
	DEC	A		; NOTE LOGICAL SECTORS ARE 128 BYTES LONG.
	JP	NZ,LOGICL1
	LD	(LOGSECT),HL	; SAVE LOGICAL SECTOR.
	LD	A,(BLKMASK)	; GET BLOCK MASK.
	LD	C,A
	LD	A,(SAVNREC)	; GET NEXT SECTOR TO ACCESS.
	AND	C		; EXTRACT THE RELATIVE POSITION WITHIN PHYSICAL BLOCK.
	OR	L		; AND ADD IT TOO LOGICAL SECTOR.
	LD	L,A
	LD	(BLKNMBR),HL	; AND STORE.
	RET
;
;   SET (HL) TO POINT TO EXTENT BYTE IN FCB.
;
SETEXT:	LD	HL,(PARAMS)
	LD	DE,12		; IT IS THE TWELTH BYTE.
	ADD	HL,DE
	RET
;
;   SET (HL) TO POINT TO RECORD COUNT BYTE IN FCB AND (DE) TO
; NEXT RECORD NUMBER BYTE.
;
SETHLDE:LD	HL,(PARAMS)
	LD	DE,15		; RECORD COUNT BYTE (#15).
	ADD	HL,DE
	EX	DE,HL
	LD	HL,17		; NEXT RECORD NUMBER (#32).
	ADD	HL,DE
	RET
;
;   SAVE CURRENT FILE DATA FROM FCB.
;
STRDATA:CALL	SETHLDE
	LD	A,(HL)		; GET AND STORE RECORD COUNT BYTE.
	LD	(SAVNREC),A
	EX	DE,HL
	LD	A,(HL)		; GET AND STORE NEXT RECORD NUMBER BYTE.
	LD	(SAVNXT),A
	CALL	SETEXT		; POINT TO EXTENT BYTE.
	LD	A,(EXTMASK)	; GET EXTENT MASK.
	AND	(HL)
	LD	(SAVEXT),A	; AND SAVE EXTENT HERE.
	RET
;
;   SET THE NEXT RECORD TO ACCESS. IF (MODE) IS SET TO 2, THEN
; THE LAST RECORD BYTE (SAVNREC) HAS THE CORRECT NUMBER TO ACCESS.
; FOR SEQUENTIAL ACCESS, (MODE) WILL BE EQUAL TO 1.
;
SETNREC:CALL	SETHLDE
	LD	A,(MODE)	; GET SEQUENTIAL FLAG (=1).
	CP	2		; A 2 INDICATES THAT NO ADDER IS NEEDED.
	JP	NZ,STNREC1
	XOR	A		; CLEAR ADDER (RANDOM ACCESS?).
STNREC1:LD	C,A
	LD	A,(SAVNREC)	; GET LAST RECORD NUMBER.
	ADD	A,C		; INCREMENT RECORD COUNT.
	LD	(HL),A		; AND SET FCB'S NEXT RECORD BYTE.
	EX	DE,HL
	LD	A,(SAVNXT)	; GET NEXT RECORD BYTE FROM STORAGE.
	LD	(HL),A		; AND PUT THIS INTO FCB AS NUMBER OF RECORDS USED.
	RET
;
;   SHIFT (HL) RIGHT (C) BITS.
;
SHIFTR:	INC	C
SHIFTR1:DEC	C
	RET	Z
	LD	A,H
	OR	A
	RRA
	LD	H,A
	LD	A,L
	RRA
	LD	L,A
	JP	SHIFTR1
;
;   COMPUTE THE CHECK-SUM FOR THE DIRECTORY BUFFER. RETURN
; INTEGER SUM IN (A).
;
CHECKSUM:
	LD	C,128		; LENGTH OF BUFFER.
	LD	HL,(DIRBUF)	; GET ITS LOCATION.
	XOR	A		; CLEAR SUMMATION BYTE.
CHKSUM1:ADD	A,(HL)		; AND COMPUTE SUM IGNORING CARRIES.
	INC	HL
	DEC	C
	JP	NZ,CHKSUM1
	RET
;
;   SHIFT (HL) LEFT (C) BITS.
;
SHIFTL:	INC	C
SHIFTL1:DEC	C
	RET	Z
	ADD	HL,HL		; SHIFT LEFT 1 BIT.
	JP	SHIFTL1
;
;   ROUTINE TO SET A BIT IN A 16 BIT VALUE CONTAINED IN (BC).
; THE BIT SET DEPENDS ON THE CURRENT DRIVE SELECTION.
;
SETBIT:	PUSH	BC		; SAVE 16 BIT WORD.
	LD	A,(ACTIVE)	; GET ACTIVE DRIVE.
	LD	C,A
	LD	HL,1
	CALL	SHIFTL		; SHIFT BIT 0 INTO PLACE.
	POP	BC		; NOW 'OR' THIS WITH THE ORIGINAL WORD.
	LD	A,C
	OR	L
	LD	L,A		; LOW BYTE DONE, DO HIGH BYTE.
	LD	A,B
	OR	H
	LD	H,A
	RET
;
;   EXTRACT THE WRITE PROTECT STATUS BIT FOR THE CURRENT DRIVE.
; THE RESULT IS RETURNED IN (A), BIT 0.
;
GETWPRT:LD	HL,(WRTPRT)	; GET STATUS BYTES.
	LD	A,(ACTIVE)	; WHICH DRIVE IS CURRENT?
	LD	C,A
	CALL	SHIFTR		; SHIFT STATUS SUCH THAT BIT 0 IS THE
	LD	A,L		; ONE OF INTEREST FOR THIS DRIVE.
	AND	01H		; AND ISOLATE IT.
	RET
;
;   FUNCTION TO WRITE PROTECT THE CURRENT DISK.
;
WRTPRTD:LD	HL,WRTPRT	; POINT TO STATUS WORD.
	LD	C,(HL)		; SET (BC) EQUAL TO THE STATUS.
	INC	HL
	LD	B,(HL)
	CALL	SETBIT		; AND SET THIS BIT ACCORDING TO CURRENT DRIVE.
	LD	(WRTPRT),HL	; THEN SAVE.
	LD	HL,(DIRSIZE)	; NOW SAVE DIRECTORY SIZE LIMIT.
	INC	HL		; REMEMBER THE LAST ONE.
	EX	DE,HL
	LD	HL,(SCRATCH1)	; AND STORE IT HERE.
	LD	(HL),E		; PUT LOW BYTE.
	INC	HL
	LD	(HL),D		; THEN HIGH BYTE.
	RET
;
;   CHECK FOR A READ ONLY FILE.
;
CHKROFL:CALL	FCB2HL		; SET (HL) TO FILE ENTRY IN DIRECTORY BUFFER.
CKROF1:	LD	DE,9		; LOOK AT BIT 7 OF THE NINTH BYTE.
	ADD	HL,DE
	LD	A,(HL)
	RLA
	RET	NC		; RETURN IF OK.
	LD	HL,ROFILE	; ELSE, PRINT ERROR MESSAGE AND TERMINATE.
	JP	JUMPHL
;
;   CHECK THE WRITE PROTECT STATUS OF THE ACTIVE DISK.
;
CHKWPRT:CALL	GETWPRT
	RET	Z		; RETURN IF OK.
	LD	HL,RODISK	; ELSE PRINT MESSAGE AND TERMINATE.
	JP	JUMPHL
;
;   ROUTINE TO SET (HL) POINTING TO THE PROPER ENTRY IN THE
; DIRECTORY BUFFER.
;
FCB2HL:	LD	HL,(DIRBUF)	; GET ADDRESS OF BUFFER.
	LD	A,(FCBPOS)	; RELATIVE POSITION OF FILE.
;
;   ROUTINE TO ADD (A) TO (HL).
;
ADDA2HL:ADD	A,L
	LD	L,A
	RET	NC
	INC	H		; TAKE CARE OF ANY CARRY.
	RET
;
;   ROUTINE TO GET THE 'S2' BYTE FROM THE FCB SUPPLIED IN
; THE INITIAL PARAMETER SPECIFICATION.
;
GETS2:	LD	HL,(PARAMS)	; GET ADDRESS OF FCB.
	LD	DE,14		; RELATIVE POSITION OF 'S2'.
	ADD	HL,DE
	LD	A,(HL)		; EXTRACT THIS BYTE.
	RET
;
;   CLEAR THE 'S2' BYTE IN THE FCB.
;
CLEARS2:CALL	GETS2		; THIS SETS (HL) POINTING TO IT.
	LD	(HL),0		; NOW CLEAR IT.
	RET
;
;   SET BIT 7 IN THE 'S2' BYTE OF THE FCB.
;
SETS2B7:CALL	GETS2		; GET THE BYTE.
	OR	80H		; AND SET BIT 7.
	LD	(HL),A		; THEN STORE.
	RET
;
;   COMPARE (FILEPOS) WITH (SCRATCH1) AND SET FLAGS BASED ON
; THE DIFFERENCE. THIS CHECKS TO SEE IF THERE ARE MORE FILE
; NAMES IN THE DIRECTORY. WE ARE AT (FILEPOS) AND THERE ARE
; (SCRATCH1) OF THEM TO CHECK.
;
MOREFLS:LD	HL,(FILEPOS)	; WE ARE HERE.
	EX	DE,HL
	LD	HL,(SCRATCH1)	; AND DON'T GO PAST HERE.
	LD	A,E		; COMPUTE DIFFERENCE BUT DON'T KEEP.
	SUB	(HL)
	INC	HL
	LD	A,D
	SBC	A,(HL)		; SET CARRY IF NO MORE NAMES.
	RET
;
;   CALL THIS ROUTINE TO PREVENT (SCRATCH1) FROM BEING GREATER
; THAN (FILEPOS).
;
CHKNMBR:CALL	MOREFLS		; SCRATCH1 TOO BIG?
	RET	C
	INC	DE		; YES, RESET IT TO (FILEPOS).
	LD	(HL),D
	DEC	HL
	LD	(HL),E
	RET
;
;   COMPUTE (HL)=(DE)-(HL)
;
SUBHL:	LD	A,E		; COMPUTE DIFFERENCE.
	SUB	L
	LD	L,A		; STORE LOW BYTE.
	LD	A,D
	SBC	A,H
	LD	H,A		; AND THEN HIGH BYTE.
	RET
;
;   SET THE DIRECTORY CHECKSUM BYTE.
;
SETDIR:	LD	C,0FFH
;
;   ROUTINE TO SET OR COMPARE THE DIRECTORY CHECKSUM BYTE. IF
; (C)=0FFH, THEN THIS WILL SET THE CHECKSUM BYTE. ELSE THE BYTE
; WILL BE CHECKED. IF THE CHECK FAILS (THE DISK HAS BEEN CHANGED),
; THEN THIS DISK WILL BE WRITE PROTECTED.
;
CHECKDIR:
	LD	HL,(CKSUMTBL)
	EX	DE,HL
	LD	HL,(ALLOC1)
	CALL	SUBHL
	RET	NC		; OK IF (CKSUMTBL) > (ALLOC1), SO RETURN.
	PUSH	BC
	CALL	CHECKSUM	; ELSE COMPUTE CHECKSUM.
	LD	HL,(CHKVECT)	; GET ADDRESS OF CHECKSUM TABLE.
	EX	DE,HL
	LD	HL,(CKSUMTBL)
	ADD	HL,DE		; SET (HL) TO POINT TO BYTE FOR THIS DRIVE.
	POP	BC
	INC	C		; SET OR CHECK ?
	JP	Z,CHKDIR1
	CP	(HL)		; CHECK THEM.
	RET	Z		; RETURN IF THEY ARE THE SAME.
	CALL	MOREFLS		; NOT THE SAME, DO WE CARE?
	RET	NC
	CALL	WRTPRTD		; YES, MARK THIS AS WRITE PROTECTED.
	RET
CHKDIR1:LD	(HL),A		; JUST SET THE BYTE.
	RET
;
;   DO A WRITE TO THE DIRECTORY OF THE CURRENT DISK.
;
DIRWRITE:
	CALL	SETDIR		; SET CHECKSUM BYTE.
	CALL	DIRDMA		; SET DIRECTORY DMA ADDRESS.
	LD	C,1		; TELL THE BIOS TO ACTUALLY WRITE.
	CALL	DOWRITE		; THEN DO THE WRITE.
	JP	DEFDMA
;
;   READ FROM THE DIRECTORY.
;
DIRREAD:CALL	DIRDMA		; SET THE DIRECTORY DMA ADDRESS.
	CALL	DOREAD		; AND READ IT.
;
;   ROUTINE TO SET THE DMA ADDRESS TO THE USERS CHOICE.
;
DEFDMA:	LD	HL,USERDMA	; RESET THE DEFAULT DMA ADDRESS AND RETURN.
	JP	DIRDMA1
;
;   ROUTINE TO SET THE DMA ADDRESS FOR DIRECTORY WORK.
;
DIRDMA:	LD	HL,DIRBUF
;
;   SET THE DMA ADDRESS. ON ENTRY, (HL) POINTS TO
; WORD CONTAINING THE DESIRED DMA ADDRESS.
;
DIRDMA1:LD	C,(HL)
	INC	HL
	LD	B,(HL)		; SETUP (BC) AND GO TO THE BIOS TO SET IT.
	JP	SETDMA
;
;   MOVE THE DIRECTORY BUFFER INTO USER'S DMA SPACE.
;
MOVEDIR:LD	HL,(DIRBUF)	; BUFFER IS LOCATED HERE, AND
	EX	DE,HL
	LD	HL,(USERDMA)	; PUT IT HERE.
	LD	C,128		; THIS IS ITS LENGTH.
	JP	DE2HL		; MOVE IT NOW AND RETURN.
;
;   CHECK (FILEPOS) AND SET THE ZERO FLAG IF IT EQUALS 0FFFFH.
;
CKFILPOS:
	LD	HL,FILEPOS
	LD	A,(HL)
	INC	HL
	CP	(HL)		; ARE BOTH BYTES THE SAME?
	RET	NZ
	INC	A		; YES, BUT ARE THEY EACH 0FFH?
	RET
;
;   SET LOCATION (FILEPOS) TO 0FFFFH.
;
STFILPOS:
	LD	HL,0FFFFH
	LD	(FILEPOS),HL
	RET
;
;   MOVE ON TO THE NEXT FILE POSITION WITHIN THE CURRENT
; DIRECTORY BUFFER. IF NO MORE EXIST, SET POINTER TO 0FFFFH
; AND THE CALLING ROUTINE WILL CHECK FOR THIS. ENTER WITH (C)
; EQUAL TO 0FFH TO CAUSE THE CHECKSUM BYTE TO BE SET, ELSE WE
; WILL CHECK THIS DISK AND SET WRITE PROTECT IF CHECKSUMS ARE
; NOT THE SAME (APPLIES ONLY IF ANOTHER DIRECTORY SECTOR MUST
; BE READ).
;
NXENTRY:LD	HL,(DIRSIZE)	; GET DIRECTORY ENTRY SIZE LIMIT.
	EX	DE,HL
	LD	HL,(FILEPOS)	; GET CURRENT COUNT.
	INC	HL		; GO ON TO THE NEXT ONE.
	LD	(FILEPOS),HL
	CALL	SUBHL		; (HL)=(DIRSIZE)-(FILEPOS)
	JP	NC,NXENT1	; IS THERE MORE ROOM LEFT?
	JP	STFILPOS	; NO. SET THIS FLAG AND RETURN.
NXENT1:	LD	A,(FILEPOS)	; GET FILE POSITION WITHIN DIRECTORY.
	AND	03H		; ONLY LOOK WITHIN THIS SECTOR (ONLY 4 ENTRIES FIT).
	LD	B,5		; CONVERT TO RELATIVE POSITION (32 BYTES EACH).
NXENT2:	ADD	A,A		; NOTE THAT THIS IS NOT EFFICIENT CODE.
	DEC	B		; 5 'ADD A'S WOULD BE BETTER.
	JP	NZ,NXENT2
	LD	(FCBPOS),A	; SAVE IT AS POSITION OF FCB.
	OR	A
	RET	NZ		; RETURN IF WE ARE WITHIN BUFFER.
	PUSH	BC
	CALL	TRKSEC		; WE NEED THE NEXT DIRECTORY SECTOR.
	CALL	DIRREAD
	POP	BC
	JP	CHECKDIR
;
;   ROUTINE TO TO GET A BIT FROM THE DISK SPACE ALLOCATION
; MAP. IT IS RETURNED IN (A), BIT POSITION 0. ON ENTRY TO HERE,
; SET (BC) TO THE BLOCK NUMBER ON THE DISK TO CHECK.
; ON RETURN, (D) WILL CONTAIN THE ORIGINAL BIT POSITION FOR
; THIS BLOCK NUMBER AND (HL) WILL POINT TO THE ADDRESS FOR IT.
;
CKBITMAP:
	LD	A,C		; DETERMINE BIT NUMBER OF INTEREST.
	AND	07H		; COMPUTE (D)=(E)=(C AND 7)+1.
	INC	A
	LD	E,A		; SAVE PARTICULAR BIT NUMBER.
	LD	D,A
;
;   COMPUTE (BC)=(BC)/8.
;
	LD	A,C
	RRCA			; NOW SHIFT RIGHT 3 BITS.
	RRCA
	RRCA
	AND	1FH		; AND CLEAR BITS 7,6,5.
	LD	C,A
	LD	A,B
	ADD	A,A		; NOW SHIFT (B) INTO BITS 7,6,5.
	ADD	A,A
	ADD	A,A
	ADD	A,A
	ADD	A,A
	OR	C		; AND ADD IN (C).
	LD	C,A		; OK, (C) HA BEEN COMPLETED.
	LD	A,B		; IS THERE A BETTER WAY OF DOING THIS?
	RRCA
	RRCA
	RRCA
	AND	1FH
	LD	B,A		; AND NOW (B) IS COMPLETED.
;
;   USE THIS AS AN OFFSET INTO THE DISK SPACE ALLOCATION
; TABLE.
;
	LD	HL,(ALOCVECT)
	ADD	HL,BC
	LD	A,(HL)		; NOW GET CORRECT BYTE.
CKBMAP1:RLCA			; GET CORRECT BIT INTO POSITION 0.
	DEC	E
	JP	NZ,CKBMAP1
	RET
;
;   SET OR CLEAR THE BIT MAP SUCH THAT BLOCK NUMBER (BC) WILL BE MARKED
; AS USED. ON ENTRY, IF (E)=0 THEN THIS BIT WILL BE CLEARED, IF IT EQUALS
; 1 THEN IT WILL BE SET (DON'T USE ANYOTHER VALUES).
;
STBITMAP:
	PUSH	DE
	CALL	CKBITMAP	; GET THE BYTE OF INTEREST.
	AND	0FEH		; CLEAR THE AFFECTED BIT.
	POP	BC
	OR	C		; AND NOW SET IT ACORDING TO (C).
;
;  ENTRY TO RESTORE THE ORIGINAL BIT POSITION AND THEN STORE
; IN TABLE. (A) CONTAINS THE VALUE, (D) CONTAINS THE BIT
; POSITION (1-8), AND (HL) POINTS TO THE ADDRESS WITHIN THE
; SPACE ALLOCATION TABLE FOR THIS BYTE.
;
STBMAP1:RRCA			; RESTORE ORIGINAL BIT POSITION.
	DEC	D
	JP	NZ,STBMAP1
	LD	(HL),A		; AND STOR BYTE IN TABLE.
	RET
;
;   SET/CLEAR SPACE USED BITS IN ALLOCATION MAP FOR THIS FILE.
; ON ENTRY, (C)=1 TO SET THE MAP AND (C)=0 TO CLEAR IT.
;
SETFILE:CALL	FCB2HL		; GET ADDRESS OF FCB
	LD	DE,16
	ADD	HL,DE		; GET TO BLOCK NUMBER BYTES.
	PUSH	BC
	LD	C,17		; CHECK ALL 17 BYTES (MAX) OF TABLE.
SETFL1:	POP	DE
	DEC	C		; DONE ALL BYTES YET?
	RET	Z
	PUSH	DE
	LD	A,(BIGDISK)	; CHECK DISK SIZE FOR 16 BIT BLOCK NUMBERS.
	OR	A
	JP	Z,SETFL2
	PUSH	BC		; ONLY 8 BIT NUMBERS. SET (BC) TO THIS ONE.
	PUSH	HL
	LD	C,(HL)		; GET LOW BYTE FROM TABLE, ALWAYS
	LD	B,0		; SET HIGH BYTE TO ZERO.
	JP	SETFL3
SETFL2:	DEC	C		; FOR 16 BIT BLOCK NUMBERS, ADJUST COUNTER.
	PUSH	BC
	LD	C,(HL)		; NOW GET BOTH THE LOW AND HIGH BYTES.
	INC	HL
	LD	B,(HL)
	PUSH	HL
SETFL3:	LD	A,C		; BLOCK USED?
	OR	B
	JP	Z,SETFL4
	LD	HL,(DSKSIZE)	; IS THIS BLOCK NUMBER WITHIN THE
	LD	A,L		; SPACE ON THE DISK?
	SUB	C
	LD	A,H
	SBC	A,B
	CALL	NC,STBITMAP	; YES, SET THE PROPER BIT.
SETFL4:	POP	HL		; POINT TO NEXT BLOCK NUMBER IN FCB.
	INC	HL
	POP	BC
	JP	SETFL1
;
;   CONSTRUCT THE SPACE USED ALLOCATION BIT MAP FOR THE ACTIVE
; DRIVE. IF A FILE NAME STARTS WITH '$' AND IT IS UNDER THE
; CURRENT USER NUMBER, THEN (STATUS) IS SET TO MINUS 1. OTHERWISE
; IT IS NOT SET AT ALL.
;
BITMAP:	LD	HL,(DSKSIZE)	; COMPUTE SIZE OF ALLOCATION TABLE.
	LD	C,3
	CALL	SHIFTR		; (HL)=(HL)/8.
	INC	HL		; AT LEASE 1 BYTE.
	LD	B,H
	LD	C,L		; SET (BC) TO THE ALLOCATION TABLE LENGTH.
;
;   INITIALIZE THE BITMAP FOR THIS DRIVE. RIGHT NOW, THE FIRST
; TWO BYTES ARE SPECIFIED BY THE DISK PARAMETER BLOCK. HOWEVER
; A PATCH COULD BE ENTERED HERE IF IT WERE NECESSARY TO SETUP
; THIS TABLE IN A SPECIAL MANNOR. FOR EXAMPLE, THE BIOS COULD
; DETERMINE LOCATIONS OF 'BAD BLOCKS' AND SET THEM AS ALREADY
; 'USED' IN THE MAP.
;
	LD	HL,(ALOCVECT)	; NOW ZERO OUT THE TABLE NOW.
BITMAP1:LD	(HL),0
	INC	HL
	DEC	BC
	LD	A,B
	OR	C
	JP	NZ,BITMAP1
	LD	HL,(ALLOC0)	; GET INITIAL SPACE USED BY DIRECTORY.
	EX	DE,HL
	LD	HL,(ALOCVECT)	; AND PUT THIS INTO MAP.
	LD	(HL),E
	INC	HL
	LD	(HL),D
;
;   END OF INITIALIZATION PORTION.
;
	CALL	HOMEDRV		; NOW HOME THE DRIVE.
	LD	HL,(SCRATCH1)
	LD	(HL),3		; FORCE NEXT DIRECTORY REQUEST TO READ
	INC	HL		; IN A SECTOR.
	LD	(HL),0
	CALL	STFILPOS	; CLEAR INITIAL FILE POSITION ALSO.
BITMAP2:LD	C,0FFH		; READ NEXT FILE NAME IN DIRECTORY
	CALL	NXENTRY		; AND SET CHECKSUM BYTE.
	CALL	CKFILPOS	; IS THERE ANOTHER FILE?
	RET	Z
	CALL	FCB2HL		; YES, GET ITS ADDRESS.
	LD	A,0E5H
	CP	(HL)		; EMPTY FILE ENTRY?
	JP	Z,BITMAP2
	LD	A,(USERNO)	; NO, CORRECT USER NUMBER?
	CP	(HL)
	JP	NZ,BITMAP3
	INC	HL
	LD	A,(HL)		; YES, DOES NAME START WITH A '$'?
	SUB	'$'
	JP	NZ,BITMAP3
	DEC	A		; YES, SET ATATUS TO MINUS ONE.
	LD	(STATUS),A
BITMAP3:LD	C,1		; NOW SET THIS FILE'S SPACE AS USED IN BIT MAP.
	CALL	SETFILE
	CALL	CHKNMBR		; KEEP (SCRATCH1) IN BOUNDS.
	JP	BITMAP2
;
;   SET THE STATUS (STATUS) AND RETURN.
;
STSTATUS:
	LD	A,(FNDSTAT)
	JP	SETSTAT
;
;   CHECK EXTENTS IN (A) AND (C). SET THE ZERO FLAG IF THEY
; ARE THE SAME. THE NUMBER OF 16K CHUNKS OF DISK SPACE THAT
; THE DIRECTORY EXTENT COVERS IS EXPRESSAD IS (EXTMASK+1).
; NO REGISTERS ARE MODIFIED.
;
SAMEXT:	PUSH	BC
	PUSH	AF
	LD	A,(EXTMASK)	; GET EXTENT MASK AND USE IT TO
	CPL			; TO COMPARE BOTH EXTENT NUMBERS.
	LD	B,A		; SAVE RESULTING MASK HERE.
	LD	A,C		; MASK FIRST EXTENT AND SAVE IN (C).
	AND	B
	LD	C,A
	POP	AF		; NOW MASK SECOND EXTENT AND COMPARE
	AND	B		; WITH THE FIRST ONE.
	SUB	C
	AND	1FH		; (* ONLY CHECK BUTS 0-4 *)
	POP	BC		; THE ZERO FLAG IS SET IF THEY ARE THE SAME.
	RET			; RESTORE (BC) AND RETURN.
;
;   SEARCH FOR THE FIRST OCCURENCE OF A FILE NAME. ON ENTRY,
; REGISTER (C) SHOULD CONTAIN THE NUMBER OF BYTES OF THE FCB
; THAT MUST MATCH.
;
FINDFST:LD	A,0FFH
	LD	(FNDSTAT),A
	LD	HL,COUNTER	; SAVE CHARACTER COUNT.
	LD	(HL),C
	LD	HL,(PARAMS)	; GET FILENAME TO MATCH.
	LD	(SAVEFCB),HL	; AND SAVE.
	CALL	STFILPOS	; CLEAR INITIAL FILE POSITION (SET TO 0FFFFH).
	CALL	HOMEDRV		; HOME THE DRIVE.
;
;   ENTRY TO LOCATE THE NEXT OCCURENCE OF A FILENAME WITHIN THE
; DIRECTORY. THE DISK IS NOT EXPECTED TO HAVE BEEN CHANGED. IF
; IT WAS, THEN IT WILL BE WRITE PROTECTED.
;
FINDNXT:LD	C,0		; WRITE PROTECT THE DISK IF CHANGED.
	CALL	NXENTRY		; GET NEXT FILENAME ENTRY IN DIRECTORY.
	CALL	CKFILPOS	; IS FILE POSITION = 0FFFFH?
	JP	Z,FNDNXT6	; YES, EXIT NOW THEN.
	LD	HL,(SAVEFCB)	; SET (DE) POINTING TO FILENAME TO MATCH.
	EX	DE,HL
	LD	A,(DE)
	CP	0E5H		; EMPTY DIRECTORY ENTRY?
	JP	Z,FNDNXT1	; (* ARE WE TRYING TO RESERECT ERASED ENTRIES? *)
	PUSH	DE
	CALL	MOREFLS		; MORE FILES IN DIRECTORY?
	POP	DE
	JP	NC,FNDNXT6	; NO MORE. EXIT NOW.
FNDNXT1:CALL	FCB2HL		; GET ADDRESS OF THIS FCB IN DIRECTORY.
	LD	A,(COUNTER)	; GET NUMBER OF BYTES (CHARACTERS) TO CHECK.
	LD	C,A
	LD	B,0		; INITIALIZE BYTE POSITION COUNTER.
FNDNXT2:LD	A,C		; ARE WE DONE WITH THE COMPARE?
	OR	A
	JP	Z,FNDNXT5
	LD	A,(DE)		; NO, CHECK NEXT BYTE.
	CP	'?'		; DON'T CARE ABOUT THIS CHARACTER?
	JP	Z,FNDNXT4
	LD	A,B		; GET BYTES POSITION IN FCB.
	CP	13		; DON'T CARE ABOUT THE THIRTEENTH BYTE EITHER.
	JP	Z,FNDNXT4
	CP	12		; EXTENT BYTE?
	LD	A,(DE)
	JP	Z,FNDNXT3
	SUB	(HL)		; OTHERWISE COMPARE CHARACTERS.
	AND	7FH
	JP	NZ,FINDNXT	; NOT THE SAME, CHECK NEXT ENTRY.
	JP	FNDNXT4		; SO FAR SO GOOD, KEEP CHECKING.
FNDNXT3:PUSH	BC		; CHECK THE EXTENT BYTE HERE.
	LD	C,(HL)
	CALL	SAMEXT
	POP	BC
	JP	NZ,FINDNXT	; NOT THE SAME, LOOK SOME MORE.
;
;   SO FAR THE NAMES COMPARE. BUMP POINTERS TO THE NEXT BYTE
; AND CONTINUE UNTIL ALL (C) CHARACTERS HAVE BEEN CHECKED.
;
FNDNXT4:INC	DE		; BUMP POINTERS.
	INC	HL
	INC	B
	DEC	C		; ADJUST CHARACTER COUNTER.
	JP	FNDNXT2
FNDNXT5:LD	A,(FILEPOS)	; RETURN THE POSITION OF THIS ENTRY.
	AND	03H
	LD	(STATUS),A
	LD	HL,FNDSTAT
	LD	A,(HL)
	RLA
	RET	NC
	XOR	A
	LD	(HL),A
	RET
;
;   FILENAME WAS NOT FOUND. SET APPROPRIATE STATUS.
;
FNDNXT6:CALL	STFILPOS	; SET (FILEPOS) TO 0FFFFH.
	LD	A,0FFH		; SAY NOT LOCATED.
	JP	SETSTAT
;
;   ERASE FILES FROM THE DIRECTORY. ONLY THE FIRST BYTE OF THE
; FCB WILL BE AFFECTED. IT IS SET TO (E5).
;
ERAFILE:CALL	CHKWPRT		; IS DISK WRITE PROTECTED?
	LD	C,12		; ONLY COMPARE FILE NAMES.
	CALL	FINDFST		; GET FIRST FILE NAME.
ERAFIL1:CALL	CKFILPOS	; ANY FOUND?
	RET	Z		; NOPE, WE MUST BE DONE.
	CALL	CHKROFL		; IS FILE READ ONLY?
	CALL	FCB2HL		; NOPE, GET ADDRESS OF FCB AND
	LD	(HL),0E5H	; SET FIRST BYTE TO 'EMPTY'.
	LD	C,0		; CLEAR THE SPACE FROM THE BIT MAP.
	CALL	SETFILE
	CALL	DIRWRITE	; NOW WRITE THE DIRECTORY SECTOR BACK OUT.
	CALL	FINDNXT		; FIND THE NEXT FILE NAME.
	JP	ERAFIL1		; AND REPEAT PROCESS.
;
;   LOOK THROUGH THE SPACE ALLOCATION MAP (BIT MAP) FOR THE
; NEXT AVAILABLE BLOCK. START SEARCHING AT BLOCK NUMBER (BC-1).
; THE SEARCH PROCEDURE IS TO LOOK FOR AN EMPTY BLOCK THAT IS
; BEFORE THE STARTING BLOCK. IF NOT EMPTY, LOOK AT A LATER
; BLOCK NUMBER. IN THIS WAY, WE RETURN THE CLOSEST EMPTY BLOCK
; ON EITHER SIDE OF THE 'TARGET' BLOCK NUMBER. THIS WILL SPEED
; ACCESS ON RANDOM DEVICES. FOR SERIAL DEVICES, THIS SHOULD BE
; CHANGED TO LOOK IN THE FORWARD DIRECTION FIRST AND THEN START
; AT THE FRONT AND SEARCH SOME MORE.
;
;   ON RETURN, (DE)= BLOCK NUMBER THAT IS EMPTY AND (HL) =0
; IF NO EMPRY BLOCK WAS FOUND.
;
FNDSPACE:
	LD	D,B		; SET (DE) AS THE BLOCK THAT IS CHECKED.
	LD	E,C
;
;   LOOK BEFORE TARGET BLOCK. REGISTERS (BC) ARE USED AS THE LOWER
; POINTER AND (DE) AS THE UPPER POINTER.
;
FNDSPA1:LD	A,C		; IS BLOCK 0 SPECIFIED?
	OR	B
	JP	Z,FNDSPA2
	DEC	BC		; NOPE, CHECK PREVIOUS BLOCK.
	PUSH	DE
	PUSH	BC
	CALL	CKBITMAP
	RRA			; IS THIS BLOCK EMPTY?
	JP	NC,FNDSPA3	; YES. USE THIS.
;
;   NOTE THAT THE ABOVE LOGIC GETS THE FIRST BLOCK THAT IT FINDS
; THAT IS EMPTY. THUS A FILE COULD BE WRITTEN 'BACKWARD' MAKING
; IT VERY SLOW TO ACCESS. THIS COULD BE CHANGED TO LOOK FOR THE
; FIRST EMPTY BLOCK AND THEN CONTINUE UNTIL THE START OF THIS
; EMPTY SPACE IS LOCATED AND THEN USED THAT STARTING BLOCK.
; THIS SHOULD HELP SPEED UP ACCESS TO SOME FILES ESPECIALLY ON
; A WELL USED DISK WITH LOTS OF FAIRLY SMALL 'HOLES'.
;
	POP	BC		; NOPE, CHECK SOME MORE.
	POP	DE
;
;   NOW LOOK AFTER TARGET BLOCK.
;
FNDSPA2:LD	HL,(DSKSIZE)	; IS BLOCK (DE) WITHIN DISK LIMITS?
	LD	A,E
	SUB	L
	LD	A,D
	SBC	A,H
	JP	NC,FNDSPA4
	INC	DE		; YES, MOVE ON TO NEXT ONE.
	PUSH	BC
	PUSH	DE
	LD	B,D
	LD	C,E
	CALL	CKBITMAP	; CHECK IT.
	RRA			; EMPTY?
	JP	NC,FNDSPA3
	POP	DE		; NOPE, CONTINUE SEARCHING.
	POP	BC
	JP	FNDSPA1
;
;   EMPTY BLOCK FOUND. SET IT AS USED AND RETURN WITH (HL)
; POINTING TO IT (TRUE?).
;
FNDSPA3:RLA			; RESET BYTE.
	INC	A		; AND SET BIT 0.
	CALL	STBMAP1		; UPDATE BIT MAP.
	POP	HL		; SET RETURN REGISTERS.
	POP	DE
	RET
;
;   FREE BLOCK WAS NOT FOUND. IF (BC) IS NOT ZERO, THEN WE HAVE
; NOT CHECKED ALL OF THE DISK SPACE.
;
FNDSPA4:LD	A,C
	OR	B
	JP	NZ,FNDSPA1
	LD	HL,0		; SET 'NOT FOUND' STATUS.
	RET
;
;   MOVE A COMPLETE FCB ENTRY INTO THE DIRECTORY AND WRITE IT.
;
FCBSET:	LD	C,0
	LD	E,32		; LENGTH OF EACH ENTRY.
;
;   MOVE (E) BYTES FROM THE FCB POINTED TO BY (PARAMS) INTO
; FCB IN DIRECTORY STARTING AT RELATIVE BYTE (C). THIS UPDATED
; DIRECTORY BUFFER IS THEN WRITTEN TO THE DISK.
;
UPDATE:	PUSH	DE
	LD	B,0		; SET (BC) TO RELATIVE BYTE POSITION.
	LD	HL,(PARAMS)	; GET ADDRESS OF FCB.
	ADD	HL,BC		; COMPUTE STARTING BYTE.
	EX	DE,HL
	CALL	FCB2HL		; GET ADDRESS OF FCB TO UPDATE IN DIRECTORY.
	POP	BC		; SET (C) TO NUMBER OF BYTES TO CHANGE.
	CALL	DE2HL
UPDATE1:CALL	TRKSEC		; DETERMINE THE TRACK AND SECTOR AFFECTED.
	JP	DIRWRITE	; THEN WRITE THIS SECTOR OUT.
;
;   ROUTINE TO CHANGE THE NAME OF ALL FILES ON THE DISK WITH A
; SPECIFIED NAME. THE FCB CONTAINS THE CURRENT NAME AS THE
; FIRST 12 CHARACTERS AND THE NEW NAME 16 BYTES INTO THE FCB.
;
CHGNAMES:
	CALL	CHKWPRT		; CHECK FOR A WRITE PROTECTED DISK.
	LD	C,12		; MATCH FIRST 12 BYTES OF FCB ONLY.
	CALL	FINDFST		; GET FIRST NAME.
	LD	HL,(PARAMS)	; GET ADDRESS OF FCB.
	LD	A,(HL)		; GET USER NUMBER.
	LD	DE,16		; MOVE OVER TO DESIRED NAME.
	ADD	HL,DE
	LD	(HL),A		; KEEP SAME USER NUMBER.
CHGNAM1:CALL	CKFILPOS	; ANY MATCHING FILE FOUND?
	RET	Z		; NO, WE MUST BE DONE.
	CALL	CHKROFL		; CHECK FOR READ ONLY FILE.
	LD	C,16		; START 16 BYTES INTO FCB.
	LD	E,12		; AND UPDATE THE FIRST 12 BYTES OF DIRECTORY.
	CALL	UPDATE
	CALL	FINDNXT		; GET TE NEXT FILE NAME.
	JP	CHGNAM1		; AND CONTINUE.
;
;   UPDATE A FILES ATTRIBUTES. THE PROCEDURE IS TO SEARCH FOR
; EVERY FILE WITH THE SAME NAME AS SHOWN IN FCB (IGNORING BIT 7)
; AND THEN TO UPDATE IT (WHICH INCLUDES BIT 7). NO OTHER CHANGES
; ARE MADE.
;
SAVEATTR:
	LD	C,12		; MATCH FIRST 12 BYTES.
	CALL	FINDFST		; LOOK FOR FIRST FILENAME.
SAVATR1:CALL	CKFILPOS	; WAS ONE FOUND?
	RET	Z		; NOPE, WE MUST BE DONE.
	LD	C,0		; YES, UPDATE THE FIRST 12 BYTES NOW.
	LD	E,12
	CALL	UPDATE		; UPDATE FILENAME AND WRITE DIRECTORY.
	CALL	FINDNXT		; AND GET THE NEXT FILE.
	JP	SAVATR1		; THEN CONTINUE UNTIL DONE.
;
;  OPEN A FILE (NAME SPECIFIED IN FCB).
;
OPENIT:	LD	C,15		; COMPARE THE FIRST 15 BYTES.
	CALL	FINDFST		; GET THE FIRST ONE IN DIRECTORY.
	CALL	CKFILPOS	; ANY AT ALL?
	RET	Z
OPENIT1:CALL	SETEXT		; POINT TO EXTENT BYTE WITHIN USERS FCB.
	LD	A,(HL)		; AND GET IT.
	PUSH	AF		; SAVE IT AND ADDRESS.
	PUSH	HL
	CALL	FCB2HL		; POINT TO FCB IN DIRECTORY.
	EX	DE,HL
	LD	HL,(PARAMS)	; THIS IS THE USERS COPY.
	LD	C,32		; MOVE IT INTO USERS SPACE.
	PUSH	DE
	CALL	DE2HL
	CALL	SETS2B7		; SET BIT 7 IN 'S2' BYTE (UNMODIFIED).
	POP	DE		; NOW GET THE EXTENT BYTE FROM THIS FCB.
	LD	HL,12
	ADD	HL,DE
	LD	C,(HL)		; INTO (C).
	LD	HL,15		; NOW GET THE RECORD COUNT BYTE INTO (B).
	ADD	HL,DE
	LD	B,(HL)
	POP	HL		; KEEP THE SAME EXTENT AS THE USER HAD ORIGINALLY.
	POP	AF
	LD	(HL),A
	LD	A,C		; IS IT THE SAME AS IN THE DIRECTORY FCB?
	CP	(HL)
	LD	A,B		; IF YES, THEN USE THE SAME RECORD COUNT.
	JP	Z,OPENIT2
	LD	A,0		; IF THE USER SPECIFIED AN EXTENT GREATER THAN
	JP	C,OPENIT2	; THE ONE IN THE DIRECTORY, THEN SET RECORD COUNT TO 0.
	LD	A,128		; OTHERWISE SET TO MAXIMUM.
OPENIT2:LD	HL,(PARAMS)	; SET RECORD COUNT IN USERS FCB TO (A).
	LD	DE,15
	ADD	HL,DE		; COMPUTE RELATIVE POSITION.
	LD	(HL),A		; AND SET THE RECORD COUNT.
	RET
;
;   MOVE TWO BYTES FROM (DE) TO (HL) IF (AND ONLY IF) (HL)
; POINT TO A ZERO VALUE (16 BIT).
;   RETURN WITH ZERO FLAG SET IT (DE) WAS MOVED. REGISTERS (DE)
; AND (HL) ARE NOT CHANGED. HOWEVER (A) IS.
;
MOVEWORD:
	LD	A,(HL)		; CHECK FOR A ZERO WORD.
	INC	HL
	OR	(HL)		; BOTH BYTES ZERO?
	DEC	HL
	RET	NZ		; NOPE, JUST RETURN.
	LD	A,(DE)		; YES, MOVE TWO BYTES FROM (DE) INTO
	LD	(HL),A		; THIS ZERO SPACE.
	INC	DE
	INC	HL
	LD	A,(DE)
	LD	(HL),A
	DEC	DE		; DON'T DISTURB THESE REGISTERS.
	DEC	HL
	RET
;
;   GET HERE TO CLOSE A FILE SPECIFIED BY (FCB).
;
CLOSEIT:XOR	A		; CLEAR STATUS AND FILE POSITION BYTES.
	LD	(STATUS),A
	LD	(FILEPOS),A
	LD	(FILEPOS+1),A
	CALL	GETWPRT		; GET WRITE PROTECT BIT FOR THIS DRIVE.
	RET	NZ		; JUST RETURN IF IT IS SET.
	CALL	GETS2		; ELSE GET THE 'S2' BYTE.
	AND	80H		; AND LOOK AT BIT 7 (FILE UNMODIFIED?).
	RET	NZ		; JUST RETURN IF SET.
	LD	C,15		; ELSE LOOK UP THIS FILE IN DIRECTORY.
	CALL	FINDFST
	CALL	CKFILPOS	; WAS IT FOUND?
	RET	Z		; JUST RETURN IF NOT.
	LD	BC,16		; SET (HL) POINTING TO RECORDS USED SECTION.
	CALL	FCB2HL
	ADD	HL,BC
	EX	DE,HL
	LD	HL,(PARAMS)	; DO THE SAME FOR USERS SPECIFIED FCB.
	ADD	HL,BC
	LD	C,16		; THIS MANY BYTES ARE PRESENT IN THIS EXTENT.
CLOSEIT1:
	LD	A,(BIGDISK)	; 8 OR 16 BIT RECORD NUMBERS?
	OR	A
	JP	Z,CLOSEIT4
	LD	A,(HL)		; JUST 8 BIT. GET ONE FROM USERS FCB.
	OR	A
	LD	A,(DE)		; NOW GET ONE FROM DIRECTORY FCB.
	JP	NZ,CLOSEIT2
	LD	(HL),A		; USERS BYTE WAS ZERO. UPDATE FROM DIRECTORY.
CLOSEIT2:
	OR	A
	JP	NZ,CLOSEIT3
	LD	A,(HL)		; DIRECTORIES BYTE WAS ZERO, UPDATE FROM USERS FCB.
	LD	(DE),A
CLOSEIT3:
	CP	(HL)		; IF NEITHER ONE OF THESE BYTES WERE ZERO,
	JP	NZ,CLOSEIT7	; THEN CLOSE ERROR IF THEY ARE NOT THE SAME.
	JP	CLOSEIT5	; OK SO FAR, GET TO NEXT BYTE IN FCBS.
CLOSEIT4:
	CALL	MOVEWORD	; UPDATE USERS FCB IF IT IS ZERO.
	EX	DE,HL
	CALL	MOVEWORD	; UPDATE DIRECTORIES FCB IF IT IS ZERO.
	EX	DE,HL
	LD	A,(DE)		; IF THESE TWO VALUES ARE NO DIFFERENT,
	CP	(HL)		; THEN A CLOSE ERROR OCCURED.
	JP	NZ,CLOSEIT7
	INC	DE		; CHECK SECOND BYTE.
	INC	HL
	LD	A,(DE)
	CP	(HL)
	JP	NZ,CLOSEIT7
	DEC	C		; REMEMBER 16 BIT VALUES.
CLOSEIT5:
	INC	DE		; BUMP TO NEXT ITEM IN TABLE.
	INC	HL
	DEC	C		; THERE ARE 16 ENTRIES ONLY.
	JP	NZ,CLOSEIT1	; CONTINUE IF MORE TO DO.
	LD	BC,0FFECH	; BACKUP 20 PLACES (EXTENT BYTE).
	ADD	HL,BC
	EX	DE,HL
	ADD	HL,BC
	LD	A,(DE)
	CP	(HL)		; DIRECTORY'S EXTENT ALREADY GREATER THAN THE
	JP	C,CLOSEIT6	; USERS EXTENT?
	LD	(HL),A		; NO, UPDATE DIRECTORY EXTENT.
	LD	BC,3		; AND UPDATE THE RECORD COUNT BYTE IN
	ADD	HL,BC		; DIRECTORIES FCB.
	EX	DE,HL
	ADD	HL,BC
	LD	A,(HL)		; GET FROM USER.
	LD	(DE),A		; AND PUT IN DIRECTORY.
CLOSEIT6:
	LD	A,0FFH		; SET 'WAS OPEN AND IS NOW CLOSED' BYTE.
	LD	(CLOSEFLG),A
	JP	UPDATE1		; UPDATE THE DIRECTORY NOW.
CLOSEIT7:
	LD	HL,STATUS	; SET RETURN STATUS AND THEN RETURN.
	DEC	(HL)
	RET
;
;   ROUTINE TO GET THE NEXT EMPTY SPACE IN THE DIRECTORY. IT
; WILL THEN BE CLEARED FOR USE.
;
GETEMPTY:
	CALL	CHKWPRT		; MAKE SURE DISK IS NOT WRITE PROTECTED.
	LD	HL,(PARAMS)	; SAVE CURRENT PARAMETERS (FCB).
	PUSH	HL
	LD	HL,EMPTYFCB	; USE SPECIAL ONE FOR EMPTY SPACE.
	LD	(PARAMS),HL
	LD	C,1		; SEARCH FOR FIRST EMPTY SPOT IN DIRECTORY.
	CALL	FINDFST		; (* ONLY CHECK FIRST BYTE *)
	CALL	CKFILPOS	; NONE?
	POP	HL
	LD	(PARAMS),HL	; RESTORE ORIGINAL FCB ADDRESS.
	RET	Z		; RETURN IF NO MORE SPACE.
	EX	DE,HL
	LD	HL,15		; POINT TO NUMBER OF RECORDS FOR THIS FILE.
	ADD	HL,DE
	LD	C,17		; AND CLEAR ALL OF THIS SPACE.
	XOR	A
GETMT1:	LD	(HL),A
	INC	HL
	DEC	C
	JP	NZ,GETMT1
	LD	HL,13		; CLEAR THE 'S1' BYTE ALSO.
	ADD	HL,DE
	LD	(HL),A
	CALL	CHKNMBR		; KEEP (SCRATCH1) WITHIN BOUNDS.
	CALL	FCBSET		; WRITE OUT THIS FCB ENTRY TO DIRECTORY.
	JP	SETS2B7		; SET 'S2' BYTE BIT 7 (UNMODIFIED AT PRESENT).
;
;   ROUTINE TO CLOSE THE CURRENT EXTENT AND OPEN THE NEXT ONE
; FOR READING.
;
GETNEXT:XOR	A
	LD	(CLOSEFLG),A	; CLEAR CLOSE FLAG.
	CALL	CLOSEIT		; CLOSE THIS EXTENT.
	CALL	CKFILPOS
	RET	Z		; NOT THERE???
	LD	HL,(PARAMS)	; GET EXTENT BYTE.
	LD	BC,12
	ADD	HL,BC
	LD	A,(HL)		; AND INCREMENT IT.
	INC	A
	AND	1FH		; KEEP WITHIN RANGE 0-31.
	LD	(HL),A
	JP	Z,GTNEXT1	; OVERFLOW?
	LD	B,A		; MASK EXTENT BYTE.
	LD	A,(EXTMASK)
	AND	B
	LD	HL,CLOSEFLG	; CHECK CLOSE FLAG (0FFH IS OK).
	AND	(HL)
	JP	Z,GTNEXT2	; IF ZERO, WE MUST READ IN NEXT EXTENT.
	JP	GTNEXT3		; ELSE, IT IS ALREADY IN MEMORY.
GTNEXT1:LD	BC,2		; POINT TO THE 'S2' BYTE.
	ADD	HL,BC
	INC	(HL)		; AND BUMP IT.
	LD	A,(HL)		; TOO MANY EXTENTS?
	AND	0FH
	JP	Z,GTNEXT5	; YES, SET ERROR CODE.
;
;   GET HERE TO OPEN THE NEXT EXTENT.
;
GTNEXT2:LD	C,15		; SET TO CHECK FIRST 15 BYTES OF FCB.
	CALL	FINDFST		; FIND THE FIRST ONE.
	CALL	CKFILPOS	; NONE AVAILABLE?
	JP	NZ,GTNEXT3
	LD	A,(RDWRTFLG)	; NO EXTENT PRESENT. CAN WE OPEN AN EMPTY ONE?
	INC	A		; 0FFH MEANS READING (SO NOT POSSIBLE).
	JP	Z,GTNEXT5	; OR AN ERROR.
	CALL	GETEMPTY	; WE ARE WRITING, GET AN EMPTY ENTRY.
	CALL	CKFILPOS	; NONE?
	JP	Z,GTNEXT5	; ERROR IF TRUE.
	JP	GTNEXT4		; ELSE WE ARE ALMOST DONE.
GTNEXT3:CALL	OPENIT1		; OPEN THIS EXTENT.
GTNEXT4:CALL	STRDATA		; MOVE IN UPDATED DATA (REC #, EXTENT #, ETC.)
	XOR	A		; CLEAR STATUS AND RETURN.
	JP	SETSTAT
;
;   ERROR IN EXTENDING THE FILE. TOO MANY EXTENTS WERE NEEDED
; OR NOT ENOUGH SPACE ON THE DISK.
;
GTNEXT5:CALL	IOERR1		; SET ERROR CODE, CLEAR BIT 7 OF 'S2'
	JP	SETS2B7		; SO THIS IS NOT WRITTEN ON A CLOSE.
;
;   READ A SEQUENTIAL FILE.
;
RDSEQ:	LD	A,1		; SET SEQUENTIAL ACCESS MODE.
	LD	(MODE),A
RDSEQ1:	LD	A,0FFH		; DON'T ALLOW READING UNWRITTEN SPACE.
	LD	(RDWRTFLG),A
	CALL	STRDATA		; PUT REC# AND EXT# INTO FCB.
	LD	A,(SAVNREC)	; GET NEXT RECORD TO READ.
	LD	HL,SAVNXT	; GET NUMBER OF RECORDS IN EXTENT.
	CP	(HL)		; WITHIN THIS EXTENT?
	JP	C,RDSEQ2
	CP	128		; NO. IS THIS EXTENT FULLY USED?
	JP	NZ,RDSEQ3	; NO. END-OF-FILE.
	CALL	GETNEXT		; YES, OPEN THE NEXT ONE.
	XOR	A		; RESET NEXT RECORD TO READ.
	LD	(SAVNREC),A
	LD	A,(STATUS)	; CHECK ON OPEN, SUCCESSFUL?
	OR	A
	JP	NZ,RDSEQ3	; NO, ERROR.
RDSEQ2:	CALL	COMBLK		; OK. COMPUTE BLOCK NUMBER TO READ.
	CALL	CHKBLK		; CHECK IT. WITHIN BOUNDS?
	JP	Z,RDSEQ3	; NO, ERROR.
	CALL	LOGICAL		; CONVERT (BLKNMBR) TO LOGICAL SECTOR (128 BYTE).
	CALL	TRKSEC1		; SET THE TRACK AND SECTOR FOR THIS BLOCK #.
	CALL	DOREAD		; AND READ IT.
	JP	SETNREC		; AND SET THE NEXT RECORD TO BE ACCESSED.
;
;   READ ERROR OCCURED. SET STATUS AND RETURN.
;
RDSEQ3:	JP	IOERR1
;
;   WRITE THE NEXT SEQUENTIAL RECORD.
;
WTSEQ:	LD	A,1		; SET SEQUENTIAL ACCESS MODE.
	LD	(MODE),A
WTSEQ1:	LD	A,0		; ALLOW AN ADDITION EMPTY EXTENT TO BE OPENED.
	LD	(RDWRTFLG),A
	CALL	CHKWPRT		; CHECK WRITE PROTECT STATUS.
	LD	HL,(PARAMS)
	CALL	CKROF1		; CHECK FOR READ ONLY FILE, (HL) ALREADY SET TO FCB.
	CALL	STRDATA		; PUT UPDATED DATA INTO FCB.
	LD	A,(SAVNREC)	; GET RECORD NUMBER TO WRITE.
	CP	128		; WITHIN RANGE?
	JP	NC,IOERR1	; NO, ERROR(?).
	CALL	COMBLK		; COMPUTE BLOCK NUMBER.
	CALL	CHKBLK		; CHECK NUMBER.
	LD	C,0		; IS THERE ONE TO WRITE TO?
	JP	NZ,WTSEQ6	; YES, GO DO IT.
	CALL	GETBLOCK	; GET NEXT BLOCK NUMBER WITHIN FCB TO USE.
	LD	(RELBLOCK),A	; AND SAVE.
	LD	BC,0		; START LOOKING FOR SPACE FROM THE START
	OR	A		; IF NONE ALLOCATED AS YET.
	JP	Z,WTSEQ2
	LD	C,A		; EXTRACT PREVIOUS BLOCK NUMBER FROM FCB
	DEC	BC		; SO WE CAN BE CLOSEST TO IT.
	CALL	EXTBLK
	LD	B,H
	LD	C,L
WTSEQ2:	CALL	FNDSPACE	; FIND THE NEXT EMPTY BLOCK NEAREST NUMBER (BC).
	LD	A,L		; CHECK FOR A ZERO NUMBER.
	OR	H
	JP	NZ,WTSEQ3
	LD	A,2		; NO MORE SPACE?
	JP	SETSTAT
WTSEQ3:	LD	(BLKNMBR),HL	; SAVE BLOCK NUMBER TO ACCESS.
	EX	DE,HL		; PUT BLOCK NUMBER INTO (DE).
	LD	HL,(PARAMS)	; NOW WE MUST UPDATE THE FCB FOR THIS
	LD	BC,16		; NEWLY ALLOCATED BLOCK.
	ADD	HL,BC
	LD	A,(BIGDISK)	; 8 OR 16 BIT BLOCK NUMBERS?
	OR	A
	LD	A,(RELBLOCK)	; (* UPDATE THIS ENTRY *)
	JP	Z,WTSEQ4	; ZERO MEANS 16 BIT ONES.
	CALL	ADDA2HL		; (HL)=(HL)+(A)
	LD	(HL),E		; STORE NEW BLOCK NUMBER.
	JP	WTSEQ5
WTSEQ4:	LD	C,A		; COMPUTE SPOT IN THIS 16 BIT TABLE.
	LD	B,0
	ADD	HL,BC
	ADD	HL,BC
	LD	(HL),E		; STUFF BLOCK NUMBER (DE) THERE.
	INC	HL
	LD	(HL),D
WTSEQ5:	LD	C,2		; SET (C) TO INDICATE WRITING TO UN-USED DISK SPACE.
WTSEQ6:	LD	A,(STATUS)	; ARE WE OK SO FAR?
	OR	A
	RET	NZ
	PUSH	BC		; YES, SAVE WRITE FLAG FOR BIOS (REGISTER C).
	CALL	LOGICAL		; CONVERT (BLKNMBR) OVER TO LOICAL SECTORS.
	LD	A,(MODE)	; GET ACCESS MODE FLAG (1=SEQUENTIAL,
	DEC	A		; 0=RANDOM, 2=SPECIAL?).
	DEC	A
	JP	NZ,WTSEQ9
;
;   SPECIAL RANDOM I/O FROM FUNCTION #40. MAYBE FOR M/PM, BUT THE
; CURRENT BLOCK, IF IT HAS NOT BEEN WRITTEN TO, WILL BE ZEROED
; OUT AND THEN WRITTEN (REASON?).
;
	POP	BC
	PUSH	BC
	LD	A,C		; GET WRITE STATUS FLAG (2=WRITING UNUSED SPACE).
	DEC	A
	DEC	A
	JP	NZ,WTSEQ9
	PUSH	HL
	LD	HL,(DIRBUF)	; ZERO OUT THE DIRECTORY BUFFER.
	LD	D,A		; NOTE THAT (A) IS ZERO HERE.
WTSEQ7:	LD	(HL),A
	INC	HL
	INC	D		; DO 128 BYTES.
	JP	P,WTSEQ7
	CALL	DIRDMA		; TELL THE BIOS THE DMA ADDRESS FOR DIRECTORY ACCESS.
	LD	HL,(LOGSECT)	; GET SECTOR THAT STARTS CURRENT BLOCK.
	LD	C,2		; SET 'WRITING TO UNUSED SPACE' FLAG.
WTSEQ8:	LD	(BLKNMBR),HL	; SAVE SECTOR TO WRITE.
	PUSH	BC
	CALL	TRKSEC1		; DETERMINE ITS TRACK AND SECTOR NUMBERS.
	POP	BC
	CALL	DOWRITE		; NOW WRITE OUT 128 BYTES OF ZEROS.
	LD	HL,(BLKNMBR)	; GET SECTOR NUMBER.
	LD	C,0		; SET NORMAL WRITE FLAG.
	LD	A,(BLKMASK)	; DETERMINE IF WE HAVE WRITTEN THE ENTIRE
	LD	B,A		; PHYSICAL BLOCK.
	AND	L
	CP	B
	INC	HL		; PREPARE FOR THE NEXT ONE.
	JP	NZ,WTSEQ8	; CONTINUE UNTIL (BLKMASK+1) SECTORS WRITTEN.
	POP	HL		; RESET NEXT SECTOR NUMBER.
	LD	(BLKNMBR),HL
	CALL	DEFDMA		; AND RESET DMA ADDRESS.
;
;   NORMAL DISK WRITE. SET THE DESIRED TRACK AND SECTOR THEN
; DO THE ACTUAL WRITE.
;
WTSEQ9:	CALL	TRKSEC1		; DETERMINE TRACK AND SECTOR FOR THIS WRITE.
	POP	BC		; GET WRITE STATUS FLAG.
	PUSH	BC
	CALL	DOWRITE		; AND WRITE THIS OUT.
	POP	BC
	LD	A,(SAVNREC)	; GET NUMBER OF RECORDS IN FILE.
	LD	HL,SAVNXT	; GET LAST RECORD WRITTEN.
	CP	(HL)
	JP	C,WTSEQ10
	LD	(HL),A		; WE HAVE TO UPDATE RECORD COUNT.
	INC	(HL)
	LD	C,2
;
;*   THIS AREA HAS BEEN PATCHED TO CORRECT DISK UPDATE PROBLEM
;* WHEN USING BLOCKING AND DE-BLOCKING IN THE BIOS.
;
WTSEQ10:NOP			; WAS 'DCR C'
	NOP			; WAS 'DCR C'
	LD	HL,0		; WAS 'JNZ WTSEQ99'
;
; *   END OF PATCH.
;
	PUSH	AF
	CALL	GETS2		; SET 'EXTENT WRITTEN TO' FLAG.
	AND	7FH		; (* CLEAR BIT 7 *)
	LD	(HL),A
	POP	AF		; GET RECORD COUNT FOR THIS EXTENT.
WTSEQ99:CP	127		; IS IT FULL?
	JP	NZ,WTSEQ12
	LD	A,(MODE)	; YES, ARE WE IN SEQUENTIAL MODE?
	CP	1
	JP	NZ,WTSEQ12
	CALL	SETNREC		; YES, SET NEXT RECORD NUMBER.
	CALL	GETNEXT		; AND GET NEXT EMPTY SPACE IN DIRECTORY.
	LD	HL,STATUS	; OK?
	LD	A,(HL)
	OR	A
	JP	NZ,WTSEQ11
	DEC	A		; YES, SET RECORD COUNT TO -1.
	LD	(SAVNREC),A
WTSEQ11:LD	(HL),0		; CLEAR STATUS.
WTSEQ12:JP	SETNREC		; SET NEXT RECORD TO ACCESS.
;
;   FOR RANDOM I/O, SET THE FCB FOR THE DESIRED RECORD NUMBER
; BASED ON THE 'R0,R1,R2' BYTES. THESE BYTES IN THE FCB ARE
; USED AS FOLLOWS:
;
;       FCB+35            FCB+34            FCB+33
;  |     'R-2'      |      'R-1'      |      'R-0'     |
;  |7             0 | 7             0 | 7             0|
;  |0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0|
;  |    OVERFLOW   | | EXTRA |  EXTENT   |   RECORD #  |
;  | ______________| |_EXTENT|__NUMBER___|_____________|
;                     ALSO 'S2'
;
;   ON ENTRY, REGISTER (C) CONTAINS 0FFH IF THIS IS A READ
; AND THUS WE CAN NOT ACCESS UNWRITTEN DISK SPACE. OTHERWISE,
; ANOTHER EXTENT WILL BE OPENED (FOR WRITING) IF REQUIRED.
;
POSITION:
	XOR	A		; SET RANDOM I/O FLAG.
	LD	(MODE),A
;
;   SPECIAL ENTRY (FUNCTION #40). M/PM ?
;
POSITN1:PUSH	BC		; SAVE READ/WRITE FLAG.
	LD	HL,(PARAMS)	; GET ADDRESS OF FCB.
	EX	DE,HL
	LD	HL,33		; NOW GET BYTE 'R0'.
	ADD	HL,DE
	LD	A,(HL)
	AND	7FH		; KEEP BITS 0-6 FOR THE RECORD NUMBER TO ACCESS.
	PUSH	AF
	LD	A,(HL)		; NOW GET BIT 7 OF 'R0' AND BITS 0-3 OF 'R1'.
	RLA
	INC	HL
	LD	A,(HL)
	RLA
	AND	1FH		; AND SAVE THIS IN BITS 0-4 OF (C).
	LD	C,A		; THIS IS THE EXTENT BYTE.
	LD	A,(HL)		; NOW GET THE EXTRA EXTENT BYTE.
	RRA
	RRA
	RRA
	RRA
	AND	0FH
	LD	B,A		; AND SAVE IT IN (B).
	POP	AF		; GET RECORD NUMBER BACK TO (A).
	INC	HL		; CHECK OVERFLOW BYTE 'R2'.
	LD	L,(HL)
	INC	L
	DEC	L
	LD	L,6		; PREPARE FOR ERROR.
	JP	NZ,POSITN5	; OUT OF DISK SPACE ERROR.
	LD	HL,32		; STORE RECORD NUMBER INTO FCB.
	ADD	HL,DE
	LD	(HL),A
	LD	HL,12		; AND NOW CHECK THE EXTENT BYTE.
	ADD	HL,DE
	LD	A,C
	SUB	(HL)		; SAME EXTENT AS BEFORE?
	JP	NZ,POSITN2
	LD	HL,14		; YES, CHECK EXTRA EXTENT BYTE 'S2' ALSO.
	ADD	HL,DE
	LD	A,B
	SUB	(HL)
	AND	7FH
	JP	Z,POSITN3	; SAME, WE ARE ALMOST DONE THEN.
;
;  GET HERE WHEN ANOTHER EXTENT IS REQUIRED.
;
POSITN2:PUSH	BC
	PUSH	DE
	CALL	CLOSEIT		; CLOSE CURRENT EXTENT.
	POP	DE
	POP	BC
	LD	L,3		; PREPARE FOR ERROR.
	LD	A,(STATUS)
	INC	A
	JP	Z,POSITN4	; CLOSE ERROR.
	LD	HL,12		; PUT DESIRED EXTENT INTO FCB NOW.
	ADD	HL,DE
	LD	(HL),C
	LD	HL,14		; AND STORE EXTRA EXTENT BYTE 'S2'.
	ADD	HL,DE
	LD	(HL),B
	CALL	OPENIT		; TRY AND GET THIS EXTENT.
	LD	A,(STATUS)	; WAS IT THERE?
	INC	A
	JP	NZ,POSITN3
	POP	BC		; NO. CAN WE CREATE A NEW ONE (WRITING?).
	PUSH	BC
	LD	L,4		; PREPARE FOR ERROR.
	INC	C
	JP	Z,POSITN4	; NOPE, READING UNWRITTEN SPACE ERROR.
	CALL	GETEMPTY	; YES WE CAN, TRY TO FIND SPACE.
	LD	L,5		; PREPARE FOR ERROR.
	LD	A,(STATUS)
	INC	A
	JP	Z,POSITN4	; OUT OF SPACE?
;
;   NORMAL RETURN LOCATION. CLEAR ERROR CODE AND RETURN.
;
POSITN3:POP	BC		; RESTORE STACK.
	XOR	A		; AND CLEAR ERROR CODE BYTE.
	JP	SETSTAT
;
;   ERROR. SET THE 'S2' BYTE TO INDICATE THIS (WHY?).
;
POSITN4:PUSH	HL
	CALL	GETS2
	LD	(HL),0C0H
	POP	HL
;
;   RETURN WITH ERROR CODE (PRESENTLY IN L).
;
POSITN5:POP	BC
	LD	A,L		; GET ERROR CODE.
	LD	(STATUS),A
	JP	SETS2B7
;
;   READ A RANDOM RECORD.
;
READRAN:LD	C,0FFH		; SET 'READ' STATUS.
	CALL	POSITION	; POSITION THE FILE TO PROPER RECORD.
	CALL	Z,RDSEQ1	; AND READ IT AS USUAL (IF NO ERRORS).
	RET
;
;   WRITE TO A RANDOM RECORD.
;
WRITERAN:
	LD	C,0		; SET 'WRITING' FLAG.
	CALL	POSITION	; POSITION THE FILE TO PROPER RECORD.
	CALL	Z,WTSEQ1	; AND WRITE AS USUAL (IF NO ERRORS).
	RET
;
;   COMPUTE THE RANDOM RECORD NUMBER. ENTER WITH (HL) POINTING
; TO A FCB AN (DE) CONTAINS A RELATIVE LOCATION OF A RECORD
; NUMBER. ON EXIT, (C) CONTAINS THE 'R0' BYTE, (B) THE 'R1'
; BYTE, AND (A) THE 'R2' BYTE.
;
;   ON RETURN, THE ZERO FLAG IS SET IF THE RECORD IS WITHIN
; BOUNDS. OTHERWISE, AN OVERFLOW OCCURED.
;
COMPRAND:
	EX	DE,HL		; SAVE FCB POINTER IN (DE).
	ADD	HL,DE		; COMPUTE RELATIVE POSITION OF RECORD #.
	LD	C,(HL)		; GET RECORD NUMBER INTO (BC).
	LD	B,0
	LD	HL,12		; NOW GET EXTENT.
	ADD	HL,DE
	LD	A,(HL)		; COMPUTE (BC)=(RECORD #)+(EXTENT)*128.
	RRCA			; MOVE LOWER BIT INTO BIT 7.
	AND	80H		; AND IGNORE ALL OTHER BITS.
	ADD	A,C		; ADD TO OUR RECORD NUMBER.
	LD	C,A
	LD	A,0		; TAKE CARE OF ANY CARRY.
	ADC	A,B
	LD	B,A
	LD	A,(HL)		; NOW GET THE UPPER BITS OF EXTENT INTO
	RRCA			; BIT POSITIONS 0-3.
	AND	0FH		; AND IGNORE ALL OTHERS.
	ADD	A,B		; ADD THIS IN TO 'R1' BYTE.
	LD	B,A
	LD	HL,14		; GET THE 'S2' BYTE (EXTRA EXTENT).
	ADD	HL,DE
	LD	A,(HL)
	ADD	A,A		; AND SHIFT IT LEFT 4 BITS (BITS 4-7).
	ADD	A,A
	ADD	A,A
	ADD	A,A
	PUSH	AF		; SAVE CARRY FLAG (BIT 0 OF FLAG BYTE).
	ADD	A,B		; NOW ADD EXTRA EXTENT INTO 'R1'.
	LD	B,A
	PUSH	AF		; AND SAVE CARRY (OVERFLOW BYTE 'R2').
	POP	HL		; BIT 0 OF (L) IS THE OVERFLOW INDICATOR.
	LD	A,L
	POP	HL		; AND SAME FOR FIRST CARRY FLAG.
	OR	L		; EITHER ONE OF THESE SET?
	AND	01H		; ONLY CHECK THE CARRY FLAGS.
	RET
;
;   ROUTINE TO SETUP THE FCB (BYTES 'R0', 'R1', 'R2') TO
; REFLECT THE LAST RECORD USED FOR A RANDOM (OR OTHER) FILE.
; THIS READS THE DIRECTORY AND LOOKS AT ALL EXTENTS COMPUTING
; THE LARGERST RECORD NUMBER FOR EACH AND KEEPING THE MAXIMUM
; VALUE ONLY. THEN 'R0', 'R1', AND 'R2' WILL REFLECT THIS
; MAXIMUM RECORD NUMBER. THIS IS USED TO COMPUTE THE SPACE USED
; BY A RANDOM FILE.
;
RANSIZE:LD	C,12		; LOOK THRU DIRECTORY FOR FIRST ENTRY WITH
	CALL	FINDFST		; THIS NAME.
	LD	HL,(PARAMS)	; ZERO OUT THE 'R0, R1, R2' BYTES.
	LD	DE,33
	ADD	HL,DE
	PUSH	HL
	LD	(HL),D		; NOTE THAT (D)=0.
	INC	HL
	LD	(HL),D
	INC	HL
	LD	(HL),D
RANSIZ1:CALL	CKFILPOS	; IS THERE AN EXTENT TO PROCESS?
	JP	Z,RANSIZ3	; NO, WE ARE DONE.
	CALL	FCB2HL		; SET (HL) POINTING TO PROPER FCB IN DIR.
	LD	DE,15		; POINT TO LAST RECORD IN EXTENT.
	CALL	COMPRAND	; AND COMPUTE RANDOM PARAMETERS.
	POP	HL
	PUSH	HL		; NOW CHECK THESE VALUES AGAINST THOSE
	LD	E,A		; ALREADY IN FCB.
	LD	A,C		; THE CARRY FLAG WILL BE SET IF THOSE
	SUB	(HL)		; IN THE FCB REPRESENT A LARGER SIZE THAN
	INC	HL		; THIS EXTENT DOES.
	LD	A,B
	SBC	A,(HL)
	INC	HL
	LD	A,E
	SBC	A,(HL)
	JP	C,RANSIZ2
	LD	(HL),E		; WE FOUND A LARGER (IN SIZE) EXTENT.
	DEC	HL		; STUFF THESE VALUES INTO FCB.
	LD	(HL),B
	DEC	HL
	LD	(HL),C
RANSIZ2:CALL	FINDNXT		; NOW GET THE NEXT EXTENT.
	JP	RANSIZ1		; CONTINUE TIL ALL DONE.
RANSIZ3:POP	HL		; WE ARE DONE, RESTORE THE STACK AND
	RET			; RETURN.
;
;   FUNCTION TO RETURN THE RANDOM RECORD POSITION OF A GIVEN
; FILE WHICH HAS BEEN READ IN SEQUENTIAL MODE UP TO NOW.
;
SETRAN:	LD	HL,(PARAMS)	; POINT TO FCB.
	LD	DE,32		; AND TO LAST USED RECORD.
	CALL	COMPRAND	; COMPUTE RANDOM POSITION.
	LD	HL,33		; NOW STUFF THESE VALUES INTO FCB.
	ADD	HL,DE
	LD	(HL),C		; MOVE 'R0'.
	INC	HL
	LD	(HL),B		; AND 'R1'.
	INC	HL
	LD	(HL),A		; AND LASTLY 'R2'.
	RET
;
;   THIS ROUTINE SELECT THE DRIVE SPECIFIED IN (ACTIVE) AND
; UPDATE THE LOGIN VECTOR AND BITMAP TABLE IF THIS DRIVE WAS
; NOT ALREADY ACTIVE.
;
LOGINDRV:
	LD	HL,(LOGIN)	; GET THE LOGIN VECTOR.
	LD	A,(ACTIVE)	; GET THE DEFAULT DRIVE.
	LD	C,A
	CALL	SHIFTR		; POSITION ACTIVE BIT FOR THIS DRIVE
	PUSH	HL		; INTO BIT 0.
	EX	DE,HL
	CALL	SELECT		; SELECT THIS DRIVE.
	POP	HL
	CALL	Z,SLCTERR	; VALID DRIVE?
	LD	A,L		; IS THIS A NEWLY ACTIVATED DRIVE?
	RRA
	RET	C
	LD	HL,(LOGIN)	; YES, UPDATE THE LOGIN VECTOR.
	LD	C,L
	LD	B,H
	CALL	SETBIT
	LD	(LOGIN),HL	; AND SAVE.
	JP	BITMAP		; NOW UPDATE THE BITMAP.
;
;   FUNCTION TO SET THE ACTIVE DISK NUMBER.
;
SETDSK:	LD	A,(EPARAM)	; GET PARAMETER PASSED AND SEE IF THIS
	LD	HL,ACTIVE	; REPRESENTS A CHANGE IN DRIVES.
	CP	(HL)
	RET	Z
	LD	(HL),A		; YES IT DOES, LOG IT IN.
	JP	LOGINDRV
;
;   THIS IS THE 'AUTO DISK SELECT' ROUTINE. THE FIRSST BYTE
; OF THE FCB IS EXAMINED FOR A DRIVE SPECIFICATION. IF NON
; ZERO THEN THE DRIVE WILL BE SELECTED AND LOGED IN.
;
AUTOSEL:LD	A,0FFH		; SAY 'AUTO-SELECT ACTIVATED'.
	LD	(AUTO),A
	LD	HL,(PARAMS)	; GET DRIVE SPECIFIED.
	LD	A,(HL)
	AND	1FH		; LOOK AT LOWER 5 BITS.
	DEC	A		; ADJUST FOR (1=A, 2=B) ETC.
	LD	(EPARAM),A	; AND SAVE FOR THE SELECT ROUTINE.
	CP	1EH		; CHECK FOR 'NO CHANGE' CONDITION.
	JP	NC,AUTOSL1	; YES, DON'T CHANGE.
	LD	A,(ACTIVE)	; WE MUST CHANGE, SAVE CURRENTLY ACTIVE
	LD	(OLDDRV),A	; DRIVE.
	LD	A,(HL)		; AND SAVE FIRST BYTE OF FCB ALSO.
	LD	(AUTOFLAG),A	; THIS MUST BE NON-ZERO.
	AND	0E0H		; WHATS THIS FOR (BITS 6,7 ARE USED FOR
	LD	(HL),A		; SOMETHING)?
	CALL	SETDSK		; SELECT AND LOG IN THIS DRIVE.
AUTOSL1:LD	A,(USERNO)	; MOVE USER NUMBER INTO FCB.
	LD	HL,(PARAMS)	; (* UPPER HALF OF FIRST BYTE *)
	OR	(HL)
	LD	(HL),A
	RET			; AND RETURN (ALL DONE).
;
;   FUNCTION TO RETURN THE CURRENT CP/M VERSION NUMBER.
;
GETVER:	LD	A,022H		; VERSION 2.2
	JP	SETSTAT
;
;   FUNCTION TO RESET THE DISK SYSTEM.
;
RSTDSK:	LD	HL,0		; CLEAR WRITE PROTECT STATUS AND LOG
	LD	(WRTPRT),HL	; IN VECTOR.
	LD	(LOGIN),HL
	XOR	A		; SELECT DRIVE 'A'.
	LD	(ACTIVE),A
	LD	HL,TBUFF	; SETUP DEFAULT DMA ADDRESS.
	LD	(USERDMA),HL
	CALL	DEFDMA
	JP	LOGINDRV	; NOW LOG IN DRIVE 'A'.
;
;   FUNCTION TO OPEN A SPECIFIED FILE.
;
OPENFIL:CALL	CLEARS2		; CLEAR 'S2' BYTE.
	CALL	AUTOSEL		; SELECT PROPER DISK.
	JP	OPENIT		; AND OPEN THE FILE.
;
;   FUNCTION TO CLOSE A SPECIFIED FILE.
;
CLOSEFIL:
	CALL	AUTOSEL		; SELECT PROPER DISK.
	JP	CLOSEIT		; AND CLOSE THE FILE.
;
;   FUNCTION TO RETURN THE FIRST OCCURENCE OF A SPECIFIED FILE
; NAME. IF THE FIRST BYTE OF THE FCB IS '?' THEN THE NAME WILL
; NOT BE CHECKED (GET THE FIRST ENTRY NO MATTER WHAT).
;
GETFST:	LD	C,0		; PREPARE FOR SPECIAL SEARCH.
	EX	DE,HL
	LD	A,(HL)		; IS FIRST BYTE A '?'?
	CP	'?'
	JP	Z,GETFST1	; YES, JUST GET VERY FIRST ENTRY (ZERO LENGTH MATCH).
	CALL	SETEXT		; GET THE EXTENSION BYTE FROM FCB.
	LD	A,(HL)		; IS IT '?'? IF YES, THEN WE WANT
	CP	'?'		; AN ENTRY WITH A SPECIFIC 'S2' BYTE.
	CALL	NZ,CLEARS2	; OTHERWISE, LOOK FOR A ZERO 'S2' BYTE.
	CALL	AUTOSEL		; SELECT PROPER DRIVE.
	LD	C,15		; COMPARE BYTES 0-14 IN FCB (12&13 EXCLUDED).
GETFST1:CALL	FINDFST		; FIND AN ENTRY AND THEN MOVE IT INTO
	JP	MOVEDIR		; THE USERS DMA SPACE.
;
;   FUNCTION TO RETURN THE NEXT OCCURENCE OF A FILE NAME.
;
GETNXT:	LD	HL,(SAVEFCB)	; RESTORE POINTERS. NOTE THAT NO
	LD	(PARAMS),HL	; OTHER DBOS CALLS ARE ALLOWED.
	CALL	AUTOSEL		; NO ERROR WILL BE RETURNED, BUT THE
	CALL	FINDNXT		; RESULTS WILL BE WRONG.
	JP	MOVEDIR
;
;   FUNCTION TO DELETE A FILE BY NAME.
;
DELFILE:CALL	AUTOSEL		; SELECT PROPER DRIVE.
	CALL	ERAFILE		; ERASE THE FILE.
	JP	STSTATUS	; SET STATUS AND RETURN.
;
;   FUNCTION TO EXECUTE A SEQUENTIAL READ OF THE SPECIFIED
; RECORD NUMBER.
;
READSEQ:CALL	AUTOSEL		; SELECT PROPER DRIVE THEN READ.
	JP	RDSEQ
;
;   FUNCTION TO WRITE THE NET SEQUENTIAL RECORD.
;
WRTSEQ:	CALL	AUTOSEL		; SELECT PROPER DRIVE THEN WRITE.
	JP	WTSEQ
;
;   CREATE A FILE FUNCTION.
;
FCREATE:CALL	CLEARS2		; CLEAR THE 'S2' BYTE ON ALL CREATES.
	CALL	AUTOSEL		; SELECT PROPER DRIVE AND GET THE NEXT
	JP	GETEMPTY	; EMPTY DIRECTORY SPACE.
;
;   FUNCTION TO RENAME A FILE.
;
RENFILE:CALL	AUTOSEL		; SELECT PROPER DRIVE AND THEN SWITCH
	CALL	CHGNAMES	; FILE NAMES.
	JP	STSTATUS
;
;   FUNCTION TO RETURN THE LOGIN VECTOR.
;
GETLOG:	LD	HL,(LOGIN)
	JP	GETPRM1
;
;   FUNCTION TO RETURN THE CURRENT DISK ASSIGNMENT.
;
GETCRNT:LD	A,(ACTIVE)
	JP	SETSTAT
;
;   FUNCTION TO SET THE DMA ADDRESS.
;
PUTDMA:	EX	DE,HL
	LD	(USERDMA),HL	; SAVE IN OUR SPACE AND THEN GET TO
	JP	DEFDMA		; THE BIOS WITH THIS ALSO.
;
;   FUNCTION TO RETURN THE ALLOCATION VECTOR.
;
GETALOC:LD	HL,(ALOCVECT)
	JP	GETPRM1
;
;   FUNCTION TO RETURN THE READ-ONLY STATUS VECTOR.
;
GETROV:	LD	HL,(WRTPRT)
	JP	GETPRM1
;
;   FUNCTION TO SET THE FILE ATTRIBUTES (READ-ONLY, SYSTEM).
;
SETATTR:CALL	AUTOSEL		; SELECT PROPER DRIVE THEN SAVE ATTRIBUTES.
	CALL	SAVEATTR
	JP	STSTATUS
;
;   FUNCTION TO RETURN THE ADDRESS OF THE DISK PARAMETER BLOCK
; FOR THE CURRENT DRIVE.
;
GETPARM:LD	HL,(DISKPB)
GETPRM1:LD	(STATUS),HL
	RET
;
;   FUNCTION TO GET OR SET THE USER NUMBER. IF (E) WAS (FF)
; THEN THIS IS A REQUEST TO RETURN THE CURRENT USER NUMBER.
; ELSE SET THE USER NUMBER FROM (E).
;
GETUSER:LD	A,(EPARAM)	; GET PARAMETER.
	CP	0FFH		; GET USER NUMBER?
	JP	NZ,SETUSER
	LD	A,(USERNO)	; YES, JUST DO IT.
	JP	SETSTAT
SETUSER:AND	1FH		; NO, WE SHOULD SET IT INSTEAD. KEEP LOW
	LD	(USERNO),A	; BITS (0-4) ONLY.
	RET
;
;   FUNCTION TO READ A RANDOM RECORD FROM A FILE.
;
RDRANDOM:
	CALL	AUTOSEL		; SELECT PROPER DRIVE AND READ.
	JP	READRAN
;
;   FUNCTION TO COMPUTE THE FILE SIZE FOR RANDOM FILES.
;
WTRANDOM:
	CALL	AUTOSEL		; SELECT PROPER DRIVE AND WRITE.
	JP	WRITERAN
;
;   FUNCTION TO COMPUTE THE SIZE OF A RANDOM FILE.
;
FILESIZE:
	CALL	AUTOSEL		; SELECT PROPER DRIVE AND CHECK FILE LENGTH
	JP	RANSIZE
;
;   FUNCTION #37. THIS ALLOWS A PROGRAM TO LOG OFF ANY DRIVES.
; ON ENTRY, SET (DE) TO CONTAIN A WORD WITH BITS SET FOR THOSE
; DRIVES THAT ARE TO BE LOGGED OFF. THE LOG-IN VECTOR AND THE
; WRITE PROTECT VECTOR WILL BE UPDATED. THIS MUST BE A M/PM
; SPECIAL FUNCTION.
;
LOGOFF:	LD	HL,(PARAMS)	; GET DRIVES TO LOG OFF.
	LD	A,L		; FOR EACH BIT THAT IS SET, WE WANT
	CPL			; TO CLEAR THAT BIT IN (LOGIN)
	LD	E,A		; AND (WRTPRT).
	LD	A,H
	CPL
	LD	HL,(LOGIN)	; RESET THE LOGIN VECTOR.
	AND	H
	LD	D,A
	LD	A,L
	AND	E
	LD	E,A
	LD	HL,(WRTPRT)
	EX	DE,HL
	LD	(LOGIN),HL	; AND SAVE.
	LD	A,L		; NOW DO THE WRITE PROTECT VECTOR.
	AND	E
	LD	L,A
	LD	A,H
	AND	D
	LD	H,A
	LD	(WRTPRT),HL	; AND SAVE. ALL DONE.
	RET
;
;   GET HERE TO RETURN TO THE USER.
;
GOBACK:	LD	A,(AUTO)	; WAS AUTO SELECT ACTIVATED?
	OR	A
	JP	Z,GOBACK1
	LD	HL,(PARAMS)	; YES, BUT WAS A CHANGE MADE?
	LD	(HL),0		; (* RESET FIRST BYTE OF FCB *)
	LD	A,(AUTOFLAG)
	OR	A
	JP	Z,GOBACK1
	LD	(HL),A		; YES, RESET FIRST BYTE PROPERLY.
	LD	A,(OLDDRV)	; AND GET THE OLD DRIVE AND SELECT IT.
	LD	(EPARAM),A
	CALL	SETDSK
GOBACK1:LD	HL,(USRSTACK)	; RESET THE USERS STACK POINTER.
	LD	SP,HL
	LD	HL,(STATUS)	; GET RETURN STATUS.
	LD	A,L		; FORCE VERSION 1.4 COMPATABILITY.
	LD	B,H
	RET			; AND GO BACK TO USER.
;
;   FUNCTION #40. THIS IS A SPECIAL ENTRY TO DO RANDOM I/O.
; FOR THE CASE WHERE WE ARE WRITING TO UNUSED DISK SPACE, THIS
; SPACE WILL BE ZEROED OUT FIRST. THIS MUST BE A M/PM SPECIAL
; PURPOSE FUNCTION, BECAUSE WHY WOULD ANY NORMAL PROGRAM EVEN
; CARE ABOUT THE PREVIOUS CONTENTS OF A SECTOR ABOUT TO BE
; WRITTEN OVER.
;
WTSPECL:CALL	AUTOSEL		; SELECT PROPER DRIVE.
	LD	A,2		; USE SPECIAL WRITE MODE.
	LD	(MODE),A
	LD	C,0		; SET WRITE INDICATOR.
	CALL	POSITN1		; POSITION THE FILE.
	CALL	Z,WTSEQ1	; AND WRITE (IF NO ERRORS).
	RET
;
;**************************************************************
;*
;*     BDOS DATA STORAGE POOL.
;*
;**************************************************************
;
EMPTYFCB:
	.DB 0E5H		; EMPTY DIRECTORY SEGMENT INDICATOR.
WRTPRT:	.DW 0			; WRITE PROTECT STATUS FOR ALL 16 DRIVES.
LOGIN:	.DW 0			; DRIVE ACTIVE WORD (1 BIT PER DRIVE).
USERDMA:.DW 080H		; USER'S DMA ADDRESS (DEFAULTS TO 80H).
;
;   SCRATCH AREAS FROM PARAMETER BLOCK.
;
SCRATCH1:
	.DW 0			; RELATIVE POSITION WITHIN DIR SEGMENT FOR FILE (0-3).
SCRATCH2:
	.DW 0			; LAST SELECTED TRACK NUMBER.
SCRATCH3:
	.DW 0			; LAST SELECTED SECTOR NUMBER.
;
;   DISK STORAGE AREAS FROM PARAMETER BLOCK.
;
DIRBUF:	.DW 0			; ADDRESS OF DIRECTORY BUFFER TO USE.
DISKPB:	.DW 0			; CONTAINS ADDRESS OF DISK PARAMETER BLOCK.
CHKVECT:.DW 0			; ADDRESS OF CHECK VECTOR.
ALOCVECT:
	.DW 0			; ADDRESS OF ALLOCATION VECTOR (BIT MAP).
;
;   PARAMETER BLOCK RETURNED FROM THE BIOS.
;
SECTORS:.DW 0			; SECTORS PER TRACK FROM BIOS.
BLKSHFT:.DB 0			; BLOCK SHIFT.
BLKMASK:.DB 0			; BLOCK MASK.
EXTMASK:.DB 0			; EXTENT MASK.
DSKSIZE:.DW 0			; DISK SIZE FROM BIOS (NUMBER OF BLOCKS-1).
DIRSIZE:.DW 0			; DIRECTORY SIZE.
ALLOC0:	.DW 0			; STORAGE FOR FIRST BYTES OF BIT MAP (DIR SPACE USED).
ALLOC1:	.DW 0
OFFSET:	.DW 0			; FIRST USABLE TRACK NUMBER.
XLATE:	.DW 0			; SECTOR TRANSLATION TABLE ADDRESS.
CLOSEFLG:
	.DB 0			; CLOSE FLAG (=0FFH IS EXTENT WRITTEN OK).
RDWRTFLG:
	.DB 0			; READ/WRITE FLAG (0FFH=READ, 0=WRITE).
FNDSTAT:.DB 0			; FILENAME FOUND STATUS (0=FOUND FIRST ENTRY).
MODE:	.DB 0			; I/O MODE SELECT (0=RANDOM, 1=SEQUENTIAL, 2=SPECIAL RANDOM).
EPARAM:	.DB 0			; STORAGE FOR REGISTER (E) ON ENTRY TO BDOS.
RELBLOCK:
	.DB 0			; RELATIVE POSITION WITHIN FCB OF BLOCK NUMBER WRITTEN.
COUNTER:.DB 0			; BYTE COUNTER FOR DIRECTORY NAME SEARCHES.
SAVEFCB:.DW 0,0			; SAVE SPACE FOR ADDRESS OF FCB (FOR DIRECTORY SEARCHES).
BIGDISK:.DB 0			; IF =0 THEN DISK IS > 256 BLOCKS LONG.
AUTO:	.DB 0			; IF NON-ZERO, THEN AUTO SELECT ACTIVATED.
OLDDRV:	.DB 0			; ON AUTO SELECT, STORAGE FOR PREVIOUS DRIVE.
AUTOFLAG:
	.DB 0			; IF NON-ZERO, THEN AUTO SELECT CHANGED DRIVES.
SAVNXT:	.DB 0			; STORAGE FOR NEXT RECORD NUMBER TO ACCESS.
SAVEXT:	.DB 0			; STORAGE FOR EXTENT NUMBER OF FILE.
SAVNREC:.DW 0			; STORAGE FOR NUMBER OF RECORDS IN FILE.
BLKNMBR:.DW 0			; BLOCK NUMBER (PHYSICAL SECTOR) USED WITHIN A FILE OR LOGICAL SEC
LOGSECT:.DW 0			; STARTING LOGICAL (128 BYTE) SECTOR OF BLOCK (PHYSICAL SECTOR).
FCBPOS:	.DB 0			; RELATIVE POSITION WITHIN BUFFER FOR FCB OF FILE OF INTEREST.
FILEPOS:.DW 0			; FILES POSITION WITHIN DIRECTORY (0 TO MAX ENTRIES -1).
;
;   DISK DIRECTORY BUFFER CHECKSUM BYTES. ONE FOR EACH OF THE
; 16 POSSIBLE DRIVES.
;
CKSUMTBL:
	.DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
;
;   EXTRA SPACE ?
;
	.DB 0,0,0,0
;
;**************************************************************
;*
;*        B I O S   J U M P   T A B L E
;*
;**************************************************************
;
;BOOT	JMP	0	;NOTE WE USE FAKE DESTINATIONS
;WBOOT	JMP	0
;CONST	JMP	0
;CONIN	JMP	0
;CONOUT	JMP	0
;LIST	JMP	0
;PUNCH	JMP	0
;READER	JMP	0
;HOME	JMP	0
;SELDSK	JMP	0
;SETTRK	JMP	0
;SETSEC	JMP	0
;SETDMA	JMP	0
;READ	JMP	0
;WRITE	JMP	0
;PRSTAT	JMP	0
;SECTRN	JMP	0
;
;*
;******************   E N D   O F   C P / M   *****************
;*

;	.END


;
;**************************************************************
;*
;*        C B I O S  f o r
;*
;*  T e s t   P r o t o t y p e
;*
;*  by Andrew Lynch, with input from many sources
;*
;**************************************************************
;
;	SKELETAL CBIOS FOR FIRST LEVEL OF CP/M 2.0 ALTERATION
;             WITH MODS FOR CP/M  ROMDISK AND RAMDISK.
;
;             ENTIRELY IN 8080 MNEUMONICS (SO ASM CAN BE USED)
;             BUT ASSUMES A Z80! (remove)
;
;MSIZE	.EQU	20			;CP/M VERSION MEMORY SIZE IN KILOBYTES
;MSIZE	.EQU	62			;CP/M VERSION MEMORY SIZE IN KILOBYTES
; MEM defined in CPM22 above, line 0015
MSIZE:	.EQU MEM		; CP/M VERSION MEMORY SIZE IN KILOBYTES

;
;	"BIAS" IS ADDRESS OFFSET FROM 3400H FOR MEMORY SYSTEMS
;	THAN 16K (REFERRED TO AS "B" THROUGHOUT THE TEXT).
;
BIAS:	.EQU (MSIZE-20)*1024
CCP:	.EQU 3400H+BIAS		; BASE OF CCP
BDOS:	.EQU CCP+806H		; BASE OF BDOS
BIOS:	.EQU CCP+1600H		; BASE OF BIOS
CDISK:	.EQU 0004H		; CURRENT DISK NUMBER 0=A,...,15=P
; IOBYTE already defined in CPM22 above, line 0017
;IOBYTE	.EQU	0003H			;INTEL I/O BYTE
;
;	CONSTANTS

END:	.EQU $FF
;CR		.EQU	$0D
;LF		.EQU	$0A

; TEST PROTOTYPE SPECIFIC HARDWARE IO PORT ADDRESSES AND MEMORY LOCATIONS

UART:	.EQU $68		; BASE IO ADDRESS OF UART
MPCL_RAM:	.EQU $78		; BASE IO ADDRESS OF RAM MEMORY PAGER CONFIGURATION LATCH
MPCL_ROM:	.EQU $7C		; BASE IO ADDRESS OF ROM MEMORY PAGER CONFIGURATION LATCH

ROMSTART_MON:	.EQU $0100		; WHERE THE MONITOR IS STORED IN ROM
RAMTARG_MON:	.EQU $F800		; WHERE THE MONITOR STARTS IN RAM (ENTRY POINT)
MOVSIZ_MON:	.EQU $0800		; MONITOR IS 2KB IN LENGTH

ROMSTART_CPM:	.EQU $0900		; WHERE THE CCP+BDOS+BIOS IS STORED IN ROM
RAMTARG_CPM:	.EQU $D400		; WHERE THE CCP+BDOS+BIOS STARTS IN RAM (ENTRY POINT)
MOVSIZ_CPM:	.EQU $1EFF		; CCP, BDOS, + BIOS IS 6-7KB IN LENGTH
CCPSIZ_CPM:	.EQU $0800		; CCP $800 BYTES IN LENGTH

; IDE REGISTER		IO PORT		; FUNCTION
IDELO:		.EQU $20		; DATA PORT (LOW BYTE)
IDEERR:		.EQU $21		; READ: ERROR REGISTER; WRITE: PRECOMP
IDESECTC:	.EQU $22		; SECTOR COUNT
IDESECTN:	.EQU $23		; SECTOR NUMBER
IDECYLLO:	.EQU $24		; CYLINDER LOW
IDECYLHI:	.EQU $25		; CYLINDER HIGH
IDEHEAD:	.EQU $26		; DRIVE/HEAD
IDESTTS:	.EQU $27		; READ: STATUS; WRITE: COMMAND
IDEHI:		.EQU $28		; DATA PORT (HIGH BYTE)
IDECTRL:	.EQU $2E		; READ: ALTERNATIVE STATUS; WRITE; DEVICE CONTROL
IDEADDR:	.EQU $2F		; DRIVE ADDRESS (READ ONLY)

;	.ORG	BIOS			;ORIGIN OF THIS PROGRAM
NSECTS:	.EQU ($-CCP)/128	; WARM START SECTOR COUNT

;
;	JUMP VECTOR FOR INDIVIDUAL SUBROUTINES
	JP	BOOT		; COLD START
WBOOTE:	JP	WBOOT		; WARM START
	JP	CONST		; CONSOLE STATUS
	JP	CONIN		; CONSOLE CHARACTER IN
	JP	CONOUT		; CONSOLE CHARACTER OUT
;	JMP	LIST			;LIST CHARACTER OUT (NULL ROUTINE)
LIST:	RET
	NOP
	NOP
;	JMP	PUNCH			;PUNCH CHARACTER OUT (NULL ROUTINE)
PUNCH:	RET
	NOP
	NOP
;	JMP	READER			;READER CHARACTER OUT (NULL ROUTINE)
READER:	RET
	NOP
	NOP
	JP	HOME		; MOVE HEAD TO HOME POSITION
	JP	SELDSK		; SELECT DISK
	JP	SETTRK		; SET TRACK NUMBER
	JP	SETSEC		; SET SECTOR NUMBER
	JP	SETDMA		; SET DMA ADDRESS
	JP	READ		; READ DISK
	JP	WRITE		; WRITE DISK
;	JMP	LISTST			;RETURN LIST STATUS (NULL ROUTINE)
LISTST:	RET
	NOP
	NOP
	JP	SECTRN		; SECTOR TRANSLATE
;
;   FIXED DATA TABLES FOR ALL DRIVES
;   0= ROMDISK, 1=RAMDISK, 2=HDPART2, 3=ATAPI, AND 4=HDPART4
;   5= 1MBDISK

	.IF CONDSWAPAB

		;   DISK PARAMETER HEADER FOR DISK 00
DPBASE:		.DW 00000,0000H
		.DW 0000H,0000H
		.DW DIRBF,DPBLK0
		.DW CHK00,ALL00
		;   DISK PARAMETER HEADER FOR DISK 01
		.DW 0000,0000H
		.DW 0000H,0000H
		.DW DIRBF,DPBLK1
		.DW CHK01,ALL01

	.ELSE

		;   DISK PARAMETER HEADER FOR DISK 01
DPBASE:		.DW 0000,0000H
		.DW 0000H,0000H
		.DW DIRBF,DPBLK1
		.DW CHK01,ALL01

		;   DISK PARAMETER HEADER FOR DISK 00
		.DW 00000,0000H
		.DW 0000H,0000H
		.DW DIRBF,DPBLK0
		.DW CHK00,ALL00

	.ENDIF

;   DISK PARAMETER HEADER FOR DISK 02
	.DW 0000,0000H
	.DW 0000H,0000H
	.DW DIRBF,DPBLK2
	.DW CHK02,ALL02
;   DISK PARAMETER HEADER FOR DISK 03
	.DW 0000,0000H
	.DW 0000H,0000H
	.DW DIRBF,DPBLK3
	.DW CHK03,ALL03
;   DISK PARAMETER HEADER FOR DISK 04
	.DW 0000,0000H
	.DW 0000H,0000H
	.DW DIRBF,DPBLK4
	.DW CHK04,ALL04
;   DISK PARAMETER HEADER FOR DISK 05
	.DW 0000,0000H
	.DW 0000H,0000H
	.DW DIRBF,DPBLK5
	.DW CHK05,ALL05
;
;DPBLK0:	;DISK PARAMETER BLOCK (ROMDISK 1MB) ** RESERVED FOR FUTURE EXPANSION *
;	SPT	.DW 	256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
;	BSH	.DB 	2 		; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
;	BLM 	.DB 	15 		; PART OF THE ALLOCATION BLOCK SIZE MATH
;	EXM 	.DB 	1 		; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
;	DSM 	.DW 	495 		; BLOCKSIZE [2048] * NUMBER OF BLOCKS +1 =DRIVE SIZE
;	DRM 	.DW 	255 		; NUMBER OF DIRECTORY ENTRIES
;	AL0 	.DB 	11110000  	; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
;	AL1 	.DB 	00000000  	; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
;	CKS 	.DW 	0 	  	; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
;	OFF 	.DW 	1 	  	; 1 TRACK RESERVED [FIRST 32K OF ROM]


DPBLK0:				; DISK PARAMETER BLOCK (ROMDISK 32KB WITH 16 2K TRACKS, 22K usable)
SPT_0:	.DW  16			; 16 SECTORS OF 128 BYTES PER 2K TRACK
BSH_0:	.DB  3			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_0:	.DB  7			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_0:	.DB  0			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_0:	.DW  31			; BLOCKSIZE [1024] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
DRM_0:	.DW  31			; NUMBER OF DIRECTORY ENTRIES
AL0_0:	.DB  10000000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_0:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_0:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_0:	.DW  5			; FIRST 5 TRACKS TRACKS RESERVED (10K FOR SYSTEM)
				; SYSTEM IS ROM LOADER, CCP, BDOS, CBIOS, AND MONITOR
				; 
				; IMPORTANT NOTE: TRACKS $00 - $04 OF 2K BYTES
				; EACH ARE MARKED WITH THE OFF_0 SET TO 5 AS 
				; SYSTEM TRACKS. USABLE ROM DRIVE SPACE
				; STARTING AFTER THE FIFTH TRACK (IE, TRACK $05)
				; MOST LIKELY FIX TO THIS IS PLACING A DUMMY
				; FIRST 10K ROM CONTAINS THE ROM LOADER, MONITOR,
				; CCP, BDOS, BIOS, ETC (5 TRACKS * 2K EACH)

DPBLK1:				; DISK PARAMETER BLOCK (RAMDISK 512K, 448K usable)
SPT_1:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_1:	.DB  4			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_1:	.DB  15			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_1:	.DB  1			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_1:	.DW  225		; BLOCKSIZE [2048] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
DRM_1:	.DW  255		; NUMBER OF DIRECTORY ENTRIES
AL0_1:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_1:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_1:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_1:	.DW  1			; 1 TRACK RESERVED [FIRST 32K OF RAM]

DPBLK2:				; DISK PARAMETER BLOCK (IDE HARD DISK 8MB)
SPT_2:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_2:	.DB  5			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_2:	.DB  31			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_2:	.DB  1			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_2:	.DW  2017		; BLOCKSIZE [4096] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
				; HD PARTITION 2 IS 16128 SECTORS LONG
				; AT 512 BYTES EACH WHICH IS 
				; 2016 BLOCKS AT 4096 BYTES A PIECE.
DRM_2:	.DW  511		; NUMBER OF DIRECTORY ENTRIES
AL0_2:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_2:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_2:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_2:	.DW  1			; 1 TRACK (32K) RESERVED FOR SYSTEM

DPBLK3:				; DISK PARAMETER BLOCK (ATAPI DRIVE 8MB)
SPT_3:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_3:	.DB  5			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_3:	.DB  31			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_3:	.DB  1			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_3:	.DW  2017		; BLOCKSIZE [4096] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
				; ATAPI DISK IS 16128 SECTORS LONG
				; AT 512 BYTES EACH WHICH IS 
				; 2016 BLOCKS AT 4096 BYTES A PIECE.
DRM_3:	.DW  511		; NUMBER OF DIRECTORY ENTRIES
AL0_3:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_3:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_3:	.DW  128		; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_3:	.DW  1			; 1 TRACK (32K) RESERVED FOR SYSTEM

DPBLK4:				; DISK PARAMETER BLOCK (IDE HARD DISK 1024K)
SPT_4:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_4:	.DB  4			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_4:	.DB  15			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_4:	.DB  0			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
DSM_4:	.DW  497		; BLOCKSIZE [2048] * NUMBER OF BLOCKS + 1 = DRIVE SIZE
				; HD PARTITION 4 IS 4032 SECTORS LONG
				; AT 512 BYTES EACH WHICH IS 
				; 1008 BLOCKS AT 2048 BYTES A PIECE.
				; NOT USING ALL OF THE AVAILABLE SECTORS SINCE THIS
				; DRIVE IS INTENDED TO EMULATE A ROM DRIVE AND COPIED
				; INTO A ROM IN THE FUTURE.
DRM_4:	.DW  255		; NUMBER OF DIRECTORY ENTRIES
AL0_4:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_4:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_4:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_4:	.DW  1			; 1 TRACK RESERVED [FIRST 32K OF PARTITION]

;
DPBLK5:				; DISK PARAMETER BLOCK (ROMDISK 1MB) ** RESERVED FOR FUTURE EXPANSION 
SPT_5:	.DW  256		; 256 SECTORS OF 128 BYTES PER 32K TRACK
BSH_5:	.DB  4			; BLOCK SHIFT FACTOR (SIZE OF ALLOCATION BLOCK)
BLM_5:	.DB  15			; PART OF THE ALLOCATION BLOCK SIZE MATH
EXM_5:	.DB  0			; DEFINES SIZE OF EXTENT (DIRECTORY INFO)
;DSM_5: 	.DW 	495 			; BLOCKSIZE [2048] * NUMBER OF BLOCKS +1 =DRIVE SIZE
DSM_5:	.DW  511		; BLOCKSIZE [2048] * NUMBER OF BLOCKS +1 =DRIVE SIZE
DRM_5:	.DW  255		; NUMBER OF DIRECTORY ENTRIES
AL0_5:	.DB  11110000		; BIT MAP OF SPACE ALLOCATED TO DIRECTORY
AL1_5:	.DB  00000000		; DIRECTORY CAN HAVE UP TO 16 BLOCKS ALLOCATED
CKS_5:	.DW  0			; SIZE OF DIRECTORY CHECK [0 IF NON REMOVEABLE]
OFF_5:	.DW  1			; 1 TRACK RESERVED [FIRST 32K OF ROM]

;
;	END OF FIXED TABLES
;
;	INDIVIDUAL SUBROUTINES TO PERFORM EACH FUNCTION

BOOT:				; SIMPLEST CASE IS TO JUST PERFORM PARAMETER INITIALIZATION
	LD	A,$80		; LOAD VALUE TO SWITCH OUT ROM
	OUT	(MPCL_ROM),A	; SWITCH OUT ROM, BRING IN LOWER 32K RAM PAGE

	LD	A,$00		; ENSURE LOWEST RAM PAGE SELECTED
	OUT	(MPCL_RAM),A	; BRING IN LOWEST 32K RAM PAGE

	XOR	A		; ZERO IN THE ACCUM
	LD	(IOBYTE),A	; CLEAR THE IOBYTE
	LD	(CDISK),A	; SELECT DISK ZERO
	
	CALL	IDE_SOFT_RESET	; RESET THE IDE HARD DISK

	LD	HL,TXT_STARTUP_MSG ; PRINT STARTUP MESSAGE
	CALL	PRTMSG

	JP	GOCPM		; INITIALIZE AND GO TO CP/M

;
WBOOT:				; SIMPLEST CASE IS TO READ THE DISK UNTIL ALL SECTORS LOADED
				; WITH A ROMDISK WE SELECT THE ROM AND THE CORRECT PAGE [0]
				; THEN COPY THE CP/M IMAGE (CCP, BDOS, BIOS, MONITOR) TO HIGH RAM
				; LOAD ADDRESS.
				; FOR Z80 IT LOOKS LIKE THIS... USING 8080 NEMONICS

	DI			; DISABLE INTERRUPT
	LD	SP,80H		; USE SPACE BELOW BUFFER FOR STACK
	IM	1			; SET INTERRUPT MODE 1

	XOR	A		; CHEAP ZERO IN ACC
	OUT	(MPCL_ROM),A	; SEND 0 TO ROM MAP PORT (SWITCH IN LOWER 32K ROM PAGE)

	XOR	A		; CHEAP ZERO IN ACC
	OUT	(MPCL_RAM),A	; SEND 0 TO RAM MAP PORT (SELECT LOWEST RAM PAGE)


; REMOVED FOR DISK BOOT
;
;	.IF 	CONDWBOOT
;		LD	HL,ROMSTART_MON	; WHERE IN ROM MONITOR IS STORED (FIRST BYTE)
;		LD	DE,RAMTARG_MON	; WHERE IN RAM TO MOVE MONITOR TO (FIRST BYTE)
;		LD	BC,MOVSIZ_MON	; NUMBER OF BYTES TO MOVE FROM ROM TO RAM
;		LDIR
;
;		LD	HL,ROMSTART_CPM	; WHERE IN ROM CP/M IS STORED (FIRST BYTE)
;		LD	DE,RAMTARG_CPM	; WHERE IN RAM TO MOVE MONITOR TO (FIRST BYTE)
;		LD	BC,MOVSIZ_CPM	; NUMBER OF BYTES TO MOVE FROM ROM TO RAM
;		LDIR
;	.ELSE
;		LD 	HL,0F2F0H	;move from F2F0 the 5 TSR jumps
;		LD 	DE,0FFF0H	;move to FFF0
;		LD	BC,16		; 16 BYTES
;		LDIR
;
;		LD	HL,ROMSTART_CPM	; WHERE IN ROM CP/M IS STORED (FIRST BYTE)
;		LD	DE,RAMTARG_CPM	; WHERE IN RAM TO MOVE MONITOR TO (FIRST BYTE)
;		LD	BC,MOVSIZ_CPM	; NUMBER OF BYTES TO MOVE FROM ROM TO RAM
;		LDIR
;
;		LD	HL,0FFF0H	; move from FFF0
;		LD	DE,0F2F0H	; move back to F2F0
;		LD 	BC,16		; 16 bytes
;		LDIR
;	.ENDIF

		LD	HL,ROMSTART_CPM	; WHERE IN ROM CP/M IS STORED (FIRST BYTE)
		LD	DE,RAMTARG_CPM	; WHERE IN RAM TO MOVE MONITOR TO (FIRST BYTE)
		LD	BC,CCPSIZ_CPM	; NUMBER OF BYTES TO MOVE FROM ROM TO RAM
		LDIR


	EI			; ENABLE INTERRUPTS (ACCESS TO MONITOR WHILE
				; CP/M RUNNING)
	LD	A,$80		; LOAD VALUE TO SWITCH OUT ROM
	OUT	(MPCL_ROM),A	; SWITCH OUT ROM, BRING IN LOWER 32K RAM PAGE

	XOR	A		; CHEAP ZERO IN ACC
	OUT	(MPCL_RAM),A	; SEND 0 TO RAM MAP PORT (SELECT LOWEST RAM PAGE)

	CALL	IDE_SOFT_RESET	; RESET THE IDE HARD DISK

	.IF CONDSUPERSUB
		; do nothing for original code
	.ELSE
		; clear the autosub buffer, do on a warm boot
		XOR	A
		LD	(INBUFF+1),A	; SECOND BYTE IS ACTUAL LENGTH.

	.ENDIF

	LD	HL,TXT_STARTUP_MSG ; PRINT STARTUP MESSAGE
	CALL	PRTMSG
				; FALL THROUGH TO GOCPM ROUTINE

;	END OF LOAD OPERATION, SET PARAMETERS AND GO TO CP/M
GOCPM:
				; CPU RESET HANDLER
	LD	A,0C3H		; C3 IS A JMP INSTRUCTION
	LD	($0000),A	; FOR JMP TO WBOOT
	LD	HL,WBOOTE	; WBOOT ENTRY POINT
	LD	(1),HL		; SET ADDRESS FIELD FOR JMP AT 0
;
				; CPU INTERRUPT HANDLER
	LD	A,0C3H		; C3 IS A JMP INSTRUCTION
	LD	($0038),A	; FOR JMP TO WBOOT
	LD	HL,WBOOTE	; WBOOT ENTRY POINT
	LD	(1),HL		; SET ADDRESS FIELD FOR JMP AT 0
;
	LD	(5),A		; FOR JMP TO BDOS
	LD	HL,BDOS		; BDOS ENTRY POINT
	LD	(6),HL		; ADDRESS FIELD OF JUMP AT 5 TO BDOS
;
	LD	BC,80H		; DEFAULT DMA ADDRESS IS 80H
	CALL	SETDMA
;
	LD	A,(CDISK)	; GET CURRENT DISK NUMBER
	LD	C,A		; SEND TO THE CCP
	JP	CCP		; GO TO CP/M FOR FURTHER PROCESSING
;
;
;	SIMPLE I/O HANDLERS (MUST BE FILLED IN BY USER)
;	IN EACH CASE, THE ENTRY POINT IS PROVIDED, WITH SPACE RESERVED
;	TO INSERT YOUR OWN CODE
;

	.IF CONDKEYBOARD

CONST:					; CONSOLE STATUS, RETURN 0FFH IF CHARACTER READY, 00H IF NOT
		IN	A,(UART + $05)	; READ LINE STATUS REGISTER (UART5 = $68 + $05)
		AND	$01		; TEST IF DATA IN RECEIVE BUFFER
					; IS THERE A CHAR READY? 0=NO, 1=YES
		JP	Z,NOT_READY
		LD	A,$FF		; YES, PUT $FF IN A AND RETURN
NOT_READY:
					; NO, LEAVE $00 IN A AND RETURN
		RET
CONIN:					; CONSOLE CHARACTER INTO REGISTER A

		CALL	CONST		; IS A CHAR READY TO BE READ FROM UART?
		CP	$00		; 
		JP	Z,CONIN		; NO?  TRY AGAIN   
		IN	A,(UART)	; YES? READ THE CHAR FROM THE UART (UART0 = $68 + $00)
					; REGISTER AND PASS BACK TO USER
		.IF CONDMASKCONIN
					; no change if true leave original code
		.ELSE
		AND	127		; mask out ascii 128 to 255 = rubbish that might come from an xmodem fail
		.ENDIF
		RET
	
	.ELSE
		; scan keyboard as well (need to run TSR keyboard)
CONST:		CALL	$F2F3		; counter, used for a clock and for random number gen
					; CONSOLE STATUS, RETURN 0FFH IF CHARACTER READY, 00H IF NOT
		IN	A,(UART + $05)	; READ LINE STATUS REGISTER (UART5 = $68 + $05)
		AND	$01		; TEST IF DATA IN RECEIVE BUFFER
					; IS THERE A CHAR READY? 0=NO, 1=YES
		CALL 	Z,$F2F6		; no character, so try PS/2 keyboard TSR
		CP	$FF		; anything from the keyboard? FF=yes (need to call again with A=1 to get)
		RET	Z		; return if yes
		RET

CONIN:		LD	A,0		; start with nothing
		CALL	CONST		; returns A=0 or A=not 0
		CP	0		; keep looping while it is a zero
		JR	Z,CONIN		; if nothing, keep trying
					; if something, retest because is it keyboard or uart?
		IN	A,(UART + $05)	; READ LINE STATUS REGISTER (UART5 = $68 + $05)
		AND	$01		; TEST IF DATA IN RECEIVE BUFFER
		JR	NZ,CONUART	; if not a zero then get the data from the uart
					; if got here it must be the keyboard 
		LD	A,1		; tells the keyboard TSR to return the ascii character
		CALL	$F2F6		; call the TSR, returns the character in A, the TSR resets the flag
		RET

CONUART:				; read the character from the UART
		IN	A,(UART)	
		.IF CONDMASKCONIN	
					; leave as is
		.ELSE
		AND	127		; mask out ascii 128 to 255 = rubbish that might come from an xmodem fail
		.ENDIF
		RET			; return

	.ENDIF



	
	.IF	CONDCONOUT

CONOUT:				; CONSOLE CHARACTER OUTPUT FROM REGISTER C

CONOUT1:
		IN	A,(UART + $05)	; READ LINE STATUS REGISTER
		AND	$20		; TEST IF UART IS READY TO SEND
		JP	Z,CONOUT1	; IF NOT REPEAT

		LD	A,C		; GET TO ACCUMULATOR
		OUT	(UART),A	; THEN WRITE THE CHAR TO UART (UART0 = $68 + $00)
		RET
	
	.ELSE

CONOUT:				; CONSOLE CHARACTER OUTPUT FROM REGISTER C
CONOUT1:
		IN	A,(UART + $05)	; READ LINE STATUS REGISTER
		AND	$20		; TEST IF UART IS READY TO SEND
		JP	Z,CONOUT1	; IF NOT REPEAT
		LD	A,C		; GET TO ACCUMULATOR
		LD	B,A		;  n8VEM 
		CALL	$F2F9		;  changes B so B<>A if echo off so can do a CP and skip the out
		CP	B		;  still equal after the call
		JR	NZ,ECHOOFF	;  something chandged B so skip the uart out. Try the LCD
		OUT	(UART),A	; THEN WRITE THE CHAR TO UART (UART0 = $68 + $00)
	
ECHOOFF:	CALL	$F2F0		; TSR1	LCD display
		RET

	.ENDIF



;
;LIST:	;LIST CHARACTER FROM REGISTER C
;	MOV	A,C			;CHARACTER TO REGISTER A
;	RET				;NULL SUBROUTINE
;
;LISTST:	;RETURN LIST STATUS (0 IF NOT READY, 1 IF READY)
;	XRA	A			;0 IS ALWAYS OK TO RETURN
;	RET
;
;PUNCH:	;PUNCH CHARACTER FROM REGISTER C
				; I NEVER USE THESE SO THEY ARE NUL DEVICES.
;	MOV	A,C			;CHARACTER TO REGISTER A
;	RET				;NULL SUBROUTINE
;
;
;READER:	;READ CHARACTER INTO REGISTER A FROM READER DEVICE
				; I NEVER USE THESE SO THEY ARE NUL DEVICES.
;	MVI	A,1AH			;ENTER END OF FILE FOR NOW (REPLACE LATER)
;	ANI	7FH			;REMEMBER TO STRIP PARITY BIT
;	RET
;
;	I/O DRIVERS FOR THE DISK FOLLOW
;	FOR NOW, WE WILL SIMPLY STORE THE PARAMETERS AWAY FOR USE
;	IN THE READ AND WRITE SUBROUTINES
;

;
;   SELECT DISK GIVEN BY REGISTER C
;
SELDSK:	LD	HL,0000H	; ERROR RETURN CODE
	LD	A,C
	LD	(DISKNO),A
	.IF CONDABONLY
		CP	$06		; MUST BE BETWEEN 0 AND 5
	.ELSE
		CP	$02		; if no IDE then only drive A and B for the mini N8VEM so 0 or 1 only
	.ENDIF

	.IF CONDINVALIDDRV
		RET	NC		; original code - returns but gets stuck in a loop
	.ELSE
		CALL 	NC,RESETDRIVE	
	.ENDIF

;   DISK NUMBER IS IN THE PROPER RANGE
;   COMPUTE PROPER DISK PARAMETER HEADER ADDRESS
	LD	A,(DISKNO)
	LD	L,A		; L=DISK NUMBER 0,1,2,3,4
	LD	H,0		; HIGH ORDER ZERO
	ADD	HL,HL		; *2
	ADD	HL,HL		; *4
	ADD	HL,HL		; *8
	ADD	HL,HL		; *16 (SIZE OF EACH HEADER)
	LD	DE,DPBASE
	ADD	HL,DE		; HL=.DPBASE(DISKNO*16)
	RET

RESETDRIVE:			; Drive selected is out of range so change back to A
	LD	A,0		; change to drive A
	LD	(DISKNO),A	; store it
	LD	(ACTIVE),A	; and as the active drive too
	RET


;
HOME:				; MOVE TO THE TRACK 00 POSITION OF CURRENT DRIVE
				; TRANSLATE THIS CALL INTO A SETTRK CALL WITH PARAMETER 00
	LD	BC,0		; SELECT TRACK 0000
;	CALL	SETTRK
;	RET				;WE WILL MOVE TO 00 ON FIRST READ/WRITE
				; FALL THROUGH TO SETTRK TO STORE VALUE
	

SETTRK:				; SET TRACK GIVEN BY REGISTER BC
	LD	H,B
	LD	L,C
	LD	(TRACK),HL
	RET
;
SETSEC:				; SET SECTOR GIVEN BY REGISTER BC
	LD	H,B
	LD	L,C
	LD	(SECTOR),HL
	RET
;
;   TRANSLATE THE SECTOR GIVEN BY BC USING THE
;   TRANSLATE TABLE GIVEN BY DE
; ONLY USED FOR FLOPPIES! FOR ROMDISK/RAMDISK IT'S 1:1
; DO THE NEXT ROUTINE IS A NULL (RETURNS THE SAME)
SECTRN:	
	LD	H,B
	LD	L,C
	RET
;
SETDMA:				; SET DMA ADDRESS GIVEN BY REGISTERS B AND C
	LD	L,C		; LOW ORDER ADDRESS
	LD	H,B		; HIGH ORDER ADDRESS
	LD	(DMAAD),HL	; SAVE THE ADDRESS
	RET
;
;  DISK DRIVERS...
;
; DRIVER NEED TO DO SEVERAL THINGS FOR ROM AND RAM DISKS.
;   - INTERRUPTS ARE NOT ALLOWED DURING LOW RAM/ROM ACCESS (DISABLE!)
;   -TRANSLATE TRACK AND SECTOR INTO A POINTER TO WHERE THE 128 BYTE 
;     SECTOR BEGINS IN THE RAM/ROM
;   -TRANSLATE THE DRIVE INTO A RAM/ROM SELECT, COMBINE WITH TRACK ADDRESS
;     AND SEND TO THE MAP PORT.
;   -COPY 128 BYTE FROM OR TO THE ROM/RAMDISK AND MEMORY POINTED TO BY THE DMA 
;     ADDRESS PREVIOUSLY STORED.
;   -RESTORE MAP PORT TO PRIOR CONDITION BEFOR READ/WRITE
;
;   - FIRST TRICK IS THAT WE MADE SECTORS 256 AS 256*128=32768.  SO WE COPY 
;     THE LOW SECTOR ADDRESS TO THE LOW BYTE OF THE HL REGISTER AND THEN 
;     MULTIPLY BY 128. THIS RESULTS IN THE STARTING ADDRESS IN THE RAM OR ROM
;     (0000 -> 7F80H) 32K PAGE.
;
;    - TRICK TWO IS THE TRACK ADDRESS EQUALS THE 32K PAGE ADDRESS AND IS A 
;      DIRECT SELECT THAT CAN BE COPIED TO THE MAP PORT D0 THROUGH D5.  D7
;      SELECTS THE DRIVE (ROM OR RAM).
;      THAT MEANS THE LOW BYTE OF TRACK CONTAINS THE D0-D5 VALUE AND 
;      DISKNO HAS THE DRIVE SELECTED.  WE FIRST COPY DISKNO TO ACC
;      AND RIGHTSHIFT IT TO PLACE THAT IN BIT 7, WE THEN ADD THE LOW BYTE OF 
;      TRACK TO ACC AND THEN SEND THAT TO THE MAP PORT.
;
;      NOTE 1: A WRITE TO ROM SHOULD BE FLAGGED AS AN ERROR.
;      NOTE 2: RAM MUST START AS A "FORMATTED DISK"  IF BATTERY BACKED UP
;                   IT'S A DO ONCE AT COLD COLD START.  IF NOT BATTERY BACKED U
;                   IT WILL HAVE TO BE DONE EVERY TIME THE SYSTEM IS POWERED.
;                   FORMATTING THE RAM IS SIMPLE AS CLEARING THE DIRECTORY AREA
;                   TO A VALUE OF E5H (THE FIRST 8K OF TRACK 1 OR THE RAMDISK).
;                   IT COULD BE DONE AS A SIMPLE UTILITY PROGRAM STORED IN ROMD
;                   OR ANYTIME COLBOOT IS CALLED(LESS DESIREABLE).
;
;     -WE NOW CAN COPY TO OR FROM AS CORRECT FOR THE DEVICE 128 BYTES (SECTOR)
;      TO OR FROM THE DMA ADDRESS. ALMOST!  SINCE ROM OR RAM IS BEING PAGED
;      WE HAVE TO COPY ANYTHING DETINED FOR BELOW 8000H TO A TEMP BUFFER THEN
;      HANDLE THE PAGING.
;        
;
;     - LAST STEP IS TO RESTORE THE MAP PORT TO POINT TO THE RAM (TRACK 0) SO T
;       MEMORY MAP IS ALL RAM AGAIN AND NOT POINTING INTO THE DATA AREAS OR THE
;       SINCE THE RAM 0TH PAGE IS NOMINALLY THE LOW 32K OF RAM IN THE SYSTEM WE
;       SEND A SIMPLE MVI A,80H ; OUT MPCL_ROM; MVI A,00H ; OUT MPCL_RAM.
;
;      - THE READ OR WRITE OPERATION IS DONE.
;
;   READ DISK
;    USES DE,DL, BC,  ACC FLAGS
;      Z80 COULD USE BLOCK MOVE [LDIR] BUT WRITTEN IN 8080 
;
;


READ:
	DI			; DISABLE INTERRUPTS
	LD	A,(DISKNO)	; GET DRIVE

	.IF CONDSWAPAB

		CP	$00		; FIND OUT WHICH DRIVE IS BEING REQUESTED
		JP	Z,READ_EEPROM_DISK ; ARE WE READING RAM OR ROM?
				; READ FROM 22K EEPROM DISK
		CP	$01		; 
		JP	Z,READ_RAM_DISK	; READ FROM 448K RAM DISK

	.ELSE

		CP	$01		; FIND OUT WHICH DRIVE IS BEING REQUESTED
		JP	Z,READ_EEPROM_DISK ; ARE WE READING RAM OR ROM?
				; READ FROM 22K EEPROM DISK
		CP	$00		; 
		JP	Z,READ_RAM_DISK	; READ FROM 448K RAM DISK

	.ENDIF

	CP	$02
	JP	Z,READ_HDPART2	; READ FROM 8 MB IDE HARD DISK, PARTITION 2

	CP	$03
	JP	Z,READ_HDPART3	; READ FROM 8 MB IDE HARD DISK, PARTITION 3

	CP	$04
	JP	Z,READ_HDPART4	; READ FROM 1 MB IDE HARD DISK, PARTITION 4

	CP	$05		; 
	JP	Z,READ_RAM_DISK	; READ FROM 1M ROM DISK (UTILIZES SAME
				; ROUTINES AS RAM_DISK

				; IF NONE OF THE OTHER DISKS, IT MUST BE
				; THE EEPROM DISK, SO FALL THROUGH


READ_EEPROM_DISK:
				; 
				; IF ROM, MAP TRACK/SECTOR TO VIRTUAL TRACK/SECTOR
				; HANDLE READING FROM ROM HERE
				; 
				; PURPOSE OF THIS ROUTINE IS TO MAP 32K ROM PART
				; TRACK/SECTOR MAP (2K TRACK SIZE MADE OF 16 128
				; BYTE SECTORS EACH) ONTO WHAT THE RAM/ROM SECTOR
				; READ ROUTINES ARE EXPECTING (32K TRACK SIZE MADE
				; OF 256 128 BYTE SECTORS EACH).  THE ROUTINE 
				; CONVERTS 4 BIT TRACK # AND 4 BIT SECTOR #
				; INTO A VIRTUAL 1 TRACK, 256 SECTOR ACCESS
	LD	HL,(TRACK)	; TRACK # IS UPPER 4 BITS OF SECTOR ADDRESS
	ADD	HL,HL		; SHIFT BITS LEFT 1 (*2)
	ADD	HL,HL		; SHIFT BITS LEFT 1 (*4)
	ADD	HL,HL		; SHIFT BITS LEFT 1 (*8)
	ADD	HL,HL		; SHIFT BITS LEFT 1 (*16)
	LD	B,H		; PUT UPPER 4 BITS OF SECTOR ADDRESS IN BC
	LD	C,L		; (B IS UPPER BYTE AND C IS LOWER BYTE)
				; BC NOW CONTAINS THE UPDATED TRACK #
	LD	HL,(SECTOR)	; SECTOR # IS LOWER 4 BITS OF SECTOR ADDRESS
	ADD	HL,BC		; VIRTUAL SECTOR = (UPDATED TRACK #) + SECTOR #
	LD	(V_SECTOR),HL	; STORE VIRTUAL SECTOR #
				; NOW CONTINUE READING ROM WITH REGULAR RAM
				; SETUP FOR READ OF RAM OR ROM DISK
	LD	HL,(V_SECTOR)
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL		; *128
	LD	(SECST),HL	; SAVE SECTOR STARTING ADDRESS

				; SET PAGER WITH DRIVE (0) AND TRACK (0)
	LD	A,$00		; SWITCH IN ROM PAGE 
	OUT	(MPCL_ROM),A	; SEND TO PORT MAPPER
	LD	(PAGER),A	; SAVE COPY (JUST BECAUSE)
	
	LD	HL,(SECST)	; GET ROM/RAM ADDRESS
	LD	E,L
	LD	D,H		; GET IT INTO DE
	LD	HL,TMPBUF	; LOAD HL WITH TEMP BUF ADDRESS

;	MVI	C,128			; C IS COUNTER FOR TRANSFER
;RLOOPROM:
;	LDAX	D			; GET DATA FROM RAM/ROM VIA DE
;	MOV	M,A			; MOVE TO TEMP ADDRESS
;	DCR	C			; COUNTER -1
;	INX	D			; GET NEXT BYTE OF DATA FROM SECST+DE
;	INX	H			; PUT NEXT BYTE OF DATA AT TMPBUF+HL
;	JNZ	RLOOPROM		; LOOP TILL DONE

	CALL	COPY_CPM_SECTOR

;
; NO WITH THE ROM/RAM DATA IN THE BUFFER WE CAN NOW MOVE IT TO THE 
;  DMA ADDRESS (IN RAM)
;
	CALL	RPAGE		; SET PAGE TO CP/M RAM
	LD	HL,TMPBUF	; GET ROM/RAM ADDRESS
	LD	E,L
	LD	D,H		; GET IT INTO DE
	LD	HL,(DMAAD)	; LOAD HL WITH DMA ADDRESS

;	MVI	C,128			; C IS COUNTER FOR TRANSFER
;R2LOOPROM:	
;	LDAX	D			; GET DATA FROM RAM/ROM VIA DE
;	MOV	M,A			; MOVE TO DMA ADDRESS
;	DCR	C			; COUNTER -1
;	INX	D			; GET NEXT BYTE OF DATA AT TMPBUF + DE
;	INX	H			; PUT NEXT BYTE OF DATA FROM DMAAD + HL
;	JNZ	R2LOOPROM		; LOOP TILL DONE

	CALL	COPY_CPM_SECTOR

	LD	A,$00
	EI			; RE-ENABLE INTERRUPTS
	RET
				; ACCESS ALGORITHM (ONLY APPLICABLE TO 32K ROM PART!)

READ_RAM_DISK:
				; IF RAM, PROCEED WITH NORMAL TRACK/SECTOR READ
	CALL	SECPAGE		; SETUP FOR READ OF RAM OR ROM DISK
	CALL	PAGERB		; SET PAGER WITH DRIVE AND TRACK
	LD	HL,(SECST)	; GET ROM/RAM ADDRESS
	LD	E,L
	LD	D,H		; GET IT INTO DE
	LD	HL,TMPBUF	; LOAD HL WITH TEMP BUF ADDRESS

;	MVI	C,128			; C IS COUNTER FOR TRANSFER
;RLOOP:
;	LDAX	D			; GET DATA FROM RAM/ROM VIA DE
;	MOV	M,A			; MOVE TO TEMP ADDRESS
;	DCR	C			; COUNTER -1
;	INX	D			; GET NEXT BYTE OF DATA FROM SECST+DE
;	INX	H			; PUT NEXT BYTE OF DATA AT TMPBUF+HL
;	JNZ	RLOOP			; LOOP TILL DONE

	CALL	COPY_CPM_SECTOR

;
; NOW WITH THE ROM/RAM DATA IN THE BUFFER WE CAN NOW MOVE IT TO THE 
;  DMA ADDRESS (IN RAM)
;
	CALL	RPAGE		; SET PAGE TO CP/M RAM
	LD	HL,TMPBUF	; GET ROM/RAM ADDRESS
	LD	E,L
	LD	D,H		; GET IT INTO DE
	LD	HL,(DMAAD)	; LOAD HL WITH DMA ADDRESS

;	MVI	C,128			; C IS COUNTER FOR TRANSFER
;R2LOOP:	
;	LDAX	D			; GET DATA FROM RAM/ROM VIA DE
;	MOV	M,A			; MOVE TO DMA ADDRESS
;	DCR	C			; COUNTER -1
;	INX	D			; GET NEXT BYTE OF DATA FROM TMPBUF + DE
;	INX	H			; PUT NEXT BYTE OF DATA AT DMAAD + HL
;	JNZ	R2LOOP			; LOOP TILL DONE

	CALL	COPY_CPM_SECTOR

	LD	A,$00
	EI			; RE-ENABLE INTERRUPTS
	RET

READ_HDPART2:

	DI			; DISABLE INTERRUPTS

	LD	HL,$FC00	; INITIALIZE LBA OFFSET SECTOR LO WORD
	LD	(LBA_OFFSET_LO),HL

	LD	HL,$0000	; INITIALIZE LBA OFFSET SECTOR HI WORD
	LD	(LBA_OFFSET_HI),HL

				; BDOS TRACK PARAMETER (16 BITS)
				; BDOS SECTOR PARAMETER (16 BITS)

	LD	HL,(TRACK)	; LOAD TRACK # (WORD)
	LD	B,L		; SAVE LOWER 8 BITS (TRACK # 0-255)
	LD	HL,(SECTOR)	; LOAD SECTOR # (WORD)
	LD	H,B		; HL IS 8 BIT TRACK IN H, 8 BIT SECTOR IN L

	CALL	CONVERT_IDE_SECTOR_CPM ; COMPUTE WHERE THE CP/M SECTOR IS ON THE
				; IDE PARTITION

				; MAP COMPUTED IDE HD SECTOR TO LBA REGISTERS

				; LBA REGISTERS STORE 28 BIT VALUE OF IDE HD SECTOR ADDRESS

	LD	A,(LBA_TARGET_LO) ; LOAD LBA REGISTER 0 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA0),A
	LD	A,(LBA_TARGET_LO+1) ; LOAD LBA REGISTER 1 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA1),A
	LD	A,(LBA_TARGET_HI) ; LOAD LBA REGISTER 2 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA2),A
	LD	A,(LBA_TARGET_HI+1) ; LOAD LBA REGISTER 3 WITH SECTOR ADDRESS TO READ
	AND	%00001111	; ONLY LOWER FOUR BITS ARE VALID
	ADD	A,%11100000	; ENABLE LBA BITS 5:7=111 IN IDE_LBA3
	LD	(IDE_LBA3),A

				; READ IDE HD SECTOR

	CALL	IDE_READ_SECTOR	; READ THE IDE HARD DISK SECTOR

; NEED TO ADD ERROR CHECKING HERE, CARRY FLAG IS SET IF IDE_READ_SECTOR SUCCESS

				; COMPUTE STARTING ADDRESS OF CP/M SECTOR IN READ IDE HD SECTOR BUFFER

	LD	HL,SECTOR_BUFFER ; LOAD HL WITH SECTOR BUFFER ADDRESS
	LD	A,(SECTOR_INDEX) ; GET THE SECTOR INDEX (CP/M SECTOR OFFSET IN BUFFER)
	RRCA			; MOVE BIT 0 TO BIT 7
	RRCA			; DO AGAIN - IN EFFECT MULTIPLY BY 64
	LD	D,$00		; PUT RESULT AS 16 VALUE IN DE, UPPER BYTE IN D IS $00
	LD	E,A		; PUT ADDRESS OFFSET IN E
	ADD	HL,DE		; MULTIPLY BY 2, TOTAL MULTIPLICATION IS X 128
	ADD	HL,DE		; CP/M SECTOR STARTING ADDRESS IN IDE HD SECTOR BUFFER

				; COPY CP/M SECTOR TO BDOS DMA ADDRESS BUFFER

	LD	D,H		; TRANSFER HL REGISTERS TO DE
	LD	E,L
	LD	HL,(DMAAD)	; LOAD HL WITH DMA ADDRESS

	CALL	COPY_CPM_SECTOR

	EI			; RE-ENABLE INTERRUPTS

	LD	A,$00		; RETURN ERROR CODE READ SUCCESSFUL A=0

	RET

READ_HDPART3:
			LD	A,$10					; SET TO SECONDARY DEVICE
			LD	(IDEDEVICE),A				;

			DI						; DISABLE INTERRUPTS
			LD	HL,$0000				; INITIALIZE LBA OFFSET SECTOR LO WORD
			LD	(LBA_OFFSET_LO),HL			;
									;
			LD	HL,$0000				; INITIALIZE LBA OFFSET SECTOR HI WORD
			LD	(LBA_OFFSET_HI),HL			;
									;
									; BDOS TRACK PARAMETER (16 BITS)
									; BDOS SECTOR PARAMETER (16 BITS)
									;
			LD	HL,(TRACK)				; LOAD TRACK # (WORD)
			LD	B,L					; SAVE LOWER 8 BITS (TRACK # 0-255)
			LD	HL,(SECTOR)				; LOAD SECTOR # (WORD)
			LD	H,B					; HL IS 8 BIT TRACK IN H, 8 BIT SECTOR IN L
									;
									;
			CALL	CONVERT_IDE_SECTOR_CPM			; COMPUTE WHERE THE CP/M SECTOR IS ON THE
									;IDE PARTITION
									;
									;MAP COMPUTED IDE HD SECTOR TO LBA REGISTERS
									;
									;LBA REGISTERS STORE 28 BIT VALUE OF IDE HD SECTOR ADDRESS
									;
			LD	A,(LBA_TARGET_LO)			; LOAD LBA REGISTER 0 WITH SECTOR ADDRESS TO READ
			LD	L,A					;
			LD	A,(LBA_TARGET_LO+1)			; LOAD LBA REGISTER 1 WITH SECTOR ADDRESS TO READ
			LD	H,A					;
			LD	A,(LBA_TARGET_HI)			; LOAD LBA REGISTER 2 WITH SECTOR ADDRESS TO READ
			LD	E,A					;
			LD	A,(LBA_TARGET_HI+1)			; LOAD LBA REGISTER 3 WITH SECTOR ADDRESS TO READ
			LD	D,A					;
									;
									;READ IDE HD SECTOR
									;
			CALL	ATAPI_READ_SECTOR			; READ THE IDE HARD DISK SECTOR
									;
		; NEED TO ADD ERROR CHECKING HERE, CARRY FLAG IS SET IF ATAPI_READ_SECTOR SUCCESSFUL!
									;
									;COMPUTE STARTING ADDRESS OF CP/M SECTOR IN READ IDE HD SECTOR BUFFER
									;
			LD	HL,SECTOR_BUFFER 			; LOAD HL WITH SECTOR BUFFER ADDRESS
			LD	A,(SECTOR_INDEX) 			; GET THE SECTOR INDEX (CP/M SECTOR OFFSET IN BUFFER)
			RRCA						; MOVE BIT 0 TO BIT 7
			RRCA						; DO AGAIN - IN EFFECT MULTIPLY BY 64
			LD	D,$00					; PUT RESULT AS 16 VALUE IN DE, UPPER BYTE IN D IS $00
			LD	E,A					; PUT ADDRESS OFFSET IN E
			ADD	HL,DE					; MULTIPLY BY 2, TOTAL MULTIPLICATION IS X 128
			ADD	HL,DE					; CP/M SECTOR STARTING ADDRESS IN IDE HD SECTOR BUFFER

									; COPY CP/M SECTOR TO BDOS DMA ADDRESS BUFFER
		
			LD	D,H					; TRANSFER HL REGISTERS TO DE
			LD	E,L
			LD	HL,(DMAAD)				; LOAD HL WITH DMA ADDRESS
									;
			CALL	COPY_CPM_SECTOR				;
									;
			EI						;RE-ENABLE INTERRUPTS
									;
			LD	A,$00					; RETURN ERROR CODE READ SUCCESSFUL A=0
									;
			RET						;

READ_HDPART4:
				; STUB
	RET


;
;   WRITE DISK
;
WRITE:
	DI			; DISABLE INTERRUPTS

	LD	A,(DISKNO)	; GET DRIVE

	.IF CONDSWAPAB

		CP	$00		; FIND OUT WHICH DRIVE IS BEING REQUESTED
		JP	Z,RDONLY	; JUMP TO READ ONLY ROUTINE (CANT WRITE TO ROM)
				; READ ONLY, FROM 22K EEPROM DISK, ERROR ON WRITE
		CP	$01		; 
		JP	Z,WRITE_RAM_DISK ; WRITE TO 448K RAM DISK

	.ELSE

		CP	$01		; FIND OUT WHICH DRIVE IS BEING REQUESTED
		JP	Z,RDONLY	; JUMP TO READ ONLY ROUTINE (CANT WRITE TO ROM)
				; READ ONLY, FROM 22K EEPROM DISK, ERROR ON WRITE
		CP	$00		; 
		JP	Z,WRITE_RAM_DISK ; WRITE TO 448K RAM DISK

	.ENDIF

	CP	$02
	JP	Z,WRITE_HDPART2	; WRITE TO 8 MB IDE HARD DISK, PARTITION 2

	CP	$03
	JP	Z,WRITE_HDPART3	; WRITE TO 8 MB IDE HARD DISK, PARTITION 3

	CP	$04
	JP	Z,WRITE_HDPART4	; WRITE TO 1 MB IDE HARD DISK, PARTITION 4

	CP	$05		; 
	JP	Z,WRITE_RAM_DISK ; WRITE TO 1M ROM DISK (SIMH ONLY)

				; IF NONE OF THE OTHER DISKS, IT MUST BE
				; THE RAM DISK, SO FALL THROUGH

RDONLY:

;
;   HANDLE WRITE TO READ ONLY
;
;   SENDS A MESSAGE TO TERMINAL THAT ROM DRIVE IS NOT WRITEABLE
;   DOES A PAUSE THEN RETURNS TO CPM WITH ERROR FLAGGED. THIS IS
;   DONE TO ALLOW A POSSIBLE GRACEFUL EXIT (SOME APPS MAY PUKE).
;

				; CODE TBD, PRINT A HEY WRONG DISK AND PAUSE 5SEC AND
				; CONTINUE.

	LD	HL,TXT_RO_ERROR	; SET HL TO START OF ERROR MESSAGE

	CALL	PRTMSG		; PRINT ERROR MESSAGE

	LD	A,$01		; SEND BAD SECTOR ERROR BACK
				; BDOS WILL ALSO PRINT ITS OWN ERROR MESSAGE
; ADD 5 SECOND PAUSE ROUTINE HERE
	RET


WRITE_RAM_DISK:

	LD	HL,(DMAAD)	; GET DMA ADDRESS
	LD	E,L
	LD	D,H		; GET IT INTO DE
	LD	HL,TMPBUF	; LOAD HL WITH TEMP BUF ADDRESS

;	MVI	C,128			; C IS COUNTER FOR TRANSFER
;WLOOP:
;	LDAX	D			; GET DATA FROM RAM/ROM VIA DE
;	MOV	M,A			; MOVE TO TEMP ADDRESS
;	DCR	C			; COUNTER -1
;	INX	D			; GET NEXT BYTE OF DATA FROM DMAAD + DE
;	INX	H			; PUT NEXT BYTE OF DATA AT TMPBUF + HL
;	JNZ	WLOOP			; LOOP TILL DONE

	CALL	COPY_CPM_SECTOR

;
;  NOW THAT DATA IS IN THE TEMP BUF WE SET TO RAM PAGE
;   FOR WRITE.
;
	CALL	SECPAGE		; GET RAM PAGE WRITE ADDRESS
	CALL	PAGERB		; SET PAGER WITH DRIVE AND TRACK
	LD	HL,TMPBUF	; GET TEMP BUFFER ADDRESS
	LD	E,L
	LD	D,H		; GET IT INTO DE
	LD	HL,(SECST)	; LOAD HL WITH DMA ADDRESS (WHERE TO WRITE TO)

;	MVI	C,128			; C IS COUNTER FOR TRANSFER
;W2LOOP:
;	LDAX	D			; GET DATA FROM RAM/ROM VIA DE
;	MOV	M,A			; MOVE TO RAMDRIVE ADDRESS
;	DCR	C			; COUNTER -1
;	INX	D			; PUT NEXT BYTE OF DATA AT TMPBUF+DE
;	INX	H			; GET NEXT BYTE OF DATA FROM SECST+HL
;	JNZ	W2LOOP			; LOOP TILL DONE

	CALL	COPY_CPM_SECTOR

	CALL	RPAGE		; SET BACK TO RAM
	LD	A,$00
	EI			; RE-ENABLE INTERRUPTS
	RET

WRITE_HDPART2:

	DI			; DISABLE INTERRUPTS

	LD	HL,$FC00	; INITIALIZE LBA OFFSET SECTOR LO WORD
	LD	(LBA_OFFSET_LO),HL

	LD	HL,$0000	; INITIALIZE LBA OFFSET SECTOR HI WORD
	LD	(LBA_OFFSET_HI),HL

				; BDOS TRACK PARAMETER (16 BITS)
				; BDOS SECTOR PARAMETER (16 BITS)

	LD	HL,(TRACK)	; LOAD TRACK # (WORD)
	LD	B,L		; SAVE LOWER 8 BITS (TRACK # 0-255)
	LD	HL,(SECTOR)	; LOAD SECTOR # (WORD)
	LD	H,B		; HL IS 8 BIT TRACK IN H, 8 BIT SECTOR IN L

	CALL	CONVERT_IDE_SECTOR_CPM ; COMPUTE WHERE THE CP/M SECTOR IS ON THE
				; IDE PARTITION

				; MAP COMPUTED IDE HD SECTOR TO LBA REGISTERS

				; LBA REGISTERS STORE 28 BIT VALUE OF IDE HD SECTOR ADDRESS

	LD	A,(LBA_TARGET_LO) ; LOAD LBA REGISTER 0 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA0),A
	LD	A,(LBA_TARGET_LO+1) ; LOAD LBA REGISTER 1 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA1),A
	LD	A,(LBA_TARGET_HI) ; LOAD LBA REGISTER 2 WITH SECTOR ADDRESS TO READ
	LD	(IDE_LBA2),A
	LD	A,(LBA_TARGET_HI+1) ; LOAD LBA REGISTER 3 WITH SECTOR ADDRESS TO READ
	AND	%00001111	; ONLY LOWER FOUR BITS ARE VALID
	ADD	A,%11100000	; ENABLE LBA BITS 5:7=111 IN IDE_LBA3
	LD	(IDE_LBA3),A

				; READ IDE HD SECTOR

	CALL	IDE_READ_SECTOR	; READ THE IDE HARD DISK SECTOR

; NEED TO ADD ERROR CHECKING HERE, CARRY FLAG IS SET IF IDE_READ_SECTOR SUCCESS

				; COMPUTE STARTING ADDRESS OF CP/M SECTOR IN READ IDE HD SECTOR BUFFER

	LD	HL,SECTOR_BUFFER ; LOAD HL WITH SECTOR BUFFER ADDRESS
	LD	A,(SECTOR_INDEX) ; GET THE SECTOR INDEX (CP/M SECTOR OFFSET IN BUFFER)
	RRCA			; MOVE BIT 0 TO BIT 7
	RRCA			; DO AGAIN - IN EFFECT MULTIPLY BY 64
	LD	D,$00		; PUT RESULT AS 16 VALUE IN DE, UPPER BYTE IN D IS $00
	LD	E,A		; PUT ADDRESS OFFSET IN E
	ADD	HL,DE		; CP/M SECTOR STARTING ADDRESS IN IDE HD SECTOR BUFFER
	ADD	HL,DE		; MULTIPLY BY 2, TOTAL MULTIPLICATION IS X 128

	LD	(SECST),HL	; KEEP CP/M SECTOR ADDRESS FOR LATER USE

				; COPY CP/M SECTOR FROM BDOS DMA ADDRESS BUFFER

	LD	HL,(DMAAD)	; LOAD HL WITH DMA ADDRESS (WHERE THE DATA TO BE WRITTEN IS)
	LD	D,H		; TRANSFER HL REGISTERS TO DE
	LD	E,L
	LD	HL,(SECST)	; LOAD CP/M SECTOR ADDRESS (WHERE THE DATA IS TO BE WRITTEN)

	CALL	COPY_CPM_SECTOR

				; IDE HD SECTOR IS NOW UPDATED WITH CURRENT CP/M SECTOR DATA SO WRITE TO DISK

	CALL	IDE_WRITE_SECTOR ; WRITE THE UPDATED IDE HARD DISK SECTOR

; NEED TO ADD ERROR CHECKING HERE, CARRY FLAG IS SET IF IDE_WRITE_SECTOR SUCCES

	EI			; RE-ENABLE INTERRUPTS

	LD	A,$00		; RETURN ERROR CODE WRITE SUCCESSFUL A=0

	RET

WRITE_HDPART3:
			LD	A,$10					; SET TO SECONDARY DEVICE
			LD	(IDEDEVICE),A				;
			DI						;DISABLE INTERRUPTS
									;
			LD	HL,$0000				; INITIALIZE LBA OFFSET SECTOR LO WORD
			LD	(LBA_OFFSET_LO),HL			;
									;
			LD	HL,$0000				; INITIALIZE LBA OFFSET SECTOR HI WORD
			LD	(LBA_OFFSET_HI),HL			;
									;
									;BDOS TRACK PARAMETER (16 BITS)
									;BDOS SECTOR PARAMETER (16 BITS)
									;
			LD	HL,(TRACK)				; LOAD TRACK # (WORD)
			LD	B,L					; SAVE LOWER 8 BITS (TRACK # 0-255)
			LD	HL,(SECTOR)				; LOAD SECTOR # (WORD)
			LD	H,B					; HL IS 8 BIT TRACK IN H, 8 BIT SECTOR IN L
									;	
			CALL	CONVERT_IDE_SECTOR_CPM			; COMPUTE WHERE THE CP/M SECTOR IS ON THE
									;IDE PARTITION
									;
									;MAP COMPUTED IDE HD SECTOR TO LBA REGISTERS
									;
									;LBA REGISTERS STORE 28 BIT VALUE OF IDE HD SECTOR ADDRESS
									;
			LD	A,(LBA_TARGET_LO)			; LOAD LBA REGISTER 0 WITH SECTOR ADDRESS TO READ
			LD	L,A					;
			LD	A,(LBA_TARGET_LO+1)			; LOAD LBA REGISTER 1 WITH SECTOR ADDRESS TO READ
			LD	H,A					;
			LD	A,(LBA_TARGET_HI)			; LOAD LBA REGISTER 2 WITH SECTOR ADDRESS TO READ
			LD	E,A					;
			LD	A,(LBA_TARGET_HI+1)			; LOAD LBA REGISTER 3 WITH SECTOR ADDRESS TO READ
			LD	D,A					;
									;
									;READ IDE HD SECTOR
									;
			CALL	ATAPI_READ_SECTOR			; READ THE IDE HARD DISK SECTOR
									;
			; NEED TO ADD ERROR CHECKING HERE, CARRY FLAG IS SET IF IDE_READ_SECTOR SUCCESSFUL!
									;
									;COMPUTE STARTING ADDRESS OF CP/M SECTOR IN READ IDE HD SECTOR BUFFER
			LD	HL,SECTOR_BUFFER 			; LOAD HL WITH SECTOR BUFFER ADDRESS
			LD	A,(SECTOR_INDEX)	 		; GET THE SECTOR INDEX (CP/M SECTOR OFFSET IN BUFFER)
			RRCA						; MOVE BIT 0 TO BIT 7
			RRCA						; DO AGAIN - IN EFFECT MULTIPLY BY 64
			LD	D,$00					; PUT RESULT AS 16 VALUE IN DE, UPPER BYTE IN D IS $00
			LD	E,A					; PUT ADDRESS OFFSET IN E
			ADD	HL,DE					; CP/M SECTOR STARTING ADDRESS IN IDE HD SECTOR BUFFER
			ADD	HL,DE					; MULTIPLY BY 2, TOTAL MULTIPLICATION IS X 128

			LD	(SECST),HL				; KEEP CP/M SECTOR ADDRESS FOR LATER USE

				; COPY CP/M SECTOR FROM BDOS DMA ADDRESS BUFFER
		
			LD	HL,(DMAAD)				; LOAD HL WITH DMA ADDRESS (WHERE THE DATA TO BE WRITTEN IS)
			LD	D,H					; TRANSFER HL REGISTERS TO DE
			LD	E,L
			LD	HL,(SECST)				; LOAD CP/M SECTOR ADDRESS (WHERE THE DATA IS TO BE WRITTEN)
									;
			CALL	COPY_CPM_SECTOR				;
									;
									;IDE HD SECTOR IS NOW UPDATED WITH CURRENT CP/M SECTOR DATA SO WRITE TO DISK
									;
			LD	A,(LBA_TARGET_LO)			; LOAD LBA REGISTER 0 WITH SECTOR ADDRESS TO READ
			LD	L,A					;
			LD	A,(LBA_TARGET_LO+1)			; LOAD LBA REGISTER 1 WITH SECTOR ADDRESS TO READ
			LD	H,A					;
			LD	A,(LBA_TARGET_HI)			; LOAD LBA REGISTER 2 WITH SECTOR ADDRESS TO READ
			LD	E,A					;
			LD	A,(LBA_TARGET_HI+1)			; LOAD LBA REGISTER 3 WITH SECTOR ADDRESS TO READ
			LD	D,A					;
									;
			CALL	ATAPI_WRITE_SECTOR			; WRITE THE UPDATED IDE HARD DISK SECTOR
									;
									; NEED TO ADD ERROR CHECKING HERE, CARRY FLAG IS SET IF IDE_WRITE_SECTOR SUCCESSFUL!
									;
			EI						;RE-ENABLE INTERRUPTS
									;
			LD	A,$00					; RETURN ERROR CODE WRITE SUCCESSFUL A=0
									;
			RET
WRITE_HDPART4:
				; STUB
	RET

; ABOVE CODE MAKES TEST PROTOTYPE COLD START DURING CP/M WARM BOOT (WBOOT)

	PUSH	AF
LDIR_LOOP:
	
	LD	A,(HL)
	LD	(DE),A
	INC	DE
	INC	HL
	DEC	C
	JP	NZ,LDIR_LOOP
	DEC	B
	JP	NZ,LDIR_LOOP
	POP	AF

	RET


PRTMSG:
	LD	A,(HL)		; GET CHARACTER TO A
	CP	END		; TEST FOR END BYTE
	JP	Z,PRTMSG1	; JUMP IF END BYTE IS FOUND
	LD	C,A		; PUT CHAR TO PRINT VALUE IN REG C FOR CONOUT
	CALL	CONOUT		; SEND CHARACTER TO CONSOLE FROM REG C
	INC	HL		; INC POINTER, TO NEXT CHAR
	JP	PRTMSG		; TRANSMIT LOOP

PRTMSG1:
	RET


;
; UTILITY ROUTINE FOR SECTOR TO PAGE ADDRESS
;   USES HL AND CARRY
;SECPAG:	LHLD	SECTOR
SECPAGE:
	LD	HL,(SECTOR)
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL
	ADD	HL,HL		; *128
	LD	(SECST),HL	; SAVE SECTOR STARTING ADDRESS
	RET
;
;  PAGER BYTE CREATION
;    ASSEMBLES DRIVE AND TRACK AND SENDS IT TO PAGER PORT
;
PAGERB:	LD	HL,(TRACK)
	LD	A,(DISKNO)
	CP	$05		; 
	JP	Z,ROMD		; READ FROM 1M ROM DISK
	AND	1		; MASK FOR 1 BIT OF DRIVE SELECT 
	RRCA			; MOVE BIT 0 TO BIT 7
	OR	L		; OR L WITH ACC TO COMBINE TRACK AND DRIVE
	OUT	(MPCL_RAM),A	; SEND TO RAM PORT MAPPER
	LD	(PAGER),A	; SAVE COPY (JUST BECAUSE)
	RET
ROMD:	AND	1		; MASK FOR 1 BIT OF DRIVE SELECT 
	RRCA			; MOVE BIT 0 TO BIT 7
	OR	L		; OR L WITH ACC TO COMBINE TRACK AND DRIVE
	AND	$7F		; STRIP OFF BIT 7 (ROM_ENABLE BIT)
	OUT	(MPCL_ROM),A	; SEND TO ROM PORT MAPPER
	LD	(PAGER),A	; SAVE COPY (JUST BECAUSE)
	LD	(DB_PAGER),A	; SAVE COPY (JUST BECAUSE) (debug)
	RET



;
;   RESET PAGER BACK TO RAM.  
;
RPAGE:
	LD	A,$80		; DESELECT ROM PAGE
	OUT	(MPCL_ROM),A	; SELECT RAM
	LD	A,$00		; SET TO RAM, TRACK 0
	OUT	(MPCL_RAM),A	; SELECT RAM
	LD	(PAGER),A	; SAVE COPY OF PAGER BYTE
	RET


COPY_CPM_SECTOR:
				; COPIES ONE CPM SECTOR FROM ONE MEMORY ADDRESS TO ANOTHER

				; INPUT
				;  - DE SOURCE ADDRESS
				;  - HL TARGET ADDRESS
				; USES C REGISTER

	LD	C,128		; C IS COUNTER FOR FIXED SIZE TRANSFER (128 BYTES)
COPYLOOP:
	
	LD	A,(DE)		; GET DATA FROM RAM/ROM VIA DE (SOURCE)
	LD	(HL),A		; MOVE TO HL ADDRESS (TARGET)
	DEC	C		; COUNTER -1
	INC	DE		; GET NEXT BYTE OF DATA FROM SOURCE
	INC	HL		; PUT NEXT BYTE OF DATA AT TARGET
	JP	NZ,COPYLOOP	; LOOP TILL DONE

	RET


CONVERT_IDE_SECTOR_CPM:

				; COMPUTES WHERE THE CP/M SECTOR IS IN THE IDE PARTITION
				; IDE HD SECTORS ARE 512 BYTES EACH, CP/M SECTORS ARE 128 BYTES EACH
				; MAXIMUM SIZE OF CP/M DISK IS 8 MB = 65536 (16 BITS) X 128 BYTES PER SECTOR
				; IDE HD PARTITION CAN HAVE AT MOST 16384 IDE SECTORS -> 65536 CP/M SECTORS
				; EACH IDE HD SECTOR CONTAINS 4 ADJACENT CP/M SECTORS
				; 
				; 
				; INPUT:
				; IDE HD PARTITION STARTING SECTOR NUMBER (FROM PARTITION TABLE)
				;  - LOWER 16 BITS STORED IN LBA_OFFSET_LO
				;  - UPPER 16 BITS STORED IN LBA_OFFSET_HI
				; PARTITION OFFSET IN HL (16 BITS)
				;  - A UNIQUELY COMPUTED FUNCTION BASED ON GEOMETRY OF DISKS NUMBER OF
				;    CP/M TRACKS AND SECTORS SPECIFIED IN DPB
				; 
				; 
				; OUTPUT:
				; IDE TARGET SECTOR (SENT TO IDE HD CONTROLLER FOR READ OPERATION)
				;  - LOWER 16 BITS STORED IN LBA_TARGET_LO
				;  - UPPER 16 BITS STORED IN LBA_TARGET_HI
				; CP/M TO IDE HD SECTOR MAPPING PARAMETER STORED IN SECTOR_INDEX
				;  - 8 BIT VALUE WITH 4 LEGAL STATES (00, 01, 02, 04) WHICH IS
				;    TO BE USED TO COMPUTE STARTING ADDRESS OF 128 BYTE CP/M SECTOR ONCE
				;    512 BYTE IDE HD SECTOR READ INTO MEMORY BUFFER
				; 

				; ROTATE WITH CARRY 16 BIT TRACK,SECTOR VALUE IN HL TO GET 14 BIT IDE HD
				; TARGET SECTOR IN PARTITION
				; KEEP LAST TWO BITS IN B FOR IDE HD SECTOR TO CP/M SECTOR TRANSLATION

				; COMPUTE SECTOR_INDEX 

	XOR	A		; ZERO ACCUMULATOR
	LD	A,L		; STORE LAST 2 BITS OF L IN B
	AND	%00000011	; 
	LD	B,A
	LD	(SECTOR_INDEX),A ; LOCATES WHERE THE 128 BYTE CP/M SECTOR
				; IS WITHIN THE 512 BYTE IDE HD SECTOR

				; COMPUTE WHICH IDE HD SECTOR TO READ TO WITHIN 4 CP/M SECTORS 
				; SHIFTS 16 BIT PARTITION OFFSET TO THE RIGHT 2 BITS AND ADDS RESULT TO
				; IDE HD PARTITION STARTING SECTOR

				; SHIFT PARTITION OFFSET RIGHT 1 BIT

	SCF
	CCF			; CLEAR CARRY FLAG
	LD	A,H		; 16 BIT ROTATE HL WITH CARRY
	RRA
	LD	H,A		; ROTATE HL RIGHT 1 BIT (DIVIDE BY 2)
	LD	A,L
	RRA
	LD	L,A

				; SHIFT PARTITION OFFSET RIGHT 1 BIT

	SCF
	CCF			; CLEAR CARRY FLAG
	LD	A,H		; 16 BIT ROTATE HL WITH CARRY
	RRA
	LD	H,A		; ROTATE HL RIGHT 1 BIT (DIVIDE BY 2)
	LD	A,L
	RRA
	LD	L,A

				; ADD RESULTING 14 BIT VALUE TO IDE HD PARTITION STARTING SECTOR
				; STORE RESULT IN IDE HD TARGET SECTOR PARAMETER

	LD	A,(LBA_OFFSET_LO) ; 16 BIT ADD OF LBA_OFFSET_LO WITH HL
	ADD	A,L
	LD	(LBA_TARGET_LO),A
	LD	A,(LBA_OFFSET_LO+1)
	ADC	A,H
	LD	(LBA_TARGET_LO+1),A ; STORE OVERFLOW BIT IN CARRY

	LD	HL,$0000
	LD	A,(LBA_OFFSET_HI) ; 16 BIT ADD WITH CARRY OF LBA_OFFSET_HI WITH $0000
	ADC	A,L
	LD	(LBA_TARGET_HI),A
	LD	A,(LBA_OFFSET_HI+1)
	ADC	A,H
	LD	(LBA_TARGET_HI+1),A

	RET

IDE_READ_SECTOR:

	CALL	IDE_WAIT_BUSY_READY ; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC
	CALL	IDE_SETUP_LBA	; TELL DRIVE WHAT SECTOR IS REQUIRED
	LD	A,$20
	OUT	(IDESTTS),A	; $20 = IDE 'READ SECTOR' COMMAND 

IDE_SREX:
	CALL	IDE_WAIT_BUSY_READY ; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC
	CALL	IDE_TEST_ERROR	; ENSURE NO ERROR WAS REPORTED
	RET	NC
	CALL	IDE_WAIT_BUFFER	; WAIT FOR FULL BUFFER SIGNAL FROM DRIVE
	RET	NC

	CALL	IDE_READ_BUFFER	; GRAB THE 256 WORDS FROM THE BUFFER
	SCF			; CARRY = 1 ON RETURN = OPERATION OK
	RET
	
;-----------------------------------------------------------------------------

IDE_WRITE_SECTOR:

	CALL	IDE_WAIT_BUSY_READY ; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC
	CALL	IDE_SETUP_LBA	; TELL DRIVE WHAT SECTOR IS REQUIRED
	LD	A,$30
	OUT	(IDESTTS),A	; $30 = IDE 'WRITE SECTOR' COMMAND 
	CALL	IDE_WAIT_BUSY_READY
	RET	NC
	CALL	IDE_TEST_ERROR	; ENSURE NO ERROR WAS REPORTED
	RET	NC
	CALL	IDE_WAIT_BUFFER	; WAIT FOR BUFFER READY SIGNAL FROM DRIVE
	RET	NC
	CALL	IDE_WRITE_BUFFER ; SEND 256 WORDS TO DRIVE'S BUFFER
	CALL	IDE_WAIT_BUSY_READY ; MAKE SURE DRIVE IS READY TO PROCEED
	RET	NC
	CALL	IDE_TEST_ERROR	; ENSURE NO ERROR WAS REPORTED
	RET	NC
	SCF			; CARRY = 1 ON RETURN = OPERATION OK
	RET

;-----------------------------------------------------------------------------

IDE_SOFT_RESET:

	.IF 	CONDIDESOFT

		LD	A,%00000110	; NO INTERRUPTS, RESET DRIVE = 1
		OUT	(IDECTRL),A
		LD	A,%00000010	; NO INTERRUPTS, RESET DRIVE = 0
		OUT	(IDECTRL),A
		CALL	IDE_WAIT_BUSY_READY	;this takes a couple of seconds
		RET

	.ELSE
		; skip this if no ide drive which speeds up reboots and startups
		RET
	.ENDIF


;------------------------------------------------------------------------------
; IDE INTERNAL SUBROUTINES 
;------------------------------------------------------------------------------


ATAPI_WAIT_BUSY_READY:
	LD	DE,0			;
ATAPI_WBSY:
	LD	B,5			;
ATAPI_DLP:
	DJNZ	ATAPI_DLP		;
	INC	DE			;
	LD	A,D			;
	OR	E			;
	JR	Z,ATAPI_TO		;
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%10000000		; MASK OFF BUSY BIT
	JR	NZ,ATAPI_WBSY		; WE WANT BUSY(7) TO BE 0 
	SCF				; CARRY 1 = OK
	RET				;
ATAPI_TO:
	XOR	A			; CARRY 0 = TIMED OUT
	RET				;
	


IDE_WAIT_DRQ_READY:
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%00001000		; MASK OFF RDY BIT
	JR	Z,IDE_WAIT_DRQ_READY	; WE WANT DRQ(3) TO BE 1
	RET

IDE_WAIT_DRQ_ZERO:
	IN	A,(IDESTTS)		; READ ERROR REG
	AND	%00001000		; MASK OFF RDY BIT
	JR	NZ,IDE_WAIT_DRQ_ZERO	; WE WANT DRQ(3) TO BE 0
	RET

IDE_WAIT_BUSY_READY:
	LD	DE,0
IDE_WBSY:
	LD	B,5
IDE_DLP:
	DEC	B
	JP	NZ,IDE_DLP
	INC	DE
	LD	A,D
	OR	E
	JP	Z,IDE_TO
	IN	A,(IDESTTS)	; READ ERROR REG
	AND	%11000000	; MASK OFF BUSY AND RDY BITS
	XOR	%01000000	; WE WANT BUSY(7) TO BE 0 AND RDY(6) TO BE 1
	JP	NZ,IDE_WBSY
	SCF			; CARRY 1 = OK
	RET

IDE_TO:
	XOR	A		; CARRY 0 = TIMED OUT
	RET
	
;----------------------------------------------------------------------------

IDE_TEST_ERROR:
	
	SCF
	IN	A,(IDESTTS)
	LD	B,A		; NEW
	AND	%00000001	; TEST ERROR BIT
	SCF			; NEW
	RET	Z

	LD	A,B		; NEW
	AND	%00100000
	SCF
	JP	NZ,IDE_ERR	; TEST WRITE ERROR BIT
	
	IN	A,(IDEERR)	; READ ERROR FLAGS

IDE_ERR:
	OR	A		; CARRY 0 = ERROR
	RET			; IF A = 0, IDE BUSY TIMED OUT

;-----------------------------------------------------------------------------
	
IDE_WAIT_BUFFER:
	
	LD	DE,0

IDE_WDRQ:
	LD	B,5

IDE_BLP:
	DEC	B
	JP	NZ,IDE_BLP

	INC	DE
	LD	A,D
	OR	E
	JP	Z,IDE_TO2

	IN	A,(IDESTTS)	; WAIT FOR DRIVE'S 512 BYTE READ BUFFER 
	AND	%00001000	; TO FILL (OR READY TO FILL)
	JP	Z,IDE_WDRQ

	SCF			; CARRY 1 = OK
	RET

IDE_TO2:
	XOR	A		; CARRY 0 = TIMED OUT
	RET

;------------------------------------------------------------------------------

IDE_READ_BUFFER:

	PUSH	HL
	LD	HL,SECTOR_BUFFER
	LD	B,0		; 256 WORDS (512 BYTES PER SECTOR)

IDEBUFRD:
	IN	A,(IDELO)	; LOW BYTE OF WORD FIRST	
	LD	(HL),A
	IN	A,(IDEHI)	; THEN HIGH BYTE OF WORD
	INC	HL
	LD	(HL),A
	INC	HL
	DEC	B
	JP	NZ,IDEBUFRD

	POP	HL
	RET

;-----------------------------------------------------------------------------

IDE_WRITE_BUFFER:

	PUSH	HL
	LD	HL,SECTOR_BUFFER
	LD	B,0		; 256 WORDS (512 BYTES PER SECTOR)

IDEBUFWT:

	INC	HL
	LD	A,(HL)
	DEC	HL
	OUT	(IDEHI),A	; SET UP HIGH LATCHED BYTE BEFORE
	LD	A,(HL)
	OUT	(IDELO),A	; WRITING WORD WITH WRITE TO LOW BYTE
	INC	HL
	INC	HL
	DEC	B
	JP	NZ,IDEBUFWT

	POP	HL
	RET
	
;-----------------------------------------------------------------------------

IDE_SETUP_LBA:
	
	LD	A,1
	OUT	(IDESECTC),A	; SET SECTOR COUNT = 1	

	LD	A,(IDE_LBA0)
	OUT	(IDESECTN),A	; SET LBA 0:7

	LD	A,(IDE_LBA1)
	OUT	(IDECYLLO),A	; SET LBA 8:15

	LD	A,(IDE_LBA2)
	OUT	(IDECYLHI),A	; SET LBA 16:23

	LD	A,(IDE_LBA3)
	AND	%00001111	; LOWEST 4 BITS USED ONLY
	OR	%11100000	; TO ENABLE LBA MODE
	OUT	(IDEHEAD),A	; SET LBA 24:27 + BITS 5:7=111
	RET

;-----------------------------------------------------------------------------
ATAPI_SOFT_RESET:

	LD	A,%00001110		;NO INTERRUPTS, RESET DRIVE = 1
	OUT	(IDECTRL),A		;
	CALL	DELAY			;
	LD	A,%00001010		;NO INTERRUPTS, RESET DRIVE = 0
	OUT	(IDECTRL),A		;
	CALL	ATAPI_WAIT_BUSY_READY	;
	CALL	ATAPI_DEVICE_SELECTION	;
	CALL	DELAY			;
	CALL 	REQUEST_SENSE_LOOP	;
	RET


REQUEST_SENSE_LOOP:
	LD	HL,ATAPI_REQUEST_SENSE	;
	CALL	ATAPI_SEND_PACKET	;
	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
REQUEST_SENSE_LOOP1:
	IN	A,(IDELO)		;
	INC	IX			;
	IN	A,(IDEHI)		;
	INC	IX			;
	DJNZ	REQUEST_SENSE_LOOP1	;
	CALL	DELAY			;
	IN	A,(IDESTTS)		;READ ERROR REG
	AND	%00000001		;MASK OFF BIT
	JR	NZ,REQUEST_SENSE_LOOP	;
	RET

;--------------------------------------------------------------------------------
; IDE SELECT DEVICE 
;--------------------------------------------------------------------------------
ATAPI_DEVICE_SELECTION:

	LD	A,(IDEDEVICE)		; Selects Device
	OR	0A0H			;
	OUT	(IDEHEAD),A		;	
	RET				;

;--------------------------------------------------------------------------------
; DELAY 
;--------------------------------------------------------------------------------
DELAY:


	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;
	NOP				;	
	RET
					;



;__ATAPI_READ_SECTOR_____________________________________________________________________________________________________________ 
;  READ ATAPI SECTOR   
;
;   D E H L = SECTOR (DOUBLE WORD) TO READ 
;________________________________________________________________________________________________________________________________ 
ATAPI_READ_SECTOR:
	LD	A,D			; STORE SECTOR ADDRESS TO PACKET
	LD	(READ_DISK_PACKET+2),A	;
	LD	A,E			;
	LD	(READ_DISK_PACKET+3),A	;
	LD	A,H			;
	LD	(READ_DISK_PACKET+4),A	;
	LD	A,L			;
	LD	(READ_DISK_PACKET+5),A	;
	CALL	REQUEST_SENSE_LOOP	; GET ATAPI SENSE CODES TO CLEAR ERRORS
	LD	HL,READ_DISK_PACKET	; SET POINTER TO READ SECTOR PACKET
	CALL	ATAPI_SEND_PACKET	; SEND PACKET COMMAND
	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
	LD	IX,SECTOR_BUFFER	;
	IN	A,(IDESTTS)		; READ  REG
	AND	%00001000		; MASK OFF BIT
	CP	08H			; IS DATA WAITING?
	JR	NZ,ATAPI_READ_DATA_EXIT	; NO, EXIT
ATAPI_READ_DATA_LOOP:
	IN	A,(IDELO)		;
	LD	(IX),A			;
	INC	IX			;
	IN	A,(IDEHI)		;
	LD	(IX),A			;
	INC	IX			;
	DJNZ	ATAPI_READ_DATA_LOOP	;
ATAPI_READ_DATA_EXIT:
	RET				;



;__ATAPI_WRITE_SECTOR_____________________________________________________________________________________________________________ 
;  WRITE ATAPI SECTOR   
;
;   D E H L = SECTOR (DOUBLE WORD) TO WRITE 
;________________________________________________________________________________________________________________________________ 
ATAPI_WRITE_SECTOR:
	LD	A,D			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+2),A	;
	LD	A,E			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+3),A	;
	LD	A,H			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+4),A	;
	LD	A,L			; STORE SECTOR ADDRESS INTO PACKET
	LD	(WRITE_DISK_PACKET+5),A	;
	CALL 	REQUEST_SENSE_LOOP	;
	LD	HL,WRITE_DISK_PACKET	; SET POINTER TO WRITE PACKET COMMAND
	CALL	ATAPI_SEND_PACKET	; SEND THE PACKET COMMAND						
	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	LD	B,0			; 256 WORDS (512 BYTES PER SECTOR)
	LD	IX,SECTOR_BUFFER	;
ATAPI_WRITE_DATA_LOOP:
	IN	A,(IDESTTS)		; READ  REG
	LD	A,(IX)			;
	PUSH    AF			;
	INC	IX			;
	LD	A,(IX)			;
	OUT	(IDEHI),A		;
	POP	AF			;
	OUT	(IDELO),A		;
	INC	IX			;
	DJNZ	ATAPI_WRITE_DATA_LOOP	;
	RET				;




;__ATAPI_SEND_PACKET_____________________________________________________________________________________________________________ 
;  SEND PACKET POINTED TO BY HL TO ATAPI DRIVE   
;
;________________________________________________________________________________________________________________________________ 
ATAPI_SEND_PACKET:

	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	CALL	IDE_WAIT_DRQ_ZERO	;
					;
	LD	A,0AH			;
	OUT	(IDECTRL),A		; Disable INT
	LD	A,00H			;
	OUT	(IDEERR),A		;
	LD	A,00H			;
	OUT	(IDESECTC),A		; 
	LD	A,00H			;
	OUT	(IDESECTN),A		;
	LD	A,00H			;
	OUT	(IDECYLLO),A		; 
	LD	A,60H			;
	OUT	(IDECYLHI),A		; 
	LD	A,(IDEDEVICE)		;
	OUT	(IDEHEAD),A		; Bit 4 selects device
	LD	A,0A0H			;
	OUT	(IDESTTS),A		;
					;
	CALL	IDE_WAIT_DRQ_READY	; MAKE SURE DRIVE IS READY TO PROCEED
					;
	LD	B,6			; SEND 12 BYTES (6 WORDS)
					;
ATAPI_SEND_PACKET_LOOP:
	LD	A,(HL)			; GET BYTE
	LD	D,A			; STORE LOW BYTE IN D
	INC	HL			; INC POINTER
	LD	A,(HL)			; GET HIGH BYTE
	OUT	(IDEHI),A		; STORE HIGH BYTE
	LD	A,D			; MOVE LOW BYTE INTO A
	OUT	(IDELO),A		; STORE LOW BYTE
	INC	HL			; INC POINTER
	IN	A,(IDECTRL)		; GET STATUS
	DJNZ	ATAPI_SEND_PACKET_LOOP	; LOOP
					;
	CALL	ATAPI_WAIT_BUSY_READY	; MAKE SURE DRIVE IS READY TO PROCEED
	IN	A,(IDECTRL)		; READ STATUS (FOR DELAY)
					;
	RET				;

READ_DISK_PACKET
	.DB	0A8H,00H,00H,00H,00H,01H,00H,00H,00H,01H,00H,00H
WRITE_DISK_PACKET
	.DB	2AH,00H,00H,00H,00H,11H,00H,00H,01H,00H,00H,00H
ATAPI_REQUEST_SENSE
	.DB	03H,00H,00H,00H,011H,00H,00H,00H,00H,00H,00H,00H



;
;	THE REMAINDER OF THE CBIOS IS RESERVED UNINITIALIZED
;	DATA AREA, AND DOES NOT NEED TO BE A PART OF THE
;	SYSTEM MEMORY IMAGE (THE SPACE MUST BE AVAILABLE,
;	HOWEVER, BETWEEN "BEGDAT" AND "ENDDAT").
;
TRACK:	.DS 2			; TWO BYTES FOR TRACK #
PAGER:	.DB 1			; COPY OF PAGER BYTE
DB_PAGER:
	.DB $FF			; COPY OF PAGER BYTE (debug)
SECTOR:	.DS 2			; TWO BYTES FOR SECTOR #
V_SECTOR:
	.DS 2			; TWO BYTES FOR VIRTUAL SECTOR #
SECST:	.DS 2			; SECTOR IN ROM/RAM START ADDRESS
DMAAD:	.DS 2			; DIRECT MEMORY ADDRESS
DISKNO:	.DS 1			; DISK NUMBER 0-15
LBA_OFFSET_LO:
	.DW			; IDE HD PARTITION STARTING SECTOR (LOW 16 BITS)
LBA_OFFSET_HI:
	.DW			; IDE HD PARTITION STARTING SECTOR (HI 16 BITS, 12 USED)
LBA_TARGET_LO:
	.DW			; IDE HD PARTITION TARGET SECTOR (LOW 16 BITS)
LBA_TARGET_HI:
	.DW			; IDE HD PARTITION TARGET SECTOR (HI 16 BITS, 12 USED)
IDEDEVICE:
	.DS $01			; ATAPI DEVICE SELECTION FLAG

IDE_LBA0:
	.DS $01			; SET LBA 0:7
IDE_LBA1:
	.DS $01			; SET LBA 8:15
IDE_LBA2:
	.DS $01			; SET LBA 16:23
IDE_LBA3:
	.DS $01			; LOWEST 4 BITS USED ONLY TO ENABLE LBA MODE 
SECTOR_INDEX:
	.DB 1			; WHERE 128 BYTE CP/M SECTOR IS IN 512 BYTE IDE HD SECTOR
SECTOR_BUFFER:
	.DS 512			; STORAGE FOR 512 BYTE IDE HD SECTOR
;TMPBUF: 	.DS	128		; TEMPORARY BUFFER FOR DISK TRANSFERS

TMPBUF:	.EQU SECTOR_BUFFER

;
;	SCRATCH RAM AREA FOR BDOS USE
BEGDAT:	.EQU $			; BEGINNING OF DATA AREA
DIRBF:	.DS 128			; SCRATCH DIRECTORY AREA
ALL00:	.DS 5			; ALLOCATION VECTOR 0  (DSM/8 = 1 BIT PER BLOCK)
ALL01:	.DS 32			; ALLOCATION VECTOR 1 (225/8)
ALL02:	.DS 254			; ALLOCATION VECTOR 2 (511/8)
ALL03:	.DS 254			; ALLOCATION VECTOR 3 (511/8)
ALL04:	.DS 65			; ALLOCATION VECTOR 4 (497/8)
ALL05:	.DS 65			; ALLOCATION VECTOR 4 (495/8)
CHK00:	.DS 0			; NOT USED FOR FIXED MEDIA
CHK01:	.DS 0			; NOT USED FOR FIXED MEDIA
CHK02:	.DS 0			; NOT USED FOR FIXED MEDIA
CHK03:	.DS 128			; NOT USED FOR FIXED MEDIA
CHK04:	.DS 0			; NOT USED FOR FIXED MEDIA
CHK05:	.DS 0			; NOT USED FOR FIXED MEDIA
;
ENDDAT:	.EQU $			; END OF DATA AREA
DATSIZ:	.EQU $-BEGDAT		; SIZE OF DATA AREA





	.IF	CONDUSRPATCH
		; original is not to have this 
	.ELSE

;*****************************************
; Patch to print USER number in CCP prompt
;
USRPAT:

UPAT1:	CALL	GETUSR			;call ccp routine to
					; get current user number.
	CP	10			;check if zero thru nine
					; or if two digits
					; ten thru fifteen.
	JP	NC,UPDBL		;jump if two digits.
	ADD	A,30h			;else convert to ascii.
UPSING:	CALL	PRINT			;call ccp conout routine.
	RET				;return where ccp left off.
					;
UPDBL:	ADD	A,26h			;subtract 10 from the user
					; number; the remainder 
					; will be the second digit.
					; Add 30 to convert it to 
					; ascii or just add 26h to
					; accomplish both.
	PUSH	AF			;save the second digit
	LD	A,31h			;load an ascii 'one'
	CALL	PRINT			;call ccp conout routine
	POP	AF			;return second digit
	JP	UPSING			;output second digit
					; and return
	RET

	.ENDIF

;	******* TEXT STRINGS *******

TXT_RO_ERROR:
	.BYTE CR,LF
	.BYTE "ERROR: WRITE TO READ ONLY DISK"
	.BYTE END


TXT_STARTUP_MSG:

	.IF 	CONDSHORTMSG

		.BYTE CR,LF
		.BYTE "CP/M-80 VERSION 2.2C FOR THE "
		.BYTE "N8VEM - W/ATAPI"
		.BYTE CR,LF
		.BYTE END

	.ELSE

		.BYTE "CP/M V2.2C"
		.BYTE END

	.ENDIF


	.IF 	CONDTSR
		; add nothing
	.ELSE

		; TSR entry points here - put a call xx then ret at 5 locations
		; can add any code above $F800 as this is not used by anything
		; extra code added by seperate programs usually run at startup
		; and these programs poke a jump at each of these locations
		; if not run, these routines simply return

		.org $F2F0		; add LCD output routine here F900-FBFF
TSR1:		ret
		.org $F2F3		; add counter here uses 64 bytes F800 to F83F
TSR2:		ret
		.org $F2F6		; add scan IBM keyboard routine here FC00 to FFFF
TSR3:		ret
		.org $F2F9		; echooff 16 bytes F840 to F84F
TSR4:		ret			
		.org $F2FC		; blank
TSR5:		ret



	.ENDIF


;	.ORG $F2FF
;LASTBYTE:	.DB $00

	.END
