	PAGE	DISK FILE/DEVICE DRIVER
*	DISK DEVICE DRIVER ENTRY VECTOR
*
DISKDEVICEDRIVER	EQU	*
	FDB	DSKDOPEN	OPEN
	FDB	DSKDCLOSE	CLOSE
	FDB	DSKDREADA	READA
	FDB	DSKDWRITEA	WRITEA
	FDB	DSKDREADB	READB
	FDB	DSKDWRITEB	WRITEB
	FDB	DSKDOPEN	CREATE
	FDB	ILLDEVICEOP	RENAME
	FDB	ILLDEVICEOP	DELETE
	FDB	DSKDCONTROL	CONTROL
	FDB	DSKDSTATUS	STATUS
	FDB	DISKRTS	RESET
	FDB	ILLDEVICEOP	PFRESTART

ILLDEVICEOP	; ILLEGAL DEVICE OPERATION ATTEMPTED
	JSR	ERRET
	FDB	ERR:ILLDEVICEOP
*
DISKRTS	OKRTS
	PAGE
*
*	DISK FILE DRIVER ENTRY VECTOR
*
DISKFILEDRIVER	EQU	*
	FDB	DSKFOPEN
	FDB	DSKFCLOSE
	FDB	DSKFREADA
	FDB	DSKFWRITEA
	FDB	DSKFREADB
	FDB	DSKFWRITEB
	FDB	DSKFCREATE
	FDB	DSKFRENAME
	FDB	DSKFDELETE
	FDB	DSKFCONTROL
	FDB	DSKFSTATUS
	FDB	DSKFRESET
	FDB	DSKFPFRESTART
*
*	DSKFPFRESTART -- DISK FILE DRIVER POWER FAIL RESET
*
DSKFPFRESTART	EQU	*
	JSR	ERRET
	FDB	ERR:PWRFAILDISKF
	PAGE
*
*	DSKFCREATE -- CREATE A NEW DISK FILE
*
DSKFCREATE	EQU	*
	JSR	PLANTDCBINIOCB	SO DISK I/O ROUTINES CAN FIND DRIVERS
	JSR	PARSEFILENAME	GO PROCESS THE DISK FILE NAME
	JSR	SEARCHDIRCREATE	GO FIND OUT IF FILE ALREADY EXISTS
	JSR	SEARCHFCBINIT	SET UP TO SEARCH FCBS
	JSR	SEARCHFCBS	IS FILE OPEN ALREADY?
	BCS	DSKFCREATE1	B/ FILE IS NOT OPEN ANYWHERE
	LDAA	FCB:FLAGS,X	FILE BY THAT NAME IS ALREADY OPEN
	BITA	#FCBFLG::NEWFILE	A NEW FILE ?
	BNE	ERRFILEINCREATE	B/ FILE ALREADY BEING CREATED
*	LDAA	FILEREPLACEMENTFLAG	IS "!" PRESENT TO ALLOW FILE OVERWRITE?
*	BNE	ERROLDFILEEXISTS	B/ NO, CAN'T OVERWRITE OLD FILE!
	LDAA	FCB:PROT,X	IS OLD FILE WRITE PROTECTED ?
	BITA	#PROT::WRITE	IS FILE WRITE PROTECTED ?
	BNE	ERRFILEWRTPROT	B/ YES, CAN'T CREATE A NEW ONE
	JSR	SEARCHFCBS	OLD FILE IS OPEN ALREADY
	BCS	DSKFCREATEF	B/ NO REPLACEMENT EXISTS FOR OLD FILE
ERRFILEINCREATE	; FILE BY THAT NAME IS BEING CREATED
	JSR	ERRET
	FDB	ERR:FILEINCREATE
	PAGE
DSKFCREATE1	; OLD VERSION OF FILE IS NOT OPEN
	LDX	DIRENTRY	IS DIRECTORY ENTRY A FREE ENTRY?
	LDAA	DIR:HCSIC,X
	BEQ	DSKFCREATEC	B/ YES, GO COPY NAME TO DIRECTORY
*	LDAA	FILEREPLACEMENTFLAG	IS "!" PRESENT TO ALLOW FILE OVERWRITE?
*	BNE	ERROLDFILEEXISTS	B/ NO, CAN'T OVERWRITE OLD FILE!
	LDAA	DIR:PROTECTION,X	NO, IS OLD FILE DELETE PROTECTED ?
	BITA	#PROT::WRITE	IS FILE WRITE PROTECTED ?
	BEQ	DSKFCREATEF	B/ NO, WE CAN DO THE CREATE
ERRFILEWRTPROT	EQU	*
	JSR	ERRET
	FDB	ERR:FILEWRTPROT

*	UPDATEFILEDATE -- SET CREATION DATE OF FILE TO CURRENT DATE
*	THIS ROUTINE IS CALLED WHENEVER A FILE IS MODIFIED
*	FCBPOINTER SPECIFIES WHICH FILE
*
UPDATEFILEDATE
	LDX	FCBPOINTER	GET POINTER TO DESIRED FILE
	LDA	FCB:PROT,X	IS FILE WRITE PROTECTED?
	BITA	#PROT::WRITE	...?
	BNE	ERRFILEWRTPROT	B/ YES, DON'T ALLOW UPDATE!
	LDB	CODE+SDOS:DAY	STAMP THE FCB WITH THE CREATION DATE
	BEQ	ERRTIMENOTSET	B/ TIME NOT SET, CANNOT MODIFY FILE!
	ANDA	#(\PROT::BACKUP)&$FF	TURN OFF "FILE IS BACKED UP" FLAG
	STA	FCB:PROT,X
	STB	FCB:DAY,X	TIME STAMP FILE WITH DATE OF CHANGE
	LDD	CODE+SDOS:MONTH
	STD	FCB:MONTH,X
	LDA	FCB:FLAGS,X	UPDATE FILE FLAGS...
	ORAA	#FCBFLG::UPDATEDIR!FCBFLG::MODIFIED	REMEMBER THAT THIS DATA MUST GO TO DISK
	STAA	FCB:FLAGS,X
	OKRTS

ERRTIMENOTSET ; CLOCK DATE IS ZEROED, CAN'T CREATE A FILE!
	JSR	ERRET
	FDB	ERR:TIMENOTSET
	PAGE
DSKFCREATEC	EQU	*
	JSR	COPYNAMETODIR	COPY FILE NAME TO DIRENTRY
DSKFCREATEF	; NOW CREATE A NEW FILE IN AN FCB
***	JSR	DRIVER:WRTPROTCHECK	VERIFY THAT DISK DRIVE IS NOT WRITE PROTECTED
	JSR	FINDFREEFCB	GO FIND AN FCB TO USE
	CLR	FCB:HCSIC,X	INITIALIZE FCB FOR NEW FILE
	CLR	FCB:FILESIZE,X	RESET FILE SIZE TO ZIP
	CLR	FCB:FILESIZE+1,X
	CLR	FCB:FILESIZE+2,X
	CLR	FCB:FILESIZE+3,X
	LDAA	#FCBFLG::NEWFILE	MARK FCB AS "NEW FILE"
	STAA	FCB:FLAGS,X
	CLR	FCB:PROT,X	RESET THE PROTECTION BITS
	BSR	UPDATEFILEDATE	UPDATE FILE DATE
	BCS	DSKFCREATEOOPS	B/ TIME NOT SET, CAN'T CREATE FILE!
	JSR	DSKFOPEN1	GO SET UP IOCB TO REMEMBER FCB
	CLR	IOCB:RDCN,X	TELL ALLOCATOR TO START ...
	LDAA	#1	WITH RDCN 1 (= 1ST DATA CLUSTER)
	STAA	IOCB:RDCN+1,X
	LDX	FILESIZERQSTD	DID USER SPECIFY INITIAL FILESIZE ?
	BNE	DSKFCREATE3	B/ YES, USE SPECIFIED FILE SIZE
	LDX	FILESIZERQSTD+2
	BEQ	DSKFCREATEMIN	B/ NO, USE MINIMUM ALLOCATION
DSKFCREATE3	EQU	*
	LDX	IOCBPOINTER	COPY FILESIZE DESIRED...
	LDD	FILESIZERQSTD	TO IOCB:CURBYTE SO WE CAN USE COMPUTERDCN...
	STD	IOCB:CURBYTE,X	TO DETERMINE HOW MANY CLUSTERS TO ALLOCATE
*** NOTE: THIS CAN ACTUALLY ALLOCATE 1 CLUSTER TOO MANY, SINCE...
*	COMPUTERDCN COMPUTES CLUSTER # THAT CONTAINS THE SPECIFIED BYTE!
*	TO BE EXACT, NEED TO SUBTRACT 1 FROM "CURBYTE"
*	BUT THATS OK, SINCE CLOSE WILL FREE THE EXTRA CLUSTER
	LDD	FILESIZERQSTD+2
	STD	IOCB:CURBYTE+2,X
	JSR	COMPUTERDCN	COMPUTE # DATA CLUSTERS NEEDED
	BCS	DSKFCREATEOOPS	B/ SOME ERROR OCCURRED!
	BRA	DSKFCREATEALLOC	GO ALLOCATE THE DISK SPACE
	PAGE
DSKFCREATEMIN	; USE MINIMUM ALLOCATION
	LDX	DCBPOINTER	WE NEED THIS, SO WE CAN...
	LDD	DSKINFO:MINALLOC,X	USE MIN ALLOCATION FOR FILE CREATES
DSKFCREATEALLOC	; ALLOCATE SPACE TO A NEW FILE
	JSR	ALLOCATECLUSTERS	GO INVENT NEW FILE HEADER CLUSTER
	BCS	DSKFCREATEE	B/ RAN OUT OF DISK SPACE
DSKFCREATE2	EQU	*
	BRA	DSKFOPEN1	RESET THE IOCB TO MAKE SURE RDCN=DUMMY

DSKFCREATEE	; RAN OUT OF DISK SPACE WHILE ALLOCATING FILE
	CPX	#ERR:NODISKSPACE	DOUBLE-CHECK, MIGHT BE SOMETHING ELSE
	BNE	DSKFCREATEOOPS	IT WAS SOMETHING ELSE, CLOSE UP SHOP!
	LDX	FCBPOINTER	RAN OUT OF SPACE ALRIGHT!
	LDX	FCB:NCLUSTERS,X	WAS HEADER CLUSTER ALLOCATED?
	BNE	DSKFCREATE2	B/ YES, TREAT LIKE NORMAL FILE CREATE
DSKFCREATEOOPS	; DIDN'T CREATE FILE, OR SOME OTHER PROBLEM
	LDX	FCBPOINTER	FREE THE FCB, ITS NOT VALID ANYWAY
	CLR	FCB:REFCOUNT,X
	JMP	ERRORED	AND TRAP UPWARDS
	PAGE
*
*	DSKFOPEN -- OPEN A DISK FILE
*	ON ENTRY DCBPOINTER HAS BEEN SET UP
*
DSKFOPEN	EQU	*
	JSR	PLANTDCBINIOCB	SO DISK I/O ROUTINES CAN FIND DRIVERS
	JSR	PARSEFILENAME	GO PROCESS THE DISK FILE NAME
	JSR	SEARCHDIR	GO LOOK IT UP IN DIRECTORY
	LDAA	DIR:HCSIC,X	DOES FILE EXIST ?
	BEQ	ERRFILENOTFNDJ	B/ NOPE
	JSR	SEARCHFCBINIT	SET UP TO SEARCH FCBS
DSKFOPENL	EQU	*
	JSR	SEARCHFCBS	SEE IF FILE ALREADY OPENED
	BCS	DSKFOPEN0	NO FCB SET UP FOR FILE
	LDAA	FCB:FLAGS,X	IS THE FCB FOR A NEW FILE ?
	BITA	#FCBFLG::NEWFILE	...?
	BNE	DSKFOPENL	B/ YES, TREAT AS IF FCB DIDN'T EXIST
	INC	FCB:REFCOUNT,X	NO, FCB IS ALREADY SET UP FOR FILE
	BRA	DSKFOPEN1	PLACE HOLD ON FCB AND GO OPEN
	PAGE
DSKFOPEN0	; NO FCB EXISTS FOR FILE YET
	JSR	FINDFREEFCB	OOPS, NO FCB FOR THIS FILE YET
DSKFOPEN1	; ENTRY POINT FOR DSKFOPENERRORFILE
	LDX	IOCBPOINTER	NOW FILL IN THE IOCB
	LDD	FCBPOINTER	REMEMBER THE FCB ADDRESS
	STD	IOCB:FCB,X
	CLR	IOCB:CURBYTE,X	ASSUME WE WILL START ...
	CLR	IOCB:CURBYTE+1,X	AT BYTE ZERO OF THE FILE
	CLR	IOCB:CURBYTE+2,X
	CLR	IOCB:CURBYTE+3,X
	CLR	IOCB:LOCATEDF,X	MARK "LOCATE OPERATION NEEDED"
	LDD	#DUMMYLCN	SET "LAST EXAMINED DATA CLUSTER"...
	STD	IOCB:RDCN,X
	CLR	IOCB:DRDSI,X	MARK RDSI POINTERS AS "MUST BE READ"
	CLR	IOCB:DRDSI+1,X
	CLR	IOCB:HRDSI,X
	CLR	IOCB:HRDSI+1,X
	LDAA	#-1	SET HRSN = "NONE"
	STAA	IOCB:HRSN,X
	CLR	IOCB:COLCNT,X	START OUT ON COLUMN 0
	JSR	DSKFEOFTEST	AFTER ALL, THE FILE MIGHT BE (SIGH!) EMPTY!
	BCS	*+2	IGNORE EOF HIT ERROR
	LDX	IOCBPOINTER	BECUZ EOF TEST BOMBS (X)!
	OKRTS
*
*	DSKFOPENSYSIOCB -- SET UP SYSIOCB AS THOUGH "OPEN" WAS CALLED
*
DSKFOPENSYSIOCB	EQU	*
	LDX	#SYSIOCB	SET UP IOCBPOINTER
	STX	IOCBPOINTER
	LDX	DCBPOINTER	POKE DCB ADDRESS INTO IOCB
	STX	SYSIOCB+IOCB:DCB
	LDX	#DISKFILEDRIVER	SET UP FILE DRIVER
	STX	SYSIOCB+IOCB:DRIVER
	BRA	DSKFOPEN1	GO SET UP REST OF IOCB FOR FILE I/O
	PAGE
*
*	DSKFDELETE -- DELETE A DISK FILE
*
DSKFDELETE	EQU	*
	JSR	PARSEFILENAME	GO PROCESS THE DISK FILE NAME
	JSR	SEARCHDIR	GO LOOK IT UP IN DIRECTORY
	LDAA	DIR:HCSIC,X	IS THIS DIRECTORY SLOT BUSY ?
ERRFILENOTFNDJ	EQU	*
	BEQ	ERRFILENOTFOUND	B/ NO, NO FILE TO DELETE !
	JSR	SEARCHFCBINIT	SET UP TO SEARCH FCBS
DSKFDELETEL	EQU	*
	JSR	SEARCHFCBS	IS THE FILE ALREADY OPENED?
	BCS	DSKFDELETE1	B/ NO, MUST SET UP FCB TO DELETE
	LDAA	FCB:FLAGS,X	A NEWLY CREATED FILE?
	BITA	#FCBFLG::NEWFILE	...?
	BNE	DSKFDELETEL	B/ YES, IGNORE THIS FCB
	LDAB	FCB:PROT,X	SINCE FILE IS OPEN...
	BITB	#PROT::WRITE	FCB IS MORE UP-TO-DATE THAN DIRECTORY
	BNE	ERRFILEWRTPROTJ	B/ FILE HAS BEEN WRITE PROTECTED
	ORAA	#FCBFLG::DELETED	NEITHER ONE, FORCE DELETION OF FILE ...
	STAA	FCB:FLAGS,X	WHEN IT IS CLOSED
DSKFDELETEDE	; DELETE DIRECTORY ENTRY
	JSR	LOCATEDIRENTRY	ZAP THE DIRECTORY ENTRY NOW!
	CLR	DIR:HCSIC,X	SO FILE NO LONGER APPEARS TO EXIST
	JSR	MARKMODIFIED	MARK DIRECTORY SECTOR AS MODIFIED
	OKRTS
	PAGE
DSKFDELETE1	; MUST SET UP FCB TO DELETE FILE
	LDX	DIRENTRY
	LDAA	DIR:PROTECTION,X	IS FILE WRITE PROTECTED?
	BITA	#PROT::WRITE
	BNE	ERRFILEWRTPROTJ	B/ YES, CAN'T DELETE
	JSR	FINDFREEFCB	INVENT AN FCB FOR THIS STUNT
DSKFDELETEFCB	; DELETE FILE REPRESENTED BY FCB POINTER
	BSR	DSKFDELETEDE	DELETE THE DIRECTORY ENTRY
DSKFDELETESYSIOCB ; DELETE FILE USING SYSTEM IOCB
	BSR	DSKFOPENSYSIOCB	SET UP USING SYSIOCB
DSKFDELETEIOCBFCB ; DELETE FILE REPRESENTED BY IOCBPOINTER AND FCBPOINTER
	LDX	IOCBPOINTER	(DIRENTRY ALREADY DELETED)
	CLR	IOCB:RDCN,X	MARK ZEROTH CLUSTER AS START POINT OF DELETE
	CLR	IOCB:RDCN+1,X
	LDX	FCBPOINTER	DELETE ALL THE CLUSTERS ...
	CLR	FCB:REFCOUNT,X	LET GO OF THIS FCB
	LDD	FCB:NCLUSTERS,X
	JSR	FREECLUSTERS
	LDX	#0	MARK SYSIOCB...
	STX	SYSIOCB+IOCB:DRIVER	AS CLOSED
	OKRTS

ERRFILEWRTPROTJ
	JMP	ERRFILEWRTPROT

ERRFILENOTFOUND	EQU	*
	JSR	ERRET
	FDB	ERR:FILENOTFOUND
	PAGE
*
*	DSKFCLOSE -- CLOSE A DISK FILE
*
DSKFCLOSE	EQU	*
	JSR	GETFCBFROMIOCB	SET UP FCBPOINTER
	LDX	FCBPOINTER	IS FILE OPEN ON MORE THAN ONE CHANNEL ?
	DEC	FCB:REFCOUNT,X	IF SOME OTHER CHANNEL HAS THIS FILE OPEN ...
	BNE	DSKFCLOSEOKRTS	WE'LL LET HIM DO THE CLEANUP WHEN HE CLOSES
	LDAA	FCB:FLAGS,X	HAS THIS FILE BEEN DELETED FROM DIRECTORY?
	BITA	#FCBFLG::DELETED
	BNE	DSKFDELETEIOCBFCB	B/ YES, LET'S DELETE IT!
	BITA	#FCBFLG::MODIFIED	HAS FILE BEEN MODIFIED ?
	BEQ	DSKFCLOSE1	B/ NO, DON'T TRUNCATE!
*
*	NOW SHORTEN UP THE FILE (CHOP OFF ALL DATA CLUSTERS PAST EOF)
*
	JSR	DSKFPOSITIONTOEND	POSITION TO END OF FILE
	JSR	COMPUTERDCN	FIND RELATIVE DATA CLUSTER OF EOF
	BCS	DSKFCLOSE1	B/ OFF END OF HEADER CLUSTER, NOTHING TO DEALLOCATE
	ADDD	#1	ROUND UP TO NEXT DATA CLUSTER
	STD	IOCB:RDCN,X	AND START DELETING THERE
	CLRA		(D):=0 --> DELETE TILL END OF HEADER CLUSTER
	CLRB
	JSR	FREECLUSTERS	GO CHOP OFF THE FILE
DSKFCLOSE1
	LDX	FCBPOINTER	FILE HAS BEEN SHORTENED TO MATCH FILESIZE
	LDA	FCB:FLAGS,X	LOOK AT THE FLAGS AGAIN
	BITA	#FCBFLG::NEWFILE	A NEW FILE?
	BEQ	DSKFCLOSEOLD	B/ NO
	ORAA	#FCBFLG::UPDATEDIR	CHANGE FILE STATUS
	STAA	FCB:FLAGS,X	SO DIRECTORY WILL GET UPDATED
	JSR	LOCATEDIRENTRY	GO GET THE DIRECTORY ENTRY
	LDAA	DIR:HCSIC,X	IS THERE AN OLD FILE HERE?
	BEQ	DSKFCLOSEOLD	B/ NO, TREAT LIKE UPDATE DIRECTORY
*
*	MUST DELETE OLD VERSION OF FILE
*
	JSR	SEARCHFCBINIT	IS OLD FILE OPEN ON SOME CHANNEL?
	JSR	SEARCHFCBS	SEE IF ANOTHER FCB MATCHES
	BCS	DSKFCLOSEDOLD1	B/ NO, JUST DELETE IT
	LDAA	FCB:FLAGS,X	MARK FCB OF OLD FILE...
	ORAA	#FCBFLG::DELETED	AS 'DELETED'
	STAA	FCB:FLAGS,X	SO OLD FILE WILL GO AWAY ...
	LDX	IOCBPOINTER	WHEN IT IS CLOSED
	LDX	IOCB:FCB,X	GET THE FCB OF THE NEW FILE BACK
	STX	FCBPOINTER
DSKFCLOSEOLD	; OLD FILE TO BE CLOSED
	JSR	UPDATEDIRFROMFCB	GO UPDATE THE DIRECTORY IF NEEDED
DSKFCLOSEOKRTS	EQU	*
	OKRTS
	PAGE
DSKFCLOSEDOLD1	; OLD VERSION OF FILE IS NOT OPEN
	LDX	IOCBPOINTER	DAMN! MARK THIS FCB AS BUSY AGAIN
	LDX	IOCB:FCB,X
	INC	FCB:REFCOUNT,X	(SET REFCOUNT TO 1)
	JSR	FINDFREEFCB	COPY OLD FILE INFORMATION
	STX	SYSFCBPOINTER	AND REMEMBER WHERE WE PUT IT
	LDX	IOCBPOINTER	RESTORE FCBPOINTER TO NEW FILE
	LDX	IOCB:FCB,X
	CLR	FCB:REFCOUNT,X	NOW LET GO OF NEW FCB AGAIN
	STX	FCBPOINTER	SINCE SETUPFCB STOMPED ON FCBPOINTER
	JSR	UPDATEDIRFROMFCB	TO RECORD NEW FILE
	LDX	SYSFCBPOINTER	SET UP FCBPOINTER...
	STX	FCBPOINTER	TO SELECT OLD FILE TO BE DELETED
	JMP	DSKFDELETEIOCBFCB	NOW GO DELETE FILE REPRESENTED BY FCBPOINTER
	PAGE
*
*	DSKFRENAME -- RENAME A DISK FILE
*
DSKFRENAME	EQU	*
	JSR	GETFCBFROMIOCB	GET FCB ADDRESS
	JSR	PARSEFILENAME	GO PROCESS THE DISK FILE NAME
	LDA	CODE+SDOS:DAY	IS TIME SET ?
	LBEQ	ERRTIMENOTSET	B/ NO, CAN'T RENAME THE FILE!
	LDX	FCBPOINTER	MAKE SURE WE CAN RENAME THE FILE
	LDAA	FCB:PROT,X	GET PROTECTION BITS
	BITA	#PROT::WRITE	WRITE PROTECTED ?
	LBNE	ERRFILEWRTPROT	B/ YES, CAN'T RENAME!
	LDAA	FCB:FLAGS,X	IS THE FILE ALREADY DELETED ?
	BITA	#FCBFLG::DELETED	...?
	BNE	ERRFILEALREADYDELETED	B/ YES, CAN'T RENAME IT!
	JSR	LOCATEDIRENTRY	PULL IN THE DIRECTORY ENTRY FROM THE DISK
	JSR	SEARCHFCBINIT	YES, GO SEE IF ITS BEING CREATED
DSKFRENAMEL	EQU	*
	JSR	SEARCHFCBS	LOOK FOR AN FCB USING THIS DIRECTORY SLOT
	BCS	DSKFRENAME1	B/ NO, LOOKS GOOD SO FAR
	LDAA	FCB:FLAGS,X	RATS, MUST BE AT LEAST ONE: LOOK AT OPEN FILE
	BITA	#FCBFLG::NEWFILE	IS IT A NEW FILE?
	BEQ	DSKFRENAMEL	B/ NO, IGNORE THIS FCB
	JMP	ERRFILEINCREATE

DSKFRENAMEERR1	; DESIRED NAME IS IN USE
	JSR	GETFCBFROMIOCB	PUT OLD DIRECTORY ENTRY BACK LIKE IT WAS
	LDX	FCBPOINTER	AND THEN GO COMPLAIN!
	DEC	FCB:DIRDISP+1,X	MAKE DIRENTRY DISPLACEMENT VALID ONCE AGAIN
	JSR	LOCUPDATEDIRENTRY	GO LOCATE DIR ENTRY AND UPDATE IT
ERRNEWFILEEXISTS	EQU	*
	JSR	ERRET
	FDB	ERR:NEWFILEEXISTS

ERRFILEALREADYDELETED	EQU	*
	JSR	ERRET
	FDB	ERR:FILEALREADYDELETED
	PAGE
DSKFRENAME1	; NO PROBLEM WITH FILE TO BE RENAMED
	JSR	GETFCBFROMIOCB	SET FCBPOINTER BACK TO FCB BEING RENAMED
	LDX	FCBPOINTER	MAKE SURE THIS FCB DOESN'T MATCH ANY DIRECTORY SLOT
	INC	FCB:DIRDISP+1,X	BY MAKING DIRENTRY DISPLACEMENT INVALID
	LDX	DIRENTRY	LET GO OF THIS DIRECTORY ENTRY
	CLR	DIR:HCSIC,X	(IN CASE DIRECTORY IS FULL)
	JSR	MARKMODIFIED	(ALSO PREVENTS SEARCHDIRCREATE FROM GIVING...)
	LDX	#FILENAMEBUF	(US THE SAME SLOT BACK, THEREBY REHASHING)
	JSR	SEARCHDIRCREATE	GO LOOK FOR FREE DIRECTORY SLOT
*	NO ERROR CHECK NEEDED, BECAUSE FREE DIRECTORY SLOT IS KNOWN TO BE AVAILABLE
	LDAA	DIR:HCSIC,X	IS DIRECTORY ENTRY BUSY?
	BNE	DSKFRENAMEERR1	B/ YES, CAN'T RENAME TO THIS FILE
	JSR	SEARCHFCBINIT	SET UP TO SEARCH FCBS
	JSR	SEARCHFCBS	DOES MATCHING FCB EXIST?
	BCS	DSKFRENAME2	B/ NOBODY USING THIS SLOT
	LDX	IOCBPOINTER	IS THIS SLOT IN USE...
	LDX	IOCB:FCB,X	BY FILE BEING RENAMED ?
	CPX	FCBPOINTER	...?
	BNE	DSKFRENAMEERR1	B/ NO, NEW NAME IS OCCUPIED!
DSKFRENAME2	EQU	*
	JSR	GETFCBFROMIOCB	RESET FCB POINTER SINCE SEARCHFCBS BOMBED IT!
	JSR	UPDATEFILEDATE	MAKE SURE DATE IS MODIFIED (ASSERT: NO ERROR POSSIBLE!)
	JSR	SETUPFCBDIRPOS	GO COPY DIRECTORY ENTRY DATA INTO FCB
	LDX	DIRENTRY	SET UP (X) PROPERLY...
	JSR	UPDATEDIRENTRY	TO SAVE CRITICAL FILE DATA
*	JSR	COPYNAMETODIR	SET UP NEW NAME IN DIRENTRY
*	OKRTS
	PAGE
*
*	COPYNAMETODIR -- COPY "FILENAME" TO DIRENTRY
*		MARKS RDSI AS MODIFIED
*
COPYNAMETODIR	EQU	*
	IF	M6800!M6801
	LDX	DIRENTRY	SET UP TARGET POINTER
	STX	TOPOINTER
	ELSE	(M6809)
	LDY	DIRENTRY
	FIN
	LDX	#FILENAMEBUF	GET SOURCE POINTER
	LDD	#DIR:NAMESIZE	AND # BYTES TO COPY
	JSR	BLOCKMOVE
*	JSR	MARKMODIFIED	SO DIRECTORY ENTRY WILL GO BACK TO DISK
*	RTS
*
*	MARKMODIFIED -- MARKS RDSI(RDSIPOINTER) AS MODIFIED
*
MARKMODIFIED	EQU	*
	LDX	RDSIPOINTER	WHAT CAN I SAY?
	CLR	RDSI:MODIFIED,X	(RESETS CARRY TO MAKE "OKRTS")
	INC	RDSI:MODIFIED,X
	RTS
	PAGE
SWAPPING	EQU	0	******** TAKE NOTE!
	IF	SWAPPING
DSKFREADBOPT	; TRY TO OPTIMIZE NEXT SECTOR READ
	JSR	CHECKTRANSFERDONE	COMPUTE REMAINING # BYTES TO MOVE
	LDX	DCBPOINTER
	SUBD	DSKINFO:NBPS,X
	BCS	DSKFREADBL1	B/ NO, CAN'T TRANSFER A SECTOR'S WORTH
	LDX	FCBPOINTER	ARE THERE ENOUGH BYTES LEFT IN THE FILE ?
	LDD	FCB:FILESIZE,X	(I.E., IS FCB:FILESIZE - IOCB:CURBYTE >=NBPS ?)
	PSHD			(GET FCB:FILESIZE TO (TOS-1,TOS),(A,B))
	LDD	FCB:FILESIZE+2,X
	LDX	FCB:DISKINFO,X	(NOW SUBTRACT DSKINFO:NBPS)
	SUBD	DSKINFO:NBPS,X
	BCC	DSKFREADBO1	B/ NO BORROW BIT TO PROPOGATE
	TST	1,S	DO DOUBLE PRECISION DECREMENT
	BNE	DSKFREADBO2	B/ DON'T HAVE TO DECREMENT UPPER HALF
	IF	M6800!M6801
	DEC	,X	NOTE: THIS MIGHT GO NEGATIVE!
DSKFREADBO2	EQU	*
	DEC	1,X	PROPOGATE THE BORROW
	ELSE	(M6809)
	DEC	0,S
DSKFREADBO2
	DEC	1,S
	FIN
DSKFREADBO1	EQU	*
	LDX	IOCBPOINTER	NOW SUBTRACT IOCB:CURBYTE...
	SUBD	IOCB:CURBYTE+2,X	SO WE CAN FIND OUT IF FCB:FILESIZE-IOCB:CURBYTE...
	PULD		-DSKINFO:NBPS>=0
	SBCB	IOCB:CURBYTE+1,X
	SBCA	IOCB:CURBYTE,X
	BMI	DSKFREADBL1	B/ NOPE, NOT ENOUGH BYTES LEFT IN FILE
	BVS	DSKFREADBL1	THOROUGH BASTARD, AREN'T I ?
****NEEDS WORK!!!*****
	JSR	LOCATESECTOR	SEEMS WE CAN DO OPTIMIZED READ
	JSR	READDIRECT	DO DIRECT READ
DSKFREADBO3	; UPDATE IOBLOCK
	LDX	DCBPOINTER	NOW ADJUST EVERYTHING
	LDD	DSKINFO:NBPS,X	ADD NBPS TO ACTUAL COUNT READ
	LDX	CODE+SDOS:IOBLOCKPTR
	ADDD	READB:ACTUALCOUNT,X
	STD	READB:ACTUALCOUNT,X
	BRA	DSKFREADBOPT	GO SEE IF WE CAN TRANSFER ANOTHER BLOCK
	FIN
	PAGE
*
*	DSKFREADB -- DISK FILE READ BINARY
*
DSKFREADB	EQU	*
	JSR	DSKFIMPLIEDPOSITION	HANDLE IMPLIED POSITIONING IF GIVEN
	JSR	SETUPFORREAD	COPY BUFFER ADDRESS TO BUFFERPOINTER
*			AND SET UP FCB AND DISKINFO POINTERS
	CLR	IOCB:COLCNT,X	(YOU DID A READ BINARY, YOUR COL COUNT GETS ZAPPED)
DSKFREADBL	; READ BINARY BYTE LOOP
	IF	SWAPPING
	LDX	IOCBPOINTER	BECAUSE AN (EOF) ERROR BOMBS (X)
	LDD	IOCB:CURBYTE+2,X	AT A SECTOR BOUNDARY (I.E., IS CURBYTE MOD NBPS = 0 )?
	LDX	IOCB:DCB,X	AT SECTOR BOUNDARY ?
	BITB	DSKINFO:NBPSM1+1,X	(AND WITH NBPS - 1 FOR QUICK CHECK)
	BNE	DSKFREADBL1	B/ NO, READ A BYTE AT A TIME
	BITA	DSKINFO:NBPSM1,X	... ?
	BEQ	DSKFREADBOPT	B/ YES, MAYBE WE CAN DO A DIRECT READ!
DSKFREADBL1	; MUST READ INTO RDSI BUFFER AND COPY
	FIN
	JSR	CHECKREADDONE	ALL BYTES REQUESTED COPIED ?
	BEQ	DSKFREADBOKRTS	B/ YES, ALL DONE
	JSR	LOCATECURBYTE	LOCATE THE BYTE WE WANT
	BSR	DSKREADBCHUNK	GO COPY SOME BYTES TO THE DISK
	JSR	UPDATECURBYTEF	ADJUST CURBYTE
	BCC	DSKFREADBL	GO SEE IF MORE BYTES TO COPY
	BRA	DSKFREADBL	IGNORE EOF ERROR

DSKFREADBOKRTS	; READ BINARY COMPLETED SUCCESSFULLY
	OKRTS
	PAGE
*	DSKDREADB -- DISK DEVICE READ BINARY
*
DSKDREADB	EQU	*
	JSR	DSKDIMPLIEDPOSITION	HANDLE IMPLIED POSITIONING IF GIVEN
	JSR	SETUPFORREAD	AND SET UP DCBPOINTER
DSKDREADBL	EQU	*
	JSR	CHECKREADDONE	SEE IF ANY MORE BYTES TO MOVE TO BUFFER
	BEQ	DSKFREADBOKRTS	B/ ALL BYTES COPIED
	JSR	LOCDSKD	NO, SET UP PARAMETERS FOR THE COPY
	BSR	DSKREADBCHUNK	GO COPY THE CHUNK OF BYTES SET UP BY LOCDSKD
	JSR	UPDATECURBYTED	UPDATE POINTERS AND STUFF FOR DISK DEVICE
	BCC	DSKDREADBL	AND LOOP UNTIL USER'S BUFFER IS FILLED
	BRA	DSKDREADBL	IGNORE EOF ERROR
*
*	DSKREADBCHUNK -- READ A CHUNK OF BYTES FROM FILE TO BUFFER
*		CHUNK PARAMETERS ARE ALL SET UP BY LOCATECURBYTE
*
DSKREADBCHUNK	EQU	*
	LDX	CODE+SDOS:IOBLOCKPTR	GET THE # BYTES MOVED SO FAR
	LDD	READB:ACTUALCOUNT,X
	ADDD	USEDCOUNT	ADD # BYTES WE'LL MOVE THIS TIME
	STD	READB:ACTUALCOUNT,X
	IF	M6800!M6801
	LDX	BUFFERPOINTER	SET "TO" POINTER
	STX	TOPOINTER
	ELSE	(M6809)
	LDY	BUFFERPOINTER
	FIN
	LDX	IOCBPOINTER	GET THE SOURCE POINTER
	LDX	IOCB:NEXTBYTE,X
	LDD	USEDCOUNT	FETCH TRANSFER COUNT
	JSR	CODE+SDOS:BLOCKMOVE	GO MOVE THE BYTES
*	NOTE: THIS USES SDOS ENTRY POINT VECTOR...
*	SO THAT A PATCH TO ENTRY POINT VECTOR CAN SUPPLY A DIFFERENT...
*	(PRESUMABLY MUCH FASTER) BLOCK MOVE ROUTINE OF ITS OWN!
	IF	M6800!M6801
	LDX	TOPOINTER	UPDATE BUFFER POINTER PAST COPIED BYTES
	STX	BUFFERPOINTER
	ELSE	(M6809)
	STY	BUFFERPOINTER
	FIN
	RTS
	PAGE
*
*	DSKFREADA -- DISK FILE READ ASCII
*
DSKFREADA	EQU	*
	JSR	DSKFIMPLIEDPOSITION	HANDLE IMPLIED POSITIONING IF REQUESTED
	JSR	SETUPFORREAD	COPY BUFFER ADDRESS TO BUFFERPOINTER
*			AND SET UP FCB AND DISKINFO POINTERS
DSKFREADAL	EQU	*
	JSR	CHECKREADDONE	ALL BYTES READ THAT WERE REQUESTED ?
	BEQ	DSKFREADADONE	B/ YES, DON'T READ ANY MORE BYTES
	JSR	LOCATECURBYTE	LOCATE THE BYTE WE WANT
	BSR	DSKREADACHUNK	READ A CHUNK OF BYTES SELECTED BY LOCATECURBYTE
	BNE	DSKFREADALINE	B/ LINE MODE INPUT COMPLETED
	JSR	UPDATECURBYTEF	ADJUST CURBYTE
	BCC	DSKFREADAL	GO CHECK FOR MORE BYTES TO MOVE
	BRA	DSKFREADAL	IGNORE EOF ERROR

DSKFREADALINE	EQU	*
	JSR	UPDATECURBYTEF	UPDATE CURBYTE FOR THE FILE
	BCS	DSKFREADADONE	IGNORE EOF ERROR
DSKFREADADONE	; DONE FILLING SYSCALL REQUEST
	LDAA	COLUMNCOUNT	COPY COLUMN COUNT BACK TO IOCB
	LDX	IOCBPOINTER
	STAA	IOCB:COLCNT,X
	OKRTS
	PAGE
*
*	DSKDREADA -- DISK DEVICE READ ASCII
*
DSKDREADA	EQU	*
	JSR	DSKDIMPLIEDPOSITION	HANDLE IMPLIED POSITIONING IF REQUESTED
	JSR	SETUPFORREAD	AND SET UP DISKINFO AND COLUMNCOUNT
DSKDREADAL	EQU	*
	JSR	CHECKREADDONE	TO SEE IF ALL DESIRED BYTES WERE COPIED
	BEQ	DSKFREADADONE	B/ ALL DONE, GO CLEAN UP
	JSR	LOCDSKD	GO LOCATE THE CHUNK OF BYTES TO COPY
	BSR	DSKREADACHUNK	READ THEM INTO THE USER'S BUFFER
	BNE	DSKDREADALINE	B/ LINE MODE REQUEST SATISFIED
	JSR	UPDATECURBYTED	UPDATE THE POINTERS FOR THE DISK DEVICE
	BCC	DSKDREADAL	AND GO SEE IF THERE ARE MORE BYTES TO COPY
	BRA	DSKDREADAL	IGNORE EOF ERROR

DSKDREADALINE	EQU	*
	JSR	UPDATECURBYTED	UPDATE POINTERS FOR THE DISK DEVICE
	BCC	DSKFREADADONE	B/ NO ERROR, GET OUT QUICK!
	BRA	DSKFREADADONE	AND GO CLEAN UP
	PAGE
*
*	DSKREADACHUNK -- READ AN ASCII CHUNK OF BYTES SELECTED BY LOCATECURBYTE
*	RETURNS CC=0 IF EXHAUSTED CHUNK, NEED TO READ MORE
*		<>0 IF LINE MODE REQUEST WAS SATISFIED
*	THIS ROUTINE SHOULD TEST ACTIVATION CHARACTER SET LIKE VT DRIVER!
*
DSKREADACHUNK	EQU	*
	LDD	USEDCOUNT	MAKE EASY-TO-INCREMENT 16 BIT COUNT
	NEGD
	STD	TRANSFERCOUNT
	LDX	CODE+SDOS:IOBLOCKPTR	GET THE # BYTES MOVED SO FAR
	LDD	READA:ACTUALCOUNT,X
	ADDD	USEDCOUNT	ADD # BYTES WE'LL MOVE THIS TIME
	STD	READA:ACTUALCOUNT,X
	LDX	IOCBPOINTER	GET THE SOURCE POINTER
	IF	M6800!M6801
	LDX	IOCB:NEXTBYTE,X
	STX	FROMPOINTER
DSKREADACHUNKL	; COPY BYTES FROM SECTOR TO BUFFER
	LDX	FROMPOINTER
	LDA	,X+	COPY A BYTE...
	STX	FROMPOINTER
	ANDA	#ASCII:MASK	A NULL ?
	BEQ	DSKREADANULL	B/ YES, ADJUST ACTUAL COUNT READ
	CMPA	#ASCII:LF	A LINE FEED ?
	BEQ	DSKREADANULL	B/ YES, IGNORE IT
	CMPA	#ASCII:RUBOUT	?
	BEQ	DSKREADANULL	B/ YES, IGNORE IT
	JSR	ADJUSTCOLUMNCOUNT	NO, GO UPDATE THE COLUMN COUNTER
	LDX	BUFFERPOINTER	TO THE USER'S BUFFER
	STA	,X+
	STX	BUFFERPOINTER
	CMPA	#ASCII:CR	END OF LINE CODE ?
	BNE	DSKREADACHUNKL1	B/ NO
	LDX	CODE+SDOS:IOBLOCKPTR	YES, LINE MODE ?
	LDAA	READA:LMFLAG,X	...?
	BNE	DSKREADACR	B/ YES, CR IS IMPORTANT
DSKREADACHUNKL1 ; ADJUST NUMBER OF BYTES TRANSFERRED
	ELSE	(M6809)
	PAGE
	LDY	IOCB:NEXTBYTE,X	GET SOURCE POINTER
	LDU	BUFFERPOINTER	GET TARGET BUFFER ADDRESS
DSKREADACHUNKL	; COPY BYTES FROM SECTOR TO BUFFER
	LDA	,Y+	COPY A BYTE...
	ANDA	#ASCII:MASK	A NULL?
	BEQ	DSKREADANULL	B/ YES
	CMPA	#ASCII:LF	LINE FEED?
	BEQ	DSKREADANULL	B/ YES, IGNORE IT
	CMPA	#ASCII:RUBOUT	RUBOUT?
	BEQ	DSKREADANULL	B/ YES, IGNORE IT
	JSR	ADJUSTCOLUMNCOUNT	NO, GO UPDATE THE COLUMN COUNT
	STA	,U+	STORE THE BYTE INTO THE USER'S BUFFER
	CMPA	#ASCII:CR	END OF LINE MARK?
	BNE	DSKREADACHUNKL1	B/ NO, KEEP COPYING
	LDX	CODE+SDOS:IOBLOCKPTR	YES, LINE MODE?
	LDA	READA:LMFLAG,X	...?
	BNE	DSKREADACR	B/ YES, CR MARKS END OF TRANSFER
DSKREADACHUNKL1	EQU	*
	FIN
	INC	TRANSFERCOUNT+1	DOWN COUNT # BYTES TO MOVE
	BNE	DSKREADACHUNKL	B/ MORE TO MOVE
	IF	M6809
	STU	BUFFERPOINTER	ENSURE BUFFER POINTER IS UP TO DATE IN CASE WE EXIT
	FIN
	INC	TRANSFERCOUNT
	BNE	DSKREADACHUNKL	B/ MORE
	RTS		WITH CC=0 IF ALL BYTES SPECIFIED WERE COPIED
	PAGE
DSKREADANULL	; RATS, ENCOUNTERED A NULL!
	LDX	USERRETAPTR	HAS THIS USER BEEN KILLED ?
	LDD	M6800!M6801,X	(FETCH HIS RETURN ADDRESS)
	CMPD	#KILLPROGRAM	(DOES PC POINT TO KILL PROGRAM?)
	BEQ	DSKREADANULL2	B/ YES, STOP IGNORING NULLS --> RELATIVELY QUICK EXIT!
	LDX	CODE+SDOS:IOBLOCKPTR	ACTUALCOUNT:=ACTUALCOUNT-1
	LDAA	READA:ACTUALCOUNT+1,X	(WE CAN'T COPY THE NULL...
	BNE	DSKREADANULL1	TO THE USER'S BUFFER!)
	DEC	READA:ACTUALCOUNT,X
DSKREADANULL1	EQU	*
	DEC	READA:ACTUALCOUNT+1,X
DSKREADANULL2
	JMP	DSKREADACHUNKL1	GO GRAB THE NEXT BYTE (JMP FOR SPEED)

DSKREADACR	; WE JUST SAW A CR IN LINE MODE
	LDD	READA:ACTUALCOUNT,X	ADJUST THE ACTUAL # BYTES XFERRED
	SEC		(COUNT THE CR BYTE)
	ADCB	TRANSFERCOUNT+1	THESE GUYS...
	ADCA	TRANSFERCOUNT	WE DIDN'T ACTUALLY TRANSFER
	STD	READA:ACTUALCOUNT,X	UPDATE THE ACTUAL # OF BYTES XFERRED
	LDD	USEDCOUNT	ADJUST THE ACTUAL # OF BYTES USED THIS TIME
	SEC		(AGAIN, COUNT THE CR)
	ADCB	TRANSFERCOUNT+1	THESE GUYS WE DIDN'T TRANSFER
	ADCA	TRANSFERCOUNT
	STD	USEDCOUNT
	IF	M6809
	STU	BUFFERPOINTER	ENSURE THAT BUFFER POINTER IS UP TO DATE
	FIN
	LDAA	#1	RETURN WITH CC<>0...
	RTS		SINCE LINEMODE REQUEST WAS SATISFIED
	PAGE
*
*	ADJUSTCOLUMNCOUNT -- ADJUST COLUMNCOUNT ACCORDING TO (A)
*		MASKS PARITY ON (A)
*
ADJUSTCOLUMNCOUNT	EQU	*
	ANDA	#ASCII:MASK	DITCH THE PARITY BIT
	CMPA	#ASCII:SPACE-1	A PRINTING CHARACTER ?
	BHI	ADJUSTCOLBUMP	B/ YEP, GO PROCESS
	CMPA	#ASCII:CR	MUST BE A CONTROL CHARACTER
	BEQ	ADJUSTCOLZERO	B/ NEW LINE MARK, ZERO THE COLUMN COUNT
	CMPA	#ASCII:BS	BACKSPACE ?
	BEQ	ADJUSTCOLBS	B/ YES, GO HANDLE
	CMPA	#ASCII:HT	A TAB CHARACTER ?
	BEQ	ADJUSTCOLTAB	B/ YES, GO PROCESS
	CMPA	#ASCII:FF	FORM FEED ?
	BNE	ADJUSTCOLRTS	B/ NO, LEAVE COLUMN COUNT ALONE
ADJUSTCOLZERO ; MUST ZERO THE COLUMN COUNT
	CLR	COLUMNCOUNT	CR --> ZERO THE COLUMN COUNTER
ADJUSTCOLRTS	EQU	*
	RTS

ADJUSTCOLBUMP	; LOOKS LIKE WE SHOULD BUMP COLUMN COUNT
	CMPA	#ASCII:RUBOUT	ALL CODES > $20 ARE PRINTING CHARACTERS...
	BEQ	ADJUSTCOLRTS	WITH THE EXCEPTION OF RUBOUT
	INC	COLUMNCOUNT	BUMP THE COLUMN COUNT (TOO BAD IF IT OVERFLOWS!)
	RTS

ADJUSTCOLBS	TST	COLUMNCOUNT	HANDLE BACKSPACE
	BEQ	ADJUSTCOLRTS	B/ AT COL 0, CANNOT BACKSPACE
	DEC	COLUMNCOUNT	ELSE BACK UP THE COLUMN COUNT
	RTS

ADJUSTCOLTAB	LDB	COLUMNCOUNT	TAB SEEN
	ORB	#7	BUMP COLUMN COUNT TO NEXT MULTIPLE OF 8
	INCB
	STB	COLUMNCOUNT
	RTS
	PAGE
*	SETUPFORWRITE -- SET UP FOR "WRITEA" OR "WRITEB"
*	WORKS JUST LIKE SETUPFORREAD, EXCEPT THAT WRITEFLAG IS SET
*
SETUPFORWRITE	EQU	*
	LDAA	#1	REMEMBER, THIS IS A WRITE!
	STAA	WRITEFLAG
	LDX	CODE+SDOS:IOBLOCKPTR	GET SCBLK:WRBUF...
	LDX	SCBLK:WRBUF,X	AND COPY INTO BUFFERPOINTER
	BRA	SETUPFORREAD1
*
*	SETUPFORREAD -- COPY BUFFERADDRESS FROM IOBLOCK TO BUFFERPOINTER
*		ALSO GETS FCB AND DISKINFO POINTERS FROM IOCB
*		FINALLY, RESETS THE WRITEFLAG AND COPIES IOCB:COLCNT INTO COLUMNCOUNT
*
SETUPFORREAD	EQU	*
	CLR	WRITEFLAG	THIS IS A READ, REMEMBER?
	IF	READA:BUFFERP#READB:BUFFERP
	?SETUPFORREAD WON'T WORK?
	FIN
	LDX	CODE+SDOS:IOBLOCKPTR	GET BUFFER POINTER FROM I/O BLOCK
	LDX	READA:BUFFERP,X
SETUPFORREAD1	EQU	*
	STX	BUFFERPOINTER
	LDX	IOCBPOINTER	AND SET UP THE COLUMNCOUNT
	LDAA	IOCB:COLCNT,X	IN CASE THIS IS AN ASCII READ OR WRITE
	STAA	COLUMNCOUNT
	RTS
*
*	GETFCBFROMIOCB -- SETS UP FCBPOINTER FROM IOCB
*	PRESERVES (A) FOR DISK DRIVER CONTROL, STATUS CALLS
*
GETFCBFROMIOCB	EQU	*
	LDX	IOCBPOINTER	GRAB IOCB ADDRESS
	LDX	IOCB:FCB,X	COPY FCB ADDRESS OUT OF IOCB...
	STX	FCBPOINTER	INTO A CONVENIENT PLACE
	LDX	IOCBPOINTER	JUST TO BE NICE
	RTS
	PAGE
*	CHECKREADDONE -- CHECKS TO SEE IF READB:MAXCOUNT = READB:ACTUALCOUNT
*		RETURNS Z BIT SET IF EQUAL
*		...RESET IF NOT EQUAL
*		RETURNS (A,B):=READB:MAXCOUNT-READB:ACTUALCOUNT
*
CHECKREADDONE	EQU	*
	IF	READA:BUFFERP#READB:BUFFERP
	?CHECKREADDONE WON'T WORK?
	FIN
	LDX	CODE+SDOS:IOBLOCKPTR	IS ACTUALCOUNT TRANSFERRED...
	LDD	READB:MAXCOUNT,X	= MAXCOUNT ?
	SUBD	READB:ACTUALCOUNT,X	(COMPUTE NUMBER OF BYTES LEFT TO TRANSFER)
	IF	M6800!M6801
	BNE	CHECKREADDONE1	B/ WE STILL NEED TO MOVE LOTSA BYTES
	TSTB		... ?
CHECKREADDONE1	EQU	*
	FIN
	RTS
*
*	CHECKWRITEDONE -- CHECKS TO SEE IF WRBUF+WRLEN = BUFFERPOINTER
*		RETURNS Z BIT SET IF EQUAL, RESET IF NOT EQUAL
*		RETURNS (A,B):=WRBUF+WRLEN-BUFFERPOINTER (# BYTES LEFT TO WRITE)
*
CHECKWRITEDONE	EQU	*
	IF	WRITEA:BUFFERP#WRITEB:BUFFERP
	?CHECKWRITEDONE WON'T WORK?
	FIN
	LDX	CODE+SDOS:IOBLOCKPTR	FIND END OF WRBUF
	LDD	SCBLK:WRBUF,X
	ADDD	SCBLK:WRLEN,X
	SUBD	BUFFERPOINTER	HAS BUFFERPOINTER ARRIVED THERE ?
	IF	M6800!M6801
	BNE	CHECKWRITEDONE1	B/ GO FIND OUT.
	TSTB
CHECKWRITEDONE1
	FIN
	RTS
	PAGE
*
*	DSKFWRITEB -- WRITE BINARY TO DISK FILE
*
DSKFWRITEB	EQU	*
	JSR	DSKFIMPLIEDPOSITION	HANDLE IMPLIED POSITIONING IF REQUESTED
	BCS	*+2	IGNORE EOF ERROR IF GIVEN
	BSR	SETUPFORWRITE	SO WE CAN MODIFY IT AS WE PLEASE
*		AND GET FCB AND DISKINFO POINTERS
	CLR	IOCB:COLCNT,X	YOU SAY, "WRITE BINARY", I SAY "ZAP THE COLUMN COUNT"
	JSR	UPDATEFILEDATE	SET DATE OF FILE TO TODAY
DSKFWRITEBL	; WRITE BINARY BYTES TO DISK FILE
	BSR	CHECKWRITEDONE	ARE ALL BYTES FROM BUFFER WRITTTEN ?
	BEQ	DSKFWRITEBDONE	B/ YES, ALL DONE WRITING BYTES
	JSR	LOCATECURBYTE	LOCATE THE BYTE WE WANT
	BSR	DSKWRITEBCHUNK	COPY A CHUNK OF BYTES FROM THE BUFFER TO THE DISK
	JSR	UPDATECURBYTEF	ADJUST CURBYTE
	BCC	DSKFWRITEBL	GO LOOK FOR MORE BYTES TO MOVE
	BRA	DSKFWRITEBL	IGNORE EOF ERROR

DSKFWRITEBDONE	EQU	*
	OKRTS		AND EXIT
*
*	DSKDWRITEB -- WRITE BINARY TO DISK FILE
*
DSKDWRITEB	EQU	*
	JSR	DSKDIMPLIEDPOSITION	HANDLE IMPLIED POSITIONING IF REQUESTED
	JSR	SETUPFORWRITE	AND SET UP DISKINFO POINTER
	CLR	IOCB:COLCNT,X	SINCE YOU ASKED FOR A WRITE BINARY
DSKDWRITEBL	EQU	*
	BSR	CHECKWRITEDONE	ALL BYTES MOVED FROM BUFFER TO DISK ?
	BEQ	DSKFWRITEBDONE	B/ YES, QUIT NOW
	JSR	LOCDSKD	LOCATE THE CHUNK OF BYTES TO WRITE INTO
	BSR	DSKWRITEBCHUNK	GO COPY A CHUNK OF BYTES TO THE DISK
	JSR	UPDATECURBYTED	AND UPDATE THE POINTERS TELLING US WHERE WE ARE
	BCC	DSKDWRITEBL	GO SEE IF SOME MORE WORK TO DO
	BRA	DSKDWRITEBL	IGNORE EOF ERROR
	PAGE
*
*	DSKWRITEBCHUNK -- WRITE A CHUNK OF BYTES FROM BUFFER TO DISK FILE
*		PARAMETERS SPECIFYING THE CHUNK ARE SET UP BY LOCATECURBYTE
*
DSKWRITEBCHUNK	EQU	*
	LDX	IOCBPOINTER	GET THE TARGET POINTER
	IF	M6800!M6801
	LDX	IOCB:NEXTBYTE,X
	STX	TOPOINTER
	ELSE	(M6809)
	LDY	IOCB:NEXTBYTE,X
	FIN
	LDX	BUFFERPOINTER	GET FROM POINTER TO (A,B)
	LDD	USEDCOUNT	# BYTES TO MOVE
	JSR	CODE+SDOS:BLOCKMOVE	GO MOVE THE BYTES
*	NOTE: THIS USES SDOS ENTRY POINT VECTOR...
*	SO THAT A PATCH TO ENTRY POINT VECTOR CAN SUPPLY A DIFFERENT...
*	(PRESUMABLY MUCH FASTER) BLOCK MOVE ROUTINE OF ITS OWN!
	STX	BUFFERPOINTER	UPDATE POINTER PAST COPIED BYTES
	RTS
	PAGE
*
*	DSKFWRITEA -- WRITE ASCII TO DISK FILE
*
DSKFWRITEA	EQU	*
	JSR	DSKFIMPLIEDPOSITION	HANDLE IMPLIED POSITIONING IF REQUESTED
	BCS	*+2	IGNORE EOF ERROR IF GIVEN
	JSR	SETUPFORWRITE	SO WE CAN MODIFY IT AS WE PLEASE
*			AND SET UP FCB AND DISKINFO TABLE POINTERS
	JSR	UPDATEFILEDATE	TO MATCH TODAY'S DATE!
DSKFWRITEAL	; WRITE A BYTE TO DISK FILE
	JSR	CHECKWRITEDONE	ALL BYTES COPIED FROM BUFFER TO DISK FILE ?
	BEQ	DSKFWRITEADONE	B/ YES, ALL DONE WRITING BYTES
	JSR	LOCATECURBYTE	LOCATE THE BYTE WE WANT
	BSR	DSKWRITEACHUNK	WRITE A CHUNK OF BYTES FROM BUFFER TO DISK FILE
	JSR	UPDATECURBYTEF	ADJUST CURBYTE
	BCC	DSKFWRITEAL	GO SEE IF WE MUST STILL MOVE MORE BYTES
	BRA	DSKFWRITEAL	IGNORE EOF ERROR

DSKFWRITEADONE	EQU	*
	LDX	IOCBPOINTER	RESET THE WRITE MODE FLAG
	LDAA	COLUMNCOUNT	AND UPDATE THE COLUMN COUNTER
	STAA	IOCB:COLCNT,X
	OKRTS		AND EXIT
	PAGE
*
*	DSKWRITEACHUNK -- WRITE A CHUNK OF ASCII BYTES TO A DISK SECTOR
*		CHUNK PARAMETERS SET UP BY LOCATECURBYTE
*
DSKWRITEACHUNK	EQU	*
	IF	M6800!M6801
	LDD	USEDCOUNT	= # BYTES TO TRANSFER
	NEGD		MAKE EASY-TO-INCREMENT COUNTER
	STD	TRANSFERCOUNT
	LDX	IOCBPOINTER	GET THE TARGET POINTER
	LDX	IOCB:NEXTBYTE,X
	STX	TOPOINTER	AND SAVE IT
DSKWRITEACHUNKL	; COPY BYTES FROM SECTOR TO BUFFER
	LDX	BUFFERPOINTER
	LDA	,X+	COPY A BYTE...
	STX	BUFFERPOINTER
	JSR	ADJUSTCOLUMNCOUNT	NEED I SAY MORE ?
	LDX	TOPOINTER	TO THE DATA SECTOR
	STA	,X+
	STX	TOPOINTER
	INC	TRANSFERCOUNT+1	DOWN COUNT # BYTES TO MOVE
	BNE	DSKWRITEACHUNKL	B/ MORE TO MOVE
	INC	TRANSFERCOUNT
	BNE	DSKWRITEACHUNKL	B/ MORE
	ELSE	(M6809)
	LDY	USEDCOUNT	= # BYTES TO MOVE
	LDX	IOCBPOINTER	GET TARGET POINTER TO (U)
	LDU	IOCB:NEXTBYTE,X
	LDX	BUFFERPOINTER	SOURCE POINTER
DSKWRITEACHUNKL ; COPY BYTES FROM SECTOR TO USER SPACE BUFFER
	LDA	,X+	COPY A BYTE...
	JSR	ADJUSTCOLUMNCOUNT	NEED I SAY MORE?
	STA	,U+	... TO TARGET SECTOR BUFFER
	LEAY	-1,Y	DOWN COUNT NUMBER OF BYTES TO MOVE
	BNE	DSKWRITEACHUNKL	B/ MORE BYTES TO MOVE!
	STX	BUFFERPOINTER	MAKE SURE BUFFERPOINTER IS UP TO DATE ON EXIT
	FIN
	RTS
	PAGE
*
*	DSKDWRITEA -- WRITE ASCII TO DISK DEVICE
*
DSKDWRITEA	EQU	*
	JSR	DSKDIMPLIEDPOSITION	HANDLE IMPLIED POSITIONING IF REQUESTED
	JSR	SETUPFORWRITE	AND SET UP DISKINFO POINTER
DSKDWRITEAL	EQU	*
	JSR	CHECKWRITEDONE	ALL BYTES WRITTEN ?
	BEQ	DSKFWRITEADONE	B/ YES, QUIT NOW
	JSR	LOCDSKD	GO FIGURE OUT THE BOUNDARIES OF THE CHUNK
	BSR	DSKWRITEACHUNK	GO COPY THE BYTES TO A DISK SECTOR
	JSR	UPDATECURBYTED	UPDATE THE DISK DEVICE POINTERS
	BCC	DSKDWRITEAL	AND LOOP UNTIL ALL THE BYTES ARE COPIED
	BRA	DSKDWRITEAL	IGNORE EOF ERROR
	PAGE
*
*	TABLEBRANCH -- BRANCH THRU TABLE ON (A)
*	ENTERED WITH POINTER TO TABLE IN (X)
*	@X IS BYTE CONTAINING MINIMUM VALUE FOR (A)
*	@(X+1) IS BYTE CONTAINING MAXIMUM VALUE FOR (A) [ MUST BE < MIN + 64]
*	@(X+2) IS POINTER TO ROUTINE FOR MIN VALUE OF (A)
*	@(X+4) IS POINTER TO ROUTINE FOR MIN VALUE OF (A), + 1
*		ETC.
*	CONTROL IS RETURNED ONLY IF (A) IS NOT IN BOUNDS SPECIFIED BY TABLE
*
TABLEBRANCH
	CMPA	,X	< MIN VALUE?
	BCS	TABLEBRANCHCANT	B/ NOT IN RANGE FOR THIS TABLE
	CMPA	1,X	<= MAX VALUE?
	BHI	TABLEBRANCHCANT	B/ NOT IN RANGE FOR THIS TABLE
	LEAS	2,S	MAKE RETURN ADDRESS GO AWAY...
	SUBA	,X	FORM 0-ORIGIN OFFSET INTO BRANCH TABLE
	ASLA		ASSERT: CAN'T OVERFLOW OR SET THE SIGN BIT!
	TAB
	IF	M6800!M6801
	CLRA		COMPUTE POINTER INTO TABLE
	STX	TEMPX
	ADDD	TEMPX
	TDX
	ELSE	(M6809)
	LEAX	B,X
	FIN
	JMP	[2,X]	GO TO ROUTINE

TABLEBRANCHCANT	RTS
	PAGE
DSKFSETDATE	; SET DATE OF DISK FILE
	JSR	CHECKWRLEN	WRITE BUFFER FORMAT MATCHES CLOCK
	FDB	6
	LDX	SCBLK:WRBUF,X
	LDA	CONTROL:DATA+3,X	FETCH NEW DAY NUMBER
	PSHA		AND SAVE IT
	LDD	CONTROL:DATA+4,X	FETCH MONTH AND YEAR NUMBER
	LDX	FCBPOINTER	THIS FCB, PLEASE...
	STD	FCB:MONTH,X	SET MONTH AND YEAR
	PULA		GET DAY NUMBER BACK
	STAA	FCB:DAY,X	AND STORE IT
DSKFSET1 ; MARK DIRECTORY ENTRY AS CHANGED
	LDAA	FCB:FLAGS,X	NOTE: SETTING DATE DOES NOT ALTER "BACKUP" BIT
	ORAA	#FCBFLG::UPDATEDIR
	STAA	FCB:FLAGS,X
	OKRTS

DSKFSETPROT	; SET PROTECTION BITS
	JSR	CHECKWRLEN
	FDB	1	SINGLE BYTE WRITE BUFFER
	LDX	SCBLK:WRBUF,X	GET POINTER TO WRITE BUFFER
	LDAA	CONTROL:DATA,X	GET NEW PROTECTION BITS
	LDX	FCBPOINTER	WHICH FILE TO CHANGE
	STAA	FCB:PROT,X	CHANGE PROT AFTER UPDATEFILEDATE...
	BRA	DSKFSET1	GO MARK DIRECTORY ENTRY AS CHANGED
	PAGE
*	DSKFCONTROL -- DO A CONTROL OPERATION ON DISK FILE
*
DSKFCONTROL	EQU	*
	JSR	GETFCBFROMIOCB	SET UP FCB POINTER
	LDX	#DSKFCONTROL0	BRANCH ON STANDARD DISK FILE CONTROL OPERATIONS
	JSR	TABLEBRANCH
	LDX	#DSKFCONTROL10	BRANCH ON DISK FILE SPECIFIC CONTROL OPERATIONS
	JSR	TABLEBRANCH
ERRILLDEVOP	EQU	*
	JSR	ERRET	NO, NOT A VALID CONTROL REQUEST
	FDB	ERR:ILLDEVICEOP

DSKFCONTROL0 ; BRANCH TABLE FOR STANDARD DISK FILE CONTROL OPERATIONS
	FCB	CC:POSITION,CC:DUMPBUFFERS
	#DSKFPOSITION	CC:POSITION
	#DSKDUMPBUFFERS	CC:DUMPBUFFERS

DSKFCONTROL10	; BRANCH TABLE FOR DISK FILE SPECIFIC CONTROL OPERATIONS
	FCB	CC:SETFILEDATE,CC:POSITIONTOEND
	#DSKFSETDATE	CC:SETFILEDATE
	#DSKFSETPROT	CC:SETFILEPROT
	#DSKFSETSIZE	CC:SETFILESIZE
	#DSKFPOSITIONTOEND	CC:POSITIONTOEND
*	#DSKFTABS	CC:TABS
*	#DSKDFSETACTBLOCK	CC:SETACTBLOCK

DSKFOPENERRORFILE	; INTENDED FOR USE ONLY WITH SYSIOCB
	LDX	ERRFCBPOINTER	DOES ERROR FILE EXIST?
	BEQ	ERRNOERRMSGS	B/ NO ERROR FILE!?
	STX	FCBPOINTER	RECORD FCB ADDRESS OF ERROR FILE
	INC	FCB:REFCOUNT,X	SINCE THIS IS SECOND TIME WE'LL OPEN...
	LDX	FCB:DISKINFO,X	SET UP DISKINFO POINTER...
	STX	DCBPOINTER	JUST LIKE A CALL TO OPEN WOULD HAVE DONE
	JMP	DSKFOPENSYSIOCB	DO REST LIKE A NORMAL OPEN

ERRNOERRMSGS	EQU	*
	JSR	ERRET
	FDB	ERR:NOERRORMSGS
	PAGE
DSKFSETSIZE	; SET SIZE OF DISK FILE TO CURRENT FILE POSITION
	JSR	UPDATEFILEDATE	CHANGE DATE OF FILE
	LDX	IOCBPOINTER	GET FILE SIZE DESIRED
	LDD	IOCB:CURBYTE,X
	PSHD
	LDD	IOCB:CURBYTE+2,X
	LDX	FCBPOINTER	STUFF INTO FCB
	STD	FCB:FILESIZE+2,X
	PULD
	STD	FCB:FILESIZE,X
	BSR	DSKFEOFTEST	SET EOF FLAG
	BCS	*+2	SURPRESS EOF ERROR THAT WILL OCCUR
DSKFIMPLIEDPOSOKRTS
	OKRTS

DSKFIMPLIEDPOSITION	; HANDLE IMPLIED CC:POSITION BEFORE READ/WRITE
	JSR	GETFCBFROMIOCB	THIS IS REQUIRED BEFORE WE DO ANYTHING...
	LDB	#RWPOSITION:SCLEN	IS SYSCALL BLOCK LONG ENOUGH TO INCLUDE A POSITION ?
	JSR	CHECKSCLEN	...?
	BCS	DSKFIMPLIEDPOSOKRTS	B/ NO, SO DON'T POSITION THE FILE
	LDD	RW:POSITION,X	YES, GET THE DESIRED FILE POSITION
	PSHD
	LDD	RW:POSITION+2,X
	BRA	DSKFPOSITION1	GO DO FILE POSITION, GIVE EOF ERROR IF PAST EOF

DSKFPOSITIONTOEND ; POSITION FILE TO END
	BSR	DSKFPOSITIONTOEND1
	BCS	*+2	IGNORE EOF ERROR IF IT OCCURS
	OKRTS

DSKFPOSITIONTOEND1	; SUBROUTINE FOR DSKFPOSITIONTOEND
	LDX	FCBPOINTER	GET FILESIZE, SO WE CAN...
	LDD	FCB:FILESIZE,X	POSITION TO EOF
	PSHD
	LDD	FCB:FILESIZE+2,X
	BRA	DSKFPOSITION1	GO DO POSITIONING

DSKFPOSITION	; SET IOCB:CURBYTE TO POSITION:DIST
	JSR	CHECKWRLEN	MAKE SURE THERE ARE AT LEAST...
	FDB	4	BYTES IN WRITE LENGTH
	LDX	SCBLK:WRBUF,X
	LDD	CONTROL:DATA,X	COPY POSITION DATA TO (TOS,TOS-1),(A,B)
	PSHD
	LDD	CONTROL:DATA+2,X
DSKFPOSITION1	; ENTRY POINT FOR DSKFCLOSE
	LDX	IOCBPOINTER	NOW COPY TO IOCB:CURBYTE
	CLR	IOCB:LOCATEDF,X	MARK "LOCATECURBYTE REQ'D"
	CLR	IOCB:COLCNT,X	ALSO, ZERO THE COLUMN COUNTER
	STD	IOCB:CURBYTE+2,X
	PULD
	STD	IOCB:CURBYTE,X
	BMI	ERRBADPOSITION	B/ POSITION < 0!!
*	BRA	DSKFEOFTEST	GO TEST FOR EOF HIT
	PAGE
*
*	DSKFEOFTEST -- TEST FOR CURBYTE >= FILESIZE
*		SET EOFFLAG IF SO, TAKE ERRET
*
DSKFEOFTEST	; TEST TO SEE IF POSITION OFF END OF FILE
	LDX	FCBPOINTER	NOW MOVE FCB:FILESIZE...
	LDD	FCB:FILESIZE,X	TO (TOS-2),(TOS-1),(TOS),(A)
	PSHD
	LDD	FCB:FILESIZE+2,X
	LDX	IOCBPOINTER	COMPUTE FCB:FILESIZE-IOCB:CURBYTE-1
	CLR	IOCB:EOFFLAG,X	ASSUME NOT EOF
	SEC		(THE -1 IS BECAUSE FILESIZE IS 1 ORIGIN)
	SBCB	IOCB:CURBYTE+3,X	I.E., CHECK IF IOCB:CURBYTE >= FCB:FILESIZE
	SBCA	IOCB:CURBYTE+2,X
	PULD
	SBCB	IOCB:CURBYTE+1,X
	SBCA	IOCB:CURBYTE,X
DSKFEOFTESTROL	; ENTRY POINT FOR DSKDEOFTEST
	ROL	IOCB:EOFFLAG,X	A BORROW BIT IN CARRY --> EOF
	BNE	ERREOFHIT	B/ RAN OFF END OF FILE
	OKRTS

ERREOFHIT	EQU	*
	JSR	ERRET
	FDB	ERR:EOFHIT

ERRBADPOSITION	EQU	*
	CLR	IOCB:CURBYTE,X	RESET POINTER TO FILE START
	CLR	IOCB:CURBYTE+1,X
	CLR	IOCB:CURBYTE+2,X
	CLR	IOCB:CURBYTE+3,X
	BSR	DSKFEOFTEST	IN CASE THE FILE IS EMPTY
	JSR	ERRET
	FDB	ERR:BADPOSITION
	PAGE
DSKFGETFILESIZE	; GET FILE SIZE BACK FROM FILE
	JSR	CHECKRDLEN	MAKE SURE THERE ARE 4 BYTES IN THE READ-BACK BUFFER
	FDB	4
	LDX	FCBPOINTER	COPY FCB:FILESIZE...
	LDD	FCB:FILESIZE,X	TO (TOS,TOS-1), (A,B)
	PSHD
	LDD	FCB:FILESIZE+2,X
	BRA	DSKFGETPOS4	GO COPY TO READ BACK BUFFER
*
DSKFGETPOS	; GET POSITION BACK FROM FILE
	JSR	CHECKRDLEN	MAKE SURE THERE IS A 4 BYTE BUFFER
	FDB	4
	LDX	IOCBPOINTER	COPY IOCB:CURBYTE...
	LDD	IOCB:CURBYTE,X	TO (TOS,TOS-1), (A,B)
	PSHD
	LDD	IOCB:CURBYTE+2,X
DSKFGETPOS4	EQU	*
	LDX	CODE+SDOS:IOBLOCKPTR	AND THEN TO TARGET BUFFER
	LDX	SCBLK:RDBUF,X	GET POINTER TO PLACE TO PUT POSITION
	STAB	STATUS:DATA+3,X
DSKFGETPOS3	; STORE 3 BYTES INTO READ-BACK BUFFER
	STAA	STATUS:DATA+2,X
	PULD
	STD	STATUS:DATA+0,X
	OKRTS
	PAGE
DSKDGETBADLSN	; READ BACK THE LAST BAD LSN
	JSR	CHECKRDLEN	MAKE SURE WE HAVE ROOM FOR REPLY
	FDB	3
	LDX	DCBPOINTER	COPY LAST BAD LSN TO (TOS,TOS-1,A)
	LDD	DSKINFO:BADLSN,X
	PSHD
	LDAA	DSKINFO:BADLSN+2,X
	LDAB	#$FF	FORGET WE HAD A BAD LSN
	STAB	DSKINFO:BADLSN,X
	STAB	DSKINFO:BADLSN+1,X
	STAB	DSKINFO:BADLSN+2,X
DSKFGETPOS3A	EQU	*
	LDX	CODE+SDOS:IOBLOCKPTR	GET POINTER TO READ BACK BUFFER
	LDX	SCBLK:RDBUF,X
	BRA	DSKFGETPOS3	GO STORE 3 BYTES INTO READ-BACK BUFFER

DSKFGETPARAMS	; GET FILE "DEVICE" PARAMETERS
	JSR	CHECKRDLEN	= SECTOR SIZE, NUMBER OF SECTORS PER CLUSTER
	FDB	2+1	2 BYTES FOR SECTOR SIZE; 1 FOR # SECTORS PER CLUSTER
	LDX	DCBPOINTER	GET POINTER TO TABLE
	LDD	DSKINFO:NBPS,X	GRAB SECTOR SIZE
	PSHD
	LDAA	DSKINFO:NSPC,X	GET CLUSTER SIZE
	BRA	DSKFGETPOS3A
	PAGE
*
*	DSKFSTATUS -- DO A STATUS READ ON DISK FILE
*
DSKFSTATUS	EQU	*
	JSR	GETFCBFROMIOCB	SET UP FCB POINTER
	LDX	#DSKFSTATUS0	BRANCH TABLE FOR STANDARD DISK FILE STATUS OPERATIONS
	JSR	TABLEBRANCH
	LDX	#DSKFSTATUS10	BRANCH TABLE FOR DISK FILE SPECIFIC STATUS
	JSR	TABLEBRANCH
	JMP	ERRILLDEVOP

DSKFSTATUS0	; BRANCH TABLE FOR STANDARD DISK FILE STATUS OPERATIONS
	FCB	SC:GETPOS,SC:GETPARAMS
	#DSKFGETPOS	SC:GETPOS
	#DSKFGETCOL	SC:GETCOL
	#DSKFGETEOF	SC:GETEOF
	#DSKFGETFILESIZE	SC:GETFILESIZE
	#DSKFGETTYPE	SC:GETTYPE
	#DSKFGETPARAMS	SC:GETPARAMS

DSKFSTATUS10	; BRANCH TABLE FOR DISK FILE SPECIFIC OPERATIONS
	FCB	SC:GETFILEDATE,SC:GETFILEPROT
	#DSKFGETDATE	SC:GETFILEDATE
	#DSKFGETPROT	SC:GETFILEPROT
	PAGE
*
*	DSKDSTATUS -- DO A STATUS READ ON DISK DEVICE
*
DSKDSTATUS	EQU	*
	LDX	#DSKDSTATUS0	BRANCH TABLE FOR DISK DEVICE STANDARD STATUS CALLS
	JSR	TABLEBRANCH
	LDX	#DSKDSTATUS10	BRANCH TABLE FOR DISK DEVICE SPECIFIC STATUS CALLS
	JSR	TABLEBRANCH
	LDX	DCBPOINTER	I DON'T RECOGNIZE THE STATUS CALL...
	LDX	DCB:DRIVER,X	SO LET I/O PACKAGE DISK DRIVER HANDLE IT
	JMP	[DRIVER:DISKSTATUS,X]	WITH (A) = STATUS REQUEST CODE

DSKDSTATUS0 ; BRANCH TABLE FOR DISK DEVICE STANDARD STATUS CALLS
	FCB	SC:GETPOS,SC:GETPARAMS
	#DSKFGETPOS	SC:GETPOS
	#DSKFGETCOL	SC:GETCOL
	#DSKFGETEOF	SC:GETEOF
	#ERRILLDEVOP	SC:GETFILESIZE	CAN'T WE DO THIS BETTER?
	#DSKDGETTYPE	SC:GETTYPE
	#DSKDGETPARAMS	SC:GETPARAMS

DSKDSTATUS10	; BRANCH TABLE FOR DISK DEVICE SPECIFIC STATUS CALLS
	FCB	SC:GETLASTBADLSN,SC:GETERRORSTATS
	#DSKDGETBADLSN	SC:GETLASTBADLSN
	#DSKDGETERRORSTATS	SC:GETERRORSTATS
	PAGE
DSKDGETERRORSTATS ; GET DISK DEVICE ERROR STATISTICS
	JSR	CHECKRDLEN	ENOUGH ROOM IN REPLY BUFFER FOR ERROR STATS?
	FDB	(DSKINFO:ERRLSN+LSN:SIZE)-DSKINFO:SEEKERRCNT
	IF	M6800!M6801
	LDD	SCBLK:RDBUF,X	GET TARGET ADDRESS
	STD	TOPOINTER
	ELSE	(M6809)
	LDY	SCBLK:RDBUF,X
	FIN
	IF	M6800!M6801
	LDD	DCBPOINTER	FIGURE OUT THE SOURCE ADDRESS
	ADDD	#DSKINFO:SEEKERRCNT
	STD	FROMPOINTER
	LDD	SCBLK:RPLEN,X	HOW MANY BYTES TO MOVE...
	LDX	FROMPOINTER
	ELSE	(M6809)
	LDD	SCBLK:RPLEN,X	HOW MANY BYTES TO MOVE...
	LDX	DCBPOINTER	= WHERE TO GET BYTES FROM
	LEAX	DSKINFO:SEEKERRCNT,X
	FIN
	JSR	CODE+SDOS:BLOCKMOVE	USE ENTRY POINT VECTOR SO BLOCKMOVE CAN BE REPLACED BY USER ROUTINE
	OKRTS
	PAGE
DSKFGETPROT	; GET FILE PROTECTION/BACKUP BITS
	JSR	CHECKRDLEN	MAKE SURE REPLY BUFFER IS LARGE ENOUGH!
	FDB	1
	LDX	FCBPOINTER	THIS FILE...
	LDAA	FCB:PROT,X
	BRA	DSKFGETCOL1	GO STORE FLAG DATA IN USER BUFFER

DSKFGETDATE ; GET CREATION DATE INFORMATION IN CLOCK FORMAT
	JSR	CHECKRDLEN	MUST BE 6 BYTES
	FDB	6
	LDX	FCBPOINTER	THIS FILE...
	LDAA	FCB:DAY,X
	PSHA
	LDD	FCB:MONTH,X	AND THE YEAR
	LDX	CODE+SDOS:IOBLOCKPTR	= USER SYSCALL BLOCK
	LDX	SCBLK:RDBUF,X	WHERE TO PUT REPLY
	STD	STATUS:DATA+4,X
	PULA
	STAA	STATUS:DATA+3,X
	CLR	STATUS:DATA,X	SET TIME TO MIDNITE
	CLR	STATUS:DATA+1,X
	CLR	STATUS:DATA+2,X
	OKRTS

DSKDGETTYPE	; GET DISK DEVICE TYPE
	JSR	CHECKRDLEN	MAKE SURE READ-BACK BUFFER HAS ENOUGH ROOM
	FDB	1
*
*	IT SHOULD WORK THIS WAY! (SDOS1.2)
*	LDX	DCBPOINTER
*	LDA	DCB:DVTYP,X
*
	LDAA	#DVTYP.DISK
	BRA	DSKFGETCOL1

DSKFGETTYPE	; GET (DISK) FILE DEVICE TYPE
	JSR	CHECKRDLEN	MAKE SURE READ-BACK BUFFER HAS ENOUGH ROOM
	FDB	1
	LDAA	#DVTYP.FILE
	BRA	DSKFGETCOL1
	PAGE
DSKFGETCOL	; GET COLUMN COUNTER FOR FILE
	JSR	CHECKRDLEN	MAKE SURE READ BACK BUFFER HAS ONE BYTE
	FDB	1
	LDX	IOCBPOINTER
	LDAA	IOCB:COLCNT,X
DSKFGETCOL1	EQU	*
	LDX	CODE+SDOS:IOBLOCKPTR
	LDX	SCBLK:RDBUF,X	GET ADDRESS OF PLACE TO STORE COLUMN
	STAA	STATUS:DATA,X
	OKRTS

DSKFGETEOF	; GET END OF FILE FLAG FOR FILE
	JSR	CHECKRDLEN	ENSURE AT LEAST 1 BYTE READ BUFFER
	FDB	1
	LDX	IOCBPOINTER
	LDAA	IOCB:EOFFLAG,X
	BRA	DSKFGETCOL1

DSKDGETPARAMS	; GET DISK DEVICE PARAMETERS
	JSR	CHECKRDLEN	MAKE SURE THERE'S ENOUGH SPACE TO READ TYPE DATA BACK
	FDB	DSKINFO:NCYL+1-DSKINFO:NBPS+1
	IF	M6800!M6801
	LDX	SCBLK:RDBUF,X	GET POINTER TO READ-BACK BUFFER
	STX	TOPOINTER	SET UP TO COPY DISKINFO PHYSICAL DISK DATA
	ELSE	(M6809)
	LDY	SCBLK:RDBUF,X	GET POINTER TO TARGET BUFFER
	FIN
	LDX	DCBPOINTER	GET POINTER TO SOURCE BUFFER
	LEAX	DSKINFO:NBPS,X
	LDD	#DSKINFO:NCYL+1-DSKINFO:NBPS+1	= BYTE COUNT TO COPY
	JSR	CODE+SDOS:BLOCKMOVE	MOVE THE DATA TO USERS BUFFER
	OKRTS		ALL DONE!
	PAGE
*	DSKDSETMAPALGORITHM -- SETS DSKINFO:MAPALGORITHM TO SPECIFIED VALUE
*
DSKDSETMAPALGORITHM	EQU	*
	JSR	CHECKWRLEN	MAKE SURE AT LEAST 2 BYTES IN WRITE BUFFER
	FDB	2
	LDX	SCBLK:WRBUF,X	GET POINTER TO MAPALGORITHM DESIRED
	LDD	CONTROL:DATA,X	GET DESIRED MAP ALGORITHM NUMBER
	LDX	DCBPOINTER	UPDATE THE DISKINFO TABLE
	TST	DSKINFO:DIRFCB,X	IS THIS DRIVE DISMOUNTED ?
	BNE	ERRDISKMOUNTED	B/ NO, CAN'T CHANGE MAP ALGORITHM UNLESS DISMOUNTED!
*
*	DSKDSETMAPALGDUMP -- CHECK TO SEE IF DUMPBUFFERS IS REQUIRED...
*	BEFORE WE SET THE MAP ALGORITHM
*	(A,B) CONTAINS NEW MAP ALGORITHM
*	(X) = DCBPOINTER
*
DSKDSETMAPALGDUMP	EQU	*
	CMPD	DSKINFO:MAPALGORITHM,X	SAME MAP ALGORITHM AS LAST TIME ?
	BEQ	DSKDSETMAPALGRTS	B/ YES, DON'T NEED TO DO ANYTHING!
DSKDSETMAPALG0	; MUST DUMP BUFFERS BEFORE CHANGING MAPALGORITHM
	PSHD		SAVE NEW MAP ALGORITHM
	JSR	DSKDUMPBUFFERS	USING OLD ALGORITHM
	BCC	DSKDSETMAPALG1	B/ NO ERRORS
	LEAS	2,S	ERROR OCCURRED! FORGET NEW MAP ALGORITHM
	JMP	ERRORED	AND RETURN TO CALLER

DSKDSETMAPALG1	; BUFFERS ARE DUMPED
	PULD		RETRIEVE NEW MAP ALGORITHM
DSKDSETMAPALG2	; ENTRY POINT FOR DISK DISMOUNT
	LDX	DCBPOINTER
	STD	DSKINFO:MAPALGORITHM,X	UPATE THE TABLE TO REFLECT NEW ALGORITHM NUMBER
	JSR	MARKINVALID	MARK ALL SECTORS IN POOL FROM DISK AS INVALID
	JSR	FORGETLASTBADLSN	MAKE LAST BAD LSN DATA DISAPPEAR
DSKDSETMAPALGRTS	EQU	*
	OKRTS

ERRDISKMOUNTED	EQU	*
	JSR	ERRET
	FDB	ERR:DISKMOUNTED
	PAGE
*
*	DSKDOPEN -- OPEN THE DISK DEVICE
*
DSKDOPEN	EQU	*
	LDX	DCBPOINTER	IS THE DISK MOUNTED ?
	LDAA	DSKINFO:DIRFCB,X	... ?
	BNE	DSKDOPEN1	B/ YES, USE MAP ALGORITHM SPECIFIED BY DISK
	LDAB	#1	NO, DEFAULT TO MAPALGORITHM :0001
	BSR	DSKDSETMAPALGDUMP	GO SET THE MAP ALGORITHM
DSKDOPEN1	; NOW SET UP THE IOCB
	LDX	IOCBPOINTER
	LDD	#0
	STAA	IOCB:COLCNT,X	ZERO THE COLUMN COUNT
	STD	IOCB:CURBYTE,X	SET FILE POSITION TO START OF DISK
	STD	IOCB:CURBYTE+2,X
	STD	IOCB:DRDSI,X	FLAG 'NO DATA SECTOR READ'
	INCA		= LDAA #1
	STAA	IOCB:DISKWRITELOCKED,X	MUST "UNLOCK" BEFORE ANY WRITES
	BRA	DSKDEOFTEST	GO COMPUTE CURLSN
	PAGE
*	DSKDCONTROL -- DO A CONTROL OPERATION ON THE DISK DEVICE
*
DSKDCONTROL	EQU	*
	LDX	#DSKDCONTROL0	BRANCH TABLE FOR DISK DEVICE STANDARD CONTROL OPERATORS
	JSR	TABLEBRANCH
	LDX	#DSKDCONTROL10	BRANCH TABLE FOR DISK DEVICE SPECIFIC CONTROL OPERATIONS
	JSR	TABLEBRANCH
DSKDCONTROL1	; RE-ENTRY POINT FOR "DSKDDISMOUNT"
	LDX	DCBPOINTER	NOT ANY CONTROL OPERATION I RECOGNIZE...
	LDX	DCB:DRIVER,X	SO LET I/O PACKAGE HANDLE IT
	JMP	[DRIVER:DISKCONTROL,X]	WITH (A) = CONTROL CODE

DSKDCONTROL0	; BRANCH TABLE FOR DISK DEVICE STANDARD CONTROL OPERATIONS
	FCB	CC:POSITION,CC:DUMPBUFFERS
	#DSKDPOSITION	CC:POSITION
	#DSKDUMPBUFFERS	CC:DUMPBUFFERS

DSKDCONTROL10	; BRANCH TABLE FOR DISK DEVICE SPECIFIC CONTROL OPERATIONS
	FCB	CC:UNLOCKDISK,CC:SETMAPALGORITHM
	#DSKDUNLOCK	CC:UNLOCKDISK
	#DSKDDISMOUNT	CC:DISMOUNTDISK
	#DSKDSETMAPALGORITHM	CC:SETMAPALGORITHM
	PAGE
*	DSKDIMPLIEDPOSITION -- DO IMPLIED POSITIONING ON DISK DEVICE, IF REQUESTED
*
DSKDIMPLIEDPOSITION
	LDB	#RWPOSITION:SCLEN	DOES SYSCALL BLOCK CONTAIN FILE POSITION ?
	JSR	CHECKSCLEN	...?
	BCS	DSKDIMPLIEDPOSOKRTS	B/ NO, DON'T POSITION DISK DEVICE
	LDD	RW:POSITION+2,X	GET, GET THE POSITION DESIRED
	PSHD
	LDD	RW:POSITION,X
	BRA	DSKDPOSITION1	GO SET POSITION OF DISK DEVICE AND CAUSE EOF IF PAST END
*
*	DSKDPOSITION -- DO POSITIONING ON DISK DEVICE
*
DSKDPOSITION	EQU	*
	JSR	CHECKWRLEN	MUST HAVE AT LEAST 4 BYTES OF POSITION DATA
	FDB	4
	LDX	SCBLK:WRBUF,X	GET POINTER TO POSITIONING DATA
	LDD	CONTROL:DATA+2,X	COPY DESIRED POSITION TO (A,B,TOS,TOS-1)
	PSHD
	LDD	CONTROL:DATA+0,X
DSKDPOSITION1	; ENTRY POINT FOR DSKDIMPLIEDPOSITION
	LDX	IOCBPOINTER	NOW COPY DESIRED POSITION INTO CURBYTE
	STD	IOCB:CURBYTE,X
	PULD
	STD	IOCB:CURBYTE+2,X
	CLR	IOCB:COLCNT,X	ZERO THE COLUMN COUNTER
*
*	DSKDEOFTEST -- COMPUTE "EOFFLAG" AND CURLSN
*
DSKDEOFTEST	EQU	*
	LDX	DCBPOINTER	SET CURLSN = CURBYTE / (2^LOG2NBPS)
	LDAB	DSKINFO:LOG2NBPS,X	I.E., THE DESIRED SECTOR NUMBER ON THE DISK
	LDX	IOCBPOINTER	FIRST, SET (A,CURLSN) := CURBYTE
	LDAA	IOCB:CURBYTE+3,X
	STAA	IOCB:CURLSN+2,X
	LDAA	IOCB:CURBYTE+2,X
	STAA	IOCB:CURLSN+1,X
	LDAA	IOCB:CURBYTE+1,X
	STAA	IOCB:CURLSN,X
	LDAA	IOCB:CURBYTE,X
DSKDEOFTESTL	; DIVIDE CURLSN BY 2^1
	LSRA		NOW, SET CURLSN := CURLSN / (2^LOG2NBPS)
	ROR	IOCB:CURLSN,X
	ROR	IOCB:CURLSN+1,X
	ROR	IOCB:CURLSN+2,X
	DECB		DOWN COUNT # RIGHT SHIFTS TO MAKE
	BNE	DSKDEOFTESTL	B/ MUST RIGHT SHIFT SOME MORE
	CLR	IOCB:BYTECOUNT,X	FORCE LOCATION OF NEXT BYTE AGAIN
	CLR	IOCB:BYTECOUNT+1,X
	CLR	IOCB:EOFFLAG,X	ASSUME WE'RE NOT AT EOF
	LDX	DCBPOINTER	NOW DECIDE IF WE'RE AT EOF ON DISK DEVICE
	LDAA	DSKINFO:NLSN,X	I.E., IF CURLSN >= NLSN...
	PSHA		I.E., IF 0 > NLSN - CURLSN - 1...
	LDD	DSKINFO:NLSN+1,X	I.E., IF NLSN - CURLSN CAUSES A BORROW
	LDX	IOCBPOINTER	SUBTRACT CURLSN, LOOKING FOR BORROW
	SEC		THIS DOES THE "...- 1" PART
	SBCB	IOCB:CURLSN+2,X
	SBCA	IOCB:CURLSN+1,X
	PULA
	SBCA	IOCB:CURLSN,X
	JMP	DSKFEOFTESTROL	CARRY NOW CONTAINS BORROW BIT; GO SAVE IT
*
*	DSKDUNLOCK -- UNLOCK THE DISK DEVICE WRITE PROTECT
*
DSKDUNLOCK	EQU	*
	LDX	IOCBPOINTER	TURN THE WRITE LOCK FLAG OFF
	CLR	IOCB:DISKWRITELOCKED,X
*
*	DSKDCLOSE -- CLOSE A DISK DEVICE
*
DSKDCLOSE	EQU	*
DSKDIMPLIEDPOSOKRTS	; NO IMPLIED POSITION REQUIRED
	OKRTS
	PAGE
*	FORGETLASTBADLSN -- RESET DSKINFO:BADLSN TO :FFFFFF
*
FORGETLASTBADLSN
	LDX	DCBPOINTER
	LDAA	#$FF	FORGET ANY LAST BAD LSN
	STAA	DSKINFO:BADLSN,X
	STAA	DSKINFO:BADLSN+1,X
	STAA	DSKINFO:BADLSN+2,X
	RTS
*
*	ERRFCBOPENQ -- TEST FOR ERRORMSGS.SYS FCB SET UP ON DISK "DCBPOINTER"
*	RETURN CARRY RESET IF ERRORMSGS.SYS FCB SET UP ON DISK "DCBPOINTER"
*	ELSE RETURN WITH CARRY SET
*
ERRFCBOPENQ
	LDX	ERRFCBPOINTER	IS ERR FCB SET UP AT ALL?
	BEQ	ERRFCBOPENERRRTS	B/ NOT OPEN
	LDX	FCB:DISKINFO,X	ARE ERRORMESSAGES ON CURRENTLY SELECTED DRIVE ?
	CPX	DCBPOINTER	...
	BNE	ERRFCBOPENERRRTS	B/ NO, LEAVE ERROR MESSAGE FILE ALONE
	LDX	ERRFCBPOINTER	THIS WILL BE CONVENIENT FOR CALLER
	OKRTS

ERRFCBOPENERRRTS ; ERROR MSG FCB NOT OPEN TO THIS DRIVE
	ERRORRTS
	PAGE
*
*	DSKDDISMOUNT -- DUMP ALL DISK DEPENDENT DATA BACK TO DISK
*	RELEASES ALL FCBS ATTACHED TO DISK
*	ALSO ZEROS DISKINFO'S IN RDSI'S
*	IF DISK I/O ERROR, LEAVE "DISK MOUNTED"
*
DSKDDISMOUNT	; DISMOUNT A DISK DRIVE
*	FIRST, MAKE SURE THERE ARE NO OPEN FILES!
	LDAA	NFCBS	# FCBS TO SEARCH
	STAA	COUNT
	LDX	CODE+SDOS:CONFIGURATION	NOW SCAN ALL THE FCBS
	LDD	[CNFG:IOCBPOINTERS,X]	= ADDRESS OF LAST FCB + FCB:SIZE
DSKDDISMOUNTL	; CHECK NEXT FCB TO SEE IF OPEN TO THIS DRIVE
	SUBD	#FCB:SIZE	FIND NEXT FCB
	STD	FCBPOINTER
	LDX	FCBPOINTER	IS THIS FCB ACTIVE ?
	LDA	FCB:REFCOUNT,X	...?
	BEQ	DSKDDISMOUNT0	B/ NO, IGNORE IT!
	LDX	FCB:DISKINFO,X	IS THIS FCB FOR DISK BEING DISMOUNTED?
	CPX	DCBPOINTER	...?
	BNE	DSKDDISMOUNT0	B/ NOPE, IGNORE IT
	CMPA	#1	IS IT A SYSTEM FILE OPEN ON SOME I/O CHANNEL ?
	BHI	ERRFILEISOPEN	(REF COUNT > 1 ?)
	LDX	DSKINFO:DIRFCB,X	NO, DIRECTORY FCB FOR THIS DRIVE ?
	CPX	FCBPOINTER	... ?
	BEQ	DSKDDISMOUNT0	B/ YES, THAT'S OK...
	LDX	FCB:DISKINFO,X	NO, MAYBE DISKMAP.SYS FCB ?
	LDX	DSKINFO:MAPFCB,X	... ?
	CPX	FCBPOINTER	... ?
	BEQ	DSKDDISMOUNT0	B/ YES, THAT'S OK
	LDX	ERRFCBPOINTER	IS IT THE ERROR MESSAGE FILE ?
	CPX	FCBPOINTER	... ?
	BNE	ERRFILEISOPEN	B/ NO, A NON-SYSTEM FILE IS STILL OPEN!
DSKDDISMOUNT0	; THIS FCB IS NOT OPEN TO THIS DISK, OR IS A SYSTEM FILE OPEN ONLY ONCE
	LDD	FCBPOINTER	GET FCB ADDRESS
	DEC	COUNT	DECREMENT # FCBS TO SEARCH
	BNE	DSKDDISMOUNTL	B/ MORE FCBS TO CHECK FOR OPEN TO THIS DISK
*	ASSERT: NO NON-SYSTEM FCBS ARE OPEN TO DISK BEING DISMOUNTED
	BSR	ERRFCBOPENQ	IS FCB FOR ERRORMSGS.SYS SET UP?
	BCS	DSKDDISMOUNT2	B/ NOPE
	STX	FCBPOINTER	YES, CHECK TO SEE IF ERRORMSGS.SYS IS DELETED
	LDAA	FCB:FLAGS,X	IS IT DELETED?
	BITA	#FCBFLG::DELETED	...?
	BEQ	DSKDDISMOUNT2	B/ NO, NO SPECIAL ACTION NEEDED
	JSR	DSKFDELETESYSIOCB	YES, PUT CLUSTERS BACK IN FREE SPACE
DSKDDISMOUNT2	; RELEASE HOLD ON DISKMAP.SYS
	JSR	DSKDUMPBUFFERS	DUMP ALL THE SECTOR BUFFERS BACK TO DISK
	LDAA	#CC:DISMOUNTDISK	LET DRIVER KNOW THAT WE ARE DONE WITH THIS DISK
	JSR	DSKDCONTROL1	(DO A CONTROL OPERATION!)
	LDD	#1		NOW SET DISK MAP ALGORITHM BACK TO ONE, JUST TO BE SAFE
	JSR	DSKDSETMAPALG2	MARK ALL SECTORS INVALID, FORGET LAST BAD LSN
	LDX	DCBPOINTER	RESET THE ERROR STATISTICS
	LDA	#$FF	MARK "NO ERROR LSN"
	STA	DSKINFO:ERRLSN,X
	STA	DSKINFO:ERRLSN+1,X
	STA	DSKINFO:ERRLSN+2,X
	LDB	#DSKINFO:ERRLSN-DSKINFO:SEEKERRCNT
DSKDDISMOUNTZ	; ZERO THE ERROR STATISTICS LOOP
	CLR	DSKINFO:SEEKERRCNT,X	ZERO A STATISTIC BYTE
	INX
	DECB
	BNE	DSKDDISMOUNTZ	B/ MORE BYTES TO ZERO
	LDX	DCBPOINTER	RELEASE DIRFCB
	LDX	DSKINFO:DIRFCB,X
	BEQ	DSKDDISMOUNTOKRTS	B/ NOT BUSY, THIS DRIVE WASN'T MOUNTED
	CLR	FCB:REFCOUNT,X	WAS BUSY, LET GO OF FCB
	LDX	DCBPOINTER
	LDX	DSKINFO:MAPFCB,X
	BEQ	DSKDDISMOUNT1	B/ UNUSUAL, THERE WAS NO MAP FILE!
	CLR	FCB:REFCOUNT,X	LET GO OF MAP FCB
DSKDDISMOUNT1	EQU	*
	LDX	DCBPOINTER	MARK ALL SYSTEM FCBS OPEN TO DISK AS AVAILABLE
	CLRA		THE UNIVERSAL "INITIAL" VALUE
	CLRB
	STD	DSKINFO:DIRFCB,X	MARK DISKINFO FCBS AS NOT SET UP
	STD	DSKINFO:MAPFCB,X	(I.E., MARK DISK AS DISMOUNTED)
	JSR	ERRFCBOPENQ	IS ERRORMSGS.SYS FCB SET UP?
	BCS	DSKDDISMOUNTOKRTS	B/ NO, LEAVE ERR FCB ALONE
	CLR	FCB:REFCOUNT,X	RELEASE OUR HOLD ON ERRORMSGS.SYS FILE
	STD	ERRFCBPOINTER	AND FORGET THE FCB
DSKDDISMOUNTOKRTS
	OKRTS

ERRFILEISOPEN	; SOMETHING OTHER THAN A SYSTEM FILE IS OPEN!
	JSR	ERRET
	FDB	ERR:FILEISOPEN
*			NOTE: DELETED FILES ARE IMPORTANT...
*			BECAUSE THEY STILL OWN VALUABLE DISK SPACE
*			THAT HAS NOT YET BEEN DE-ALLOCATED!
	PAGE
*	MARKINVALID -- MARK DISK SECTORS FROM DISK "DCBPOINTER" AS INVALID
*
MARKINVALID
	LDX	LASTSECTORREADQ+RDSI:FLINK	POINTER TO BASE OF LRU QUEUE
MARKINVALIDL	; MARK THIS DISK SECTOR AS INVALID IF FROM DISK "DCBPOINTER"
	LDD	RDSI:DISKINFO,X	IS SECTOR FROM DISK WE'RE MARKING INVALID?
	CMPD	DCBPOINTER	...?
	BNE	MARKINVALID1	B/ NO, LEAVE SECTOR BUFFER ALONE
	CLR	RDSI:MODIFIED,X	MARK SECTOR AS INVALID.
	CLR	RDSI:STATE,X	ASSERT: MODIFIED=STATE=0, BUT I'M PARANOID ANYWAY!
	CLR	RDSI:DISKINFO,X	MARK RDSI AS "INITRDSIS" DID...
	CLR	RDSI:DISKINFO+1,X	SO THEY POINT TO DUMMY DISKINFO TABLE
	LDA	#1	AND MAKE SURE IT DOESN'T SELECT MAGIC "0" SECTOR!
	STAA	RDSI:LSN,X	(I.E., RDSI:LSN,...+1,...+2 <> 0)
*	THIS IS IN CASE MAP ALGORITHM GETS CHANGED...
*	WHICH INVALIDATES THE LOGICAL TO PHYSICAL MAPPING!
	STX	RDSIPOINTER	SAVE POINTER FOR NEXT ITERATION
	JSR	REMOVERDSI	SO THAT IOCB REFERENCES TO SECTOR DISAPPEAR
MARKINVALID1	; PROCESS NEXT SECTOR ON LIST
	LDX	RDSI:FLINK,X	FIND NEXT RDSI
	CPX	#LASTSECTORREADQ
	BNE	MARKINVALIDL	B/ NOT END OF LRU QUEUE
	OKRTS		ALL DONE!
	PAGE	DISK FILE DRIVERS -- DIRECTORY MANAGEMENT ROUTINES
*  SEARCHDIR -- SEARCHES DIRECTORY FILE FOR NAME (X)...
*  	ON DRIVE SPECIFIED BY DCBPOINTER
*  	IF DSKINFO:DIRFCB IS ZERO FOR THIS DRIVE,
*  		SEARCHDIR AUTOMATICALLY LOCATES DIRECTORY.SYS, DISKMAP.SYS, AND ERRORMSGS.SYS
*  		AND FILLS IN THE FCBS IN THE DISKINFO TABLE FOR THESE FILES
*  		(EFFECTIVELY AN AUTOMATIC "MOUNT" DISK COMMAND)
*  	OKRTS MEANS "FILE FOUND" (BUT DIR:HCSIC MIGHT BE ZERO!)
*  		(X), DIRENTRY POINTS TO DIRECTORY ENTRY
*  		DIRENTRYDISP SPECIFIES DISPLACEMENT INTO SECTOR OF DIRECTORY ENTRY
*  		SYSIOCB:CURLSN CONTAINS LOGICAL SECTOR NUMBER OF DIRECTORY SECTOR
*		FCBPOINTER SELECTS DIRECTORY.SYS FILE
*		RDSIPOINTER SELECTS DIRECTORY SECTOR POINTED TO BY (X)
*  	ERRET ON EXIT WITH (X) = "NOT FOUND" MEANS:
*  		FREEDIRLSN CONTAINS LSN OF DIRECTORY SECTOR CONTAINING FREE ENTRY
*  		 = 0 --> DIRECTORY IS FULL
*  		FREEDIRENTRYDISP SPECIFIES DISPLACEMENT INTO SECTOR OF DIRECTORY ENTRY
*		FCBPOINTER SELECTS DIRECTORY.SYS FILE
*  	ERRET WITH (X) <> "FILE NOT FOUND" MEANS
*  		SOME FATAL PROBLEM OCCURRED
*		NO GAURANTEES ABOUT ANYTHING!
*
*  SEARCHDIRCREATE -- SEARCH DIRECTORY FOR FILENAME(X)
*  	AND CREATE EMPTY DIRECTORY SLOT IF FILENAME IS NOT IN DIRECTORY
*  	OKRTS MEANS EITHER THE NAME WAS FOUND, OR AN EMPTY SLOT WAS FOUND
*  		SYSIOCB:CURLSN CONTAINS LSN OF DIRECTORY SECTOR
*  		(X), DIRENTRY CONTAINS POINTER TO DIRECTORY ENTRY (SECTOR IS READ IN)
*		RDSIPOINTER SELECTS DIRECTORY SECTOR POINTED TO BY (X)
*  		DIRENTRYDISP CONTAINS DISPLACEMENT TO DIRECTORY ENTRY FROM SECTORBASE
*  		IF DIR:HCSIC(DIRENTRY) <> 0, THEN AN OLD FILE EXISTS TO BE REPLACED
*  		IF =0, THIS DIRECTORY ENTRY IS FREE IF NO CURRENTLY OPEN FCB OWNS IT
*  		IF DIRECTORY WAS FULL, AND NO FREE SLOTS WERE AVAILABLE,
*  		THE DIRECTORY WAS AUTOMATICALLY EXPANDED BY MINALLOC CLUSTERS
*  		FCBPOINTER SELECTS DIRECTORY.SYS FILE
*  	ERRET MEANS DIRECTORY IS FULL, AND CANNOT BE EXPANDED
*  		SO THERE IS NO ROOM FOR NEW FILE
*  		FCBPOINTER SELECTS DIRECTORY.SYS FILE
	PAGE
*  NOTES:
*  SEARCHDIR ALWAYS COMPARES DESIRED FILENAME WITH DIRENTRY
*  IF MATCH, THEN SEARCHDIR CHECKS VALIDITY OF DIRENTRY (DIR:HCSIC<>0)
*  IF AN "EMPTY" DIRENTRY SLOT IS MATCHED, AND SLOT IS OWNED BY AN OPEN FCB,
*  THEN SEARCHDIR TREATS SLOT AS IF A REAL FILE EXISTED THERE
*  FILES WHICH ARE BRAND NEW (AREN'T REPLACEMENTS) BUT HAVE NOT YET
*  BEEN CLOSED MUST HAVE THEIR NAME PLACED IN A DIRENTRY WITH DIR:HCSIC=0
*  SO IF SYSTEM CRASHES, DIRENTRY IS AUTOMATICALLY FREE
	PAGE
SEARCHDIR	; SEARCH DIRECTORY FOR FILENAME (X)
	BSR	SAVEUSERIOCB	SO WE CAN SET UP OUR OWN AS "IOCBPOINTER"
	BSR	SEARCHDIRE	GO SEARCH DIRECTORY USING SYSIOCB
	BCS	SEARCHDIRERRED	B/ CAN'T FIND IT
*
*	RESTOREUSERIOCB -- RESTORES IOCBPOINTER TO USERIOCBPOINTER
*		RESTORES FCBPOINTER TO IOCB:FCB(USERIOCBPOINTER)
*
RESTOREUSERIOCB	EQU	*
	LDX	USERIOCBPOINTER	RESTORE VALUES TO THOSE OF INITIAL CALL TO DRIVER
	STX	IOCBPOINTER
	LDX	IOCB:FCB,X	RESTORE FCB POINTER TO ITS ORIGINAL VALUE, TOO
	STX	FCBPOINTER
	LDX	#0	MARK SYSTEM IOCB AS "CLOSED"
	STX	SYSIOCB+IOCB:DRIVER
	LDX	DIRENTRY	TO BE NICE
	OKRTS		SAY "WE FOUND AN ENTRY.."

SEARCHDIRERRED	; DIDN'T FIND THE FILE NAME IN THE DIRECTORY
	BSR	RESTOREUSERIOCB	SWITCH BACK TO ORIGINAL IOCBPOINTER
	JMP	ERRORED	GRAB THE ERROR CODE AND EXIT AGAIN
*
*	SAVEUSERIOCB -- COPIES IOCBPOINTER INTO USERIOCBPOINTER
*		PRESERVES (X) ON ENTRY AS "SEARCHNAMEP"
*
SAVEUSERIOCB	EQU	*
	STX	SEARCHNAMEP	SAVE POINTER TO DESIRED NAME
	LDX	IOCBPOINTER	SAVE USER'S DESIRED IOCB
	STX	USERIOCBPOINTER	SO WE CAN SET UP SYSIOCB AS "THE" IOCB
	LDX	SEARCHNAMEP	TO BE COMPATIBLE WITH THE SPECIAL ENTRY POINT
	RTS
	PAGE
*
*	SEARCHDIRE -- ENTRY POINT TO SEARCH DIRECTORY...
*		ASSUMING IOCBPOINTER IS FREE
*
SEARCHDIRE	EQU	*
	STX	SEARCHNAMEP	SAVE POINTER TO NAME TO SEARCH FOR
*	NOW SET UP TO USE SYSIOCB FOR DIRECTORY SEARCH
*		MAKES IOCBPOINTER POINT TO SYSIOCB
*		MAKES SYSIOCB+IOCB:FCB, FCBPOINTER POINT TO...
*		FCB FOR DIRECTORY.SYS IN DISKINFO TABLES
*
	LDX	DCBPOINTER	NOW SET UP SYSIOCB
	LDX	DSKINFO:DIRFCB,X	GET FCB ADDRESS
	STX	FCBPOINTER
	JSR	DSKFOPENSYSIOCB	SET UP SYSIOCB SO WE CAN SEARCH THE DIRECTORY
	LDAA	FCBPOINTER	GET FCB ADDRESS FOR DIRECTORY
	BEQ	SEARCHDIRMOUNT	B/ MUST MOUNT THE DISK
	JMP	SEARCHDIR1	B/ DISK HAS ALREADY BEEN MOUNTED

ERRBOOTCKSUMFAIL	EQU	*
	JSR	ERRET
	FDB	ERR:BOOTCKSUMFAIL

ERRNBPCTOOBIG	EQU	*
	JSR	ERRET
	FDB	ERR:NBPCTOOBIG

ERRWRONGFILESYSTEM	; FILE STRUCTURE FORMAT IS WRONG VERSION
	JSR	ERRET
	FDB	ERR:WRONGFILESYSTEM
	PAGE
**** MUST MOUNT DISK, SET UP DIRECTORY.SYS, DISKMAP.SYS, AND ERRORMSGS.SYS FCBS
**** FIRST, SET UP DSKINFO INFORMATION
SEARCHDIRMOUNT	EQU	*
	JSR	DSKDDISMOUNT	DUMP BUFFERS, WAIT FOR READAHEADS TO QUIT;
**** SHOULD THIS CALL DUMP BUFFERS INSTEAD???
*				MARK THE MAP ALGORITHM AS INVALID
	LDX	#SYSIOCB	GO READ BOOT SECTOR FOR DISK
	CLR	IOCB:CURLSN,X	= SECTOR ZERO
	CLR	IOCB:CURLSN+1,X
	CLR	IOCB:CURLSN+2,X
	JSR	FETCHSECTOR
	LDX	RDSI:SECTORBASE,X	GET ADDRESS OF BOOT SECTOR
	STX	FROMPOINTER	SAVE BOOT SECTOR ADDRESS
	LDAA	BOOT:FILESYSTEMVERSION,X	PROPER FILE SYSTEM VERSION ?
	SUBA	#FILESYSTEMVERSION	...?
	BNE	ERRWRONGFILESYSTEM	B/ NOPE, WE CAN'T READ THIS DISKETTE!
	LDAB	#BOOT:DISKINFOLEN	DO CHECKSUM ON INFO STORED IN BOOT SECTOR
*	CLRA		ZERO THE CHECKSUM
SEARCHDI1	; ADD BYTE TO CHECKSUM
	ADDA	BOOT:DISKINFO,X
	INX		BUMP CHECKSUM SCAN POINTER
	DECB		# BYTES LEFT TO CHECKSUM
	BNE	SEARCHDI1	B/ MORE TO CHECKSUM
	INCA		CHECKSUM OK (=-1)?
	BNE	ERRBOOTCKSUMFAIL	B/ BAD BOOT SECTOR, I QUIT!
	IF	M6800!M6801
	LDD	FROMPOINTER	NOW COPY BOOTSECTOR STUFF TO DISKINFO TABLE
	ADDD	#BOOT:NSPC	= SOURCE OF BOOTSECTOR STUFF
	TDX
	LDD	DCBPOINTER	FIGURE OUT WHERE TO PUT THE STUFF
	ADDD	#DSKINFO:NSPC
	STD	TOPOINTER	= TARGET
	ELSE	(M6809)
	LDX	FROMPOINTER	NOW COPY BOOTSECTOR STUFF TO DISKINFO TABLE
	LEAX	BOOT:NSPC,X	= WHERE TO GET BOOTSECTOR STUFF
	LDY	DCBPOINTER	DECIDE WHERE TO PUT IT
	LEAY	BOOT:NSPC,Y
	FIN
	LDD	#BOOT:PARAMSIZE	= # BYTES TO COPY
	JSR	CODE+SDOS:BLOCKMOVE	GO MOVE IT!
	LDX	DCBPOINTER	COMPUTE NUMBER OF BYTES PER CLUSTER
	LDAA	DSKINFO:LOG2NBPS,X	= DSKINFO:NSPC*2^DSKINFO:LOG2NBPS
	STAA	TEMPA	(NUMBER OF TIMES TO LEFT SHIFT)
	CLRA		DO MULTIPLY BY SHIFTING
	LDAB	DSKINFO:NSPC,X	*** NOTE: DSKINFO:NBPC <= 65535 !! ****
SEARCHDI3	; SHIFT LEFT ONE PLACE
	ASLD		* 2^1
	BCS	ERRNBPCTOOBIG	B/ DISKINFONBPC >= 65536!
	DEC	TEMPA	# PLACES LEFT TO LEFT SHIFT
	BNE	SEARCHDI3	B/ SHIFT LEFT SOME MORE
	STD	DSKINFO:NBPC,X	STORE NUMBER OF BYTES PER CLUSTER
	PAGE
*
*	COMPUTE NLCN:=INT(NLSN/NSPC)
*
	LDAA	#17	# QUOTIENT BITS TO GENERATE
	STAA	TEMPA	(FIRST Q BIT MUST ALWAYS BE 0!)
	LDB	DSKINFO:NLSN+2,X	SET (A,B,TEMP.DIVIDEND):=NLSN
	STB	TEMPB
	LDD	DSKINFO:NLSN,X
SEARCHDI5L	; GENERATE COMPLEMENTED QUOTIENT BIT
	SUBA	DSKINFO:NSPC,X
	BCC	SEARCHDI5A	B/ WENT IN, Q BIT = 1!
	ADDA	DSKINFO:NSPC,X	DIDN'T GO IN, RESTORE DIVIDEND (ASSERT: SETS CARRY)
SEARCHDI5A	EQU	*
	ROL	DSKINFO:NLCN+1,X	SHIFT COMPLEMENTED QUOTIENT BIT INTO QUOTIENT
	ROL	DSKINFO:NLCN,X
	ASL	TEMPB	SHIFT DIVIDEND LEFT ONE BIT
	ROLD
	DEC	TEMPA	DOWN COUNT # Q BITS TO GENERATE
	BNE	SEARCHDI5L	B/ GEN ANOTHER Q BIT
	COM	DSKINFO:NLCN,X	COMPLEMENT QUOTIENT TO GET TRUE VALUE
	COM	DSKINFO:NLCN+1,X
	PAGE
*
*	NOW GO LOOK UP CRITICAL SYSTEM FILES ***
*
*
	LDD	DSKINFO:NLCN,X	COMPUTE LCN OF MIDDLE OF DISK
	LSRD		= INT(NLCN/2)
	STD	DSKINFO:RANDMAP,X	INITZ RANDMAP TO MIDDLE OF DISK
	LDX	RDSIPOINTER	USE BOOT:DIRLSN TO FIND DIRECTORY.SYS ENTRY
	LDX	RDSI:SECTORBASE,X
	LDAA	BOOT:DIRLSN,X	FETCH BOOT:DIRLSN FROM BOOT SECTOR...
	STAA	SYSIOCB+IOCB:CURLSN	AND STUFF INTO SYSTEM IOCB
	LDX	BOOT:DIRLSN+1,X
	STX	SYSIOCB+IOCB:CURLSN+1
	LDX	#SYSIOCB
	JSR	FETCHSECTOR	READ IN DIRECTORY DATA SECTOR
*			GET ADDRESS OF DIRECTORY.SYS ENTRY...
	LDX	RDSI:SECTORBASE,X	WHICH (RULE!!) MUST BE 1ST ENTRY BOOT:DIRLSN
	STX	DIRENTRY	SET UP SO WE CAN FIND A FREE FCB
	LDAA	DIR:HCSIC,X	IS DIRECTORY.SYS ENTRY VALID?
	LBEQ	ERRDIRECTORYDAMAGED	B/ NO
	LDX	#0	SET UP DIRENTRYDISP CORRECTLY
	STX	DIRENTRYDISP
	LDX	#DIRECTORYNAME	VERIFY VALIDITY OF DIRECTORY.SYS ENTRY
	JSR	COMPARENAMES
	LBNE	ERRDIRECTORYDAMAGED	B/ DIRECTORY.SYS NAME IS TRASH
	JSR	FINDFREEFCB	SINCE THIS DISK IS BEING MOUNTED, CAN'T BE ANY OPEN FCBS TO IT!
	LDX	DCBPOINTER	SET UP FCB FOR DIRECTORY.SYS
	STD	DSKINFO:DIRFCB,X	SO THAT SUBSEQUENT CALLS TO "SEARCHDIR" WILL WORK
	LDX	SEARCHNAMEP	SAVE POINTER TO NAME THAT WE REALLY WANTED
	STX	SEARCHSAVEP	SO WE CAN SEARCH FOR DISKMAP.SYS, ERRORMSGS.SYS FILE
	LDX	#DISKMAPNAME	NOW GO FIND THE DISKMAP.SYS FILE
	JSR	SEARCHDIRE	(IOCBPOINTER IS FREE)
	BCS	SEARCHDI3A	B/ NO DISKMAP.SYS, WON'T BE ABLE TO MAKE NEW FILES
	JSR	FINDFREEFCB	FIND FCB FOR MAP FILE
	LDX	DCBPOINTER	SAVE FCB ADDRESS OF MAP FILE
	STD	DSKINFO:MAPFCB,X	IN CASE SOMEONE INSISTS IN OPENING MAP
	CLR	SYSIOCB+IOCB:HRSN	COMPUTE LSN OF 1ST SECTOR IN MAP CLUSTER
	JSR	GENHLSNPLUSHRSN	SET UP TO READ 1ST SECTOR OF MAP HEADER CLUSTER
	JSR	FETCHSECTOR	GO READ SECTOR ZERO OF MAP HEADER
	LDX	RDSI:SECTORBASE,X
	LDD	HEADER:LCN+LCN:SIZE,X	=2ND LCN IN HEADER
	CLR	SYSIOCB+IOCB:DRSN	SO CONVERSION WILL NOT BE AFFECTED
	JSR	CONVERTLCNTOLSN	CONVERT LCN OF CLUSTER INTO LSN
	LDAA	SECTORDB:LSN,X	COPY LSN TO (TOS), (A,B)
	PSHA	
	LDD	SECTORDB:LSN+1,X
	LDX	DCBPOINTER	NOW MOVE MAP CLUSTER LSN ...
	STD	DSKINFO:MAPLSN+1,X	INTO DISKINFO TABLE FOR EASY ACCESS
	PULA
	STAA	DSKINFO:MAPLSN,X
	PAGE
SEARCHDI3A	EQU	*
	LDX	DEFAULTDISKDCB	ARE WE MOUNTING THE DEFAULTDISK ?
	CPX	DCBPOINTER	(I.E., DO WE NEED TO SET UP ERROR MESSAGE FCB ?)
	BNE	SEARCHDI4	B/ NO, WE'D NEVER USE IT!
	LDX	ERRFCBPOINTER	IS ERRORMESSAGE FILE ALREADY SET UP?
	BNE	SEARCHDI4	B/ YES, LEAVE IT ALONE
	LDX	#ERRMSGSNAME	GO LOOK UP ERRORMSGS.SYS FILE
*	IF FILE IS ALREADY SET UP ON SAME OTHER DISK, IT SHOULD BE RELEASED AND MOVED HERE!
	JSR	SEARCHDIRE	(IOCBPOINTER IS FREE)
	BCS	SEARCHDI4	B/ NOT IN DIRECTORY, TOO BAD
	JSR	FINDFREEFCB	FOUND ERROR MESSAGES FILE
	STD	ERRFCBPOINTER	SAVE ERRORMSGS.SYS FCB ADDRESS SO WE CAN DO A QUICK OPEN LATER
SEARCHDI4	EQU	*
	LDX	SEARCHSAVEP	GET POINTER TO NAME WE ORIGINALLY WANTED
	JMP	SEARCHDIRE	GO SEARCH THE DIRECTORY AS WE ORIGINALLY INTENDED!

SEARCHDIRF	; FOUND FILE NAME MATCH
	BSR	COMPUTEDIRENTRYDISPLACEMENT
	LDX	DIRENTRY	IS THIS DIRECTORY ENTRY ACTUALLY IN USE ?
	LDAA	DIR:HCSIC,X	...?
	BNE	SEARCHDIROKRTS	B/ YES, WE FOUND DESIRED DIRECTORY ENTRY
	JSR	SEARCHFCBINIT	START UP THE SEARCH
	JSR	SEARCHFCBS	DOES SOME FCB OWN THIS SLOT ?
	BCS	SEARCHDIRL1	ITS JUST AN EMPTY SLOT!!!
	LDX	DCBPOINTER	SET UP FCB POINTER TO SELECT DIRECTORY.SYS AGAIN
	LDX	DSKINFO:DIRFCB,X	SINCE SEARCHFCBS BOMBED IT
	STX	FCBPOINTER
SEARCHDIROKRTS	EQU	*
	LDX	DIRENTRY	GRAB POINTER TO DIRECTORY ENTRY AGAIN
	OKRTS		WE FOUND THE FILE!
*
*	COMPUTEDIRENTRYDISPLACEMENT -- COMPUTES DIRENTRYDISP
*
COMPUTEDIRENTRYDISPLACEMENT
	LDD	DIRENTRY	COMPUTE DIRECTORY ENTRY DISPLACEMENT
	LDX	SYSIOCB+IOCB:DRDSI	ASSERT: IOCB:DRDSI <> 0
	STX	RDSIPOINTER	CHEAT, CHEAT! MAKE THIS MOST RECENTLY READ SECTOR
	SUBD	RDSI:SECTORBASE,X
	STD	DIRENTRYDISP
	RTS

ERRDIRECTORYDAMAGED
	JSR	ERRET
	FDB	ERR:DIRECTORYDAMAGED
	PAGE
SEARCHDIR1	; DISKINFO IS ALL SET, LET'S GO!
	LDX	SEARCHNAMEP	GET POINTER TO NAME
	JSR	HASHNAME	DO INITIAL HASH
	CLR	WRITEFLAG	TELL LOCATECURBYTE THAT WE'RE READING
	LDX	DCBPOINTER	GET DIRECTORY SECTOR SIZE
	LDD	DSKINFO:NBPS,X
	JSR	LOCATECURBYTE	GO FIND DIRECTORY ENTRY
	LDAA	SYSIOCB+IOCB:CURLSN	REMEMBER STARTING LSN OF DIRECTORY SEARCH
	STAA	SEARCHSTARTLSN	SO WE KNOW WHEN TO STOP
	LDX	SYSIOCB+IOCB:CURLSN+1
	STX	SEARCHSTARTLSN+1
	CLR	FREEDIRLSN	MARK 'NO FREE DIRECTORY ENTRY FOUND'
	CLR	FREEDIRLSN+1
	CLR	FREEDIRLSN+2
	PAGE
SEARCHDIRS	; SEARCH DIRECTORY SECTOR FOR NAME
	LDX	SYSIOCB+IOCB:DRDSI	GET ADDRESS OF DIRECTORY SECTOR
	LDX	RDSI:SECTORBASE,X	NOTE: THIS DEPENDS ON AT LEAST 2 RDSI'S AVAILABLE!
	STX	DIRENTRY	AND USE AS SEARCH STARTING POINT
	LDX	DCBPOINTER	COMPUTE END OF DIRECTORY SECTOR
	LDD	DSKINFO:NBPS,X	=SECTORBASE + DSKINFO:NBPS
	ADDD	DIRENTRY
	STD	DIRSECTOREND	REMEMBER END OF DIRSECTORY
SEARCHDIRL	EQU	*
	LDX	SEARCHNAMEP	GET ADDRESS OF NAME TO FIND
	JSR	COMPARENAMES	COMPARE AGAINST DIRECTORY ENTRY
	LBEQ	SEARCHDIRF	B/ WE FOUND IT!
	LDX	DIRENTRY	IS THIS DIRECTORY ENTRY VALID?
	LDAA	DIR:HCSIC,X
	BNE	SEARCHDIRN	B/ YES, GO FIND NEXT ENTRY
SEARCHDIRL1	EQU	*
	LDAA	FREEDIRLSN	NO, FOUND EMPTY DIRECTORY ENTRY
	ORAA	FREEDIRLSN+1	HAVE WE RECORDED EXISTENCE...
	ORAA	FREEDIRLSN+2	OF FREE DIRECTORY ENTRY YET?
	BNE	SEARCHDIR2	B/ YES, DON'T RECORD ANOTHER
	BSR	COMPUTEDIRENTRYDISPLACEMENT
	JSR	SEARCHFCBINIT	SET UP TO SEARCH THE FCBS
	JSR	SEARCHFCBS	IS THIS ENTRY IN USE?
	BCC	SEARCHDIR2	B/ YES, FIND NEXT DIRECTORY ENTRY
	LDAA	SYSIOCB+IOCB:CURLSN	SAVE LSN OF CURRENT DIRECTORY SECTOR
	STAA	FREEDIRLSN	AS LSN OF FREE ENTRY
	LDX	SYSIOCB+IOCB:CURLSN+1
	STX	FREEDIRLSN+1
	LDD	DIRENTRYDISP	
	STD	FREEDIRENTRYDISP	AND SAVE IN CASE WE DON'T FIND THE NAME IN THE DIRECTORY
	PAGE
SEARCHDIR2	EQU	*
	LDX	DCBPOINTER	MAKE FCBPOINTER SELECT DIRECTORY.SYS FILE AGAIN
	LDX	DSKINFO:DIRFCB,X
	STX	FCBPOINTER	SINCE SEARCHFCBS DESTROYED IT!
SEARCHDIRN	EQU	*
	LDD	DIRENTRY	NOT HERE, FIND NEXT DIRECTORY ENTRY
	ADDD	#DIR:ENTRYSIZE	=THIS ENTRY + DIR:ENTRYSIZE
	STD	DIRENTRY
	LDX	DIRENTRY	HIT END OF DIRECTORY SECTOR?
	CPX	DIRSECTOREND
	BNE	SEARCHDIRL
	JSR	UPDATECURBYTEF	RECORD FILE ADVANCE DELTA
	BCC	SEARCHDIRT	B/ NOT EOF, GO LOCATE NEXT SECTOR
	CPX	#ERR:EOFHIT	CHECK: END OF FILE HIT ?
	BNE	SEARCHDIRERRED4	B/ NO, SOME REAL PROBLEM!
	CLR	SYSIOCB+IOCB:EOFFLAG	RESET "EOF HIT" FLAG
	LDX	#0	HIT END OF DIRECTORY
	STX	SYSIOCB+IOCB:CURBYTE	RESET CURBYTE TO START OF DIRECTORY
	STX	SYSIOCB+IOCB:CURBYTE+2
	CLR	SYSIOCB+IOCB:LOCATEDF	FORCE RE-LOCATION OF SECTOR
SEARCHDIRT	; TEST FOR ENTIRE DIRECTORY SEARCHED
	LDX	DCBPOINTER	GET DIRECTORY SECTOR SIZE
	LDD	DSKINFO:NBPS,X
	JSR	LOCATECURBYTE	AND GO LOCATE THE DESIRED SECTOR
	LDX	SYSIOCB+IOCB:CURLSN+1	I.E., IS CURLSN=START-OF-SEARCH LSN?
	CPX	SEARCHSTARTLSN+1	(TEST LS 16 BITS FIRST...
	BNE	SEARCHDIRSJ	BECAUSE OF HIGHEST PROBABILITY
	LDAA	SYSIOCB+IOCB:CURLSN	OF MISMATCH)
	CMPA	SEARCHSTARTLSN
	BEQ	SEARCHDIRNF	B/ FILE NOT FOUND
SEARCHDIRSJ	EQU	*
	JMP	SEARCHDIRS	GO SEARCH ANOTHER DIRECTORY SECTOR

SEARCHDIRERRED4	EQU	*
	RTS
	PAGE
SEARCHDIRNF	; FILE NOT FOUND IN DIRECTORY
	JSR	ERRET	CAUSE AN ERROR OF APPROPRIATE TYPE
	FDB	ERR:FILENOTFOUND

DIRECTORYNAME	FCC	"DIRECTORY.SYS"
	RPT	DIR:NAMESIZE-(*-DIRECTORYNAME)
	FCC	" "

DISKMAPNAME	FCC	"DISKMAP.SYS"
	RPT	DIR:NAMESIZE-(*-DISKMAPNAME)
	FCC	" "

ERRMSGSNAME	FCC	"ERRORMSGS.SYS"
	RPT	DIR:NAMESIZE-(*-ERRMSGSNAME)
	FCC	" "
	PAGE
SEARCHDIRCREATE	; SEARCH DIRECTORY AND EXPAND IF NECESSARY
	JSR	SAVEUSERIOCB	SO WE CAN SET UP SYSIOCB AS IOCBPOINTER
SEARCHDIRCREAT1	EQU	*
	JSR	SEARCHDIRE	GO LOOK FOR FILE FIRST
	BCC	SEARCHDIRCOKRTS	B/ FOUND IT, GET OUT!
	CPX	#ERR:FILENOTFOUND	A TRUE 'FILE NOT FOUND' ERROR ?
	BNE	SEARCHDIRCERRED	B/ NOPE, I GIVE UP!
	LDAA	FREEDIRLSN	NOT FOUND, DID WE FIND A FREE SLOT?
	ORAA	FREEDIRLSN+1
	ORAA	FREEDIRLSN+2
	BNE	SEARCHDIRCNEW	B/ YES, TAKE OK EXIT
	CLR	SYSIOCB+IOCB:EOFFLAG	FORCE ALLOCCLUSTERS TO ZERO
	LDX	SYSIOCB+IOCB:FCB	SET UP IOCB:RDCN...
	LDX	FCB:NCLUSTERS,X	TO ALLOCATE ANOTHER CLUSTER
	STX	SYSIOCB+IOCB:RDCN
	LDD	#1	(A,B):= # CLUSTERS TO ALLOCATE
	JSR	ALLOCATECLUSTERS	GO ALLOCATE (AND ZERO!) ANOTHER CLUSTER
	BCS	SEARCHDIRCERRED	B/ PROBABLY NO DISK SPACE
	LDX	DCBPOINTER	ADD 1 CLUSTER SIZE TO FILE LENGTH
	LDD	DSKINFO:NBPC,X
	LDX	SYSIOCB+IOCB:FCB
	ADDD	FCB:FILESIZE+2,X
	STD	FCB:FILESIZE+2,X
	BCC	SEARCHDIRCREAT2	B/ NO CARRY TO PROPOGATE
	INC	FCB:FILESIZE+1,X
	BNE	SEARCHDIRCREAT2
	INC	FCB:FILESIZE,X
SEARCHDIRCREAT2	EQU	*
	LDX	SEARCHNAMEP	GET POINTER TO DESIRED FILE NAME
	BRA	SEARCHDIRCREAT1	AND START OVER AGAIN
	PAGE
SEARCHDIRCNEW	; MUST BE NEW SLOT
	LDX	FREEDIRENTRYDISP	SET UP DIRENTRYDISP
	STX	DIRENTRYDISP
	LDX	FREEDIRLSN+1	MAKE SURE DIRECTORY ENTRY DESIRED...
	STX	SYSIOCB+IOCB:CURLSN+1	IS IN MEMORY BY READING IT IN AGAIN!
	LDX	#SYSIOCB	COPY FREEDIRLSN TO SYSIOCB...
	LDAA	FREEDIRLSN	SO IT LOOKS JUST LIKE SEARCHDIR FOUND IT
	STAA	IOCB:CURLSN,X
	JSR	READSECTOR	READ IN THIS DISK SECTOR
	BCS	SEARCHDIRCERRED	B/ DISK I/O PROBLEM
	LDD	RDSI:SECTORBASE,X	COMPUTE DIRENTRY POINTER JUST LIKE SEARCHDIR WOULD HAVE
	ADDD	FREEDIRENTRYDISP
	STD	DIRENTRY
	JSR	WAITRDSI	WAIT FOR SECTOR TO ARRIVE
	BCS	SEARCHDIRCERRED	B/ DISK I/O ERROR
SEARCHDIRCOKRTS	; FOUND A DIRECTORY ENTRY FOR NEW FILE
	JMP	RESTOREUSERIOCB	AND EXIT

SEARCHDIRCERRED	EQU	*
	JMP	SEARCHDIRERRED
	PAGE
*
*	LOCATEDIRENTRY -- GET DIRECTORY SECTOR FOR FCB INTO MEMORY
*		SET UP DIRENTRY TO POINT TO DIRECTORY SLOT FOR FCB
*		EXIT PRODUCES EXACTLY THE SAME RESULTS AS OKRTS FROM "SEARCHDIR"
*			(EXCEPT FCBPOINTER IS NOT DISTURBED!)
*
LOCATEDIRENTRY	EQU	*
	LDX	FCBPOINTER	GET POINTER TO DIRECTORY SECTOR DESCRIPTOR
	JSR	READSECTOR	READ IN DIRLSN
	LDAA	RDSI:LSN,X	SAVE LSN IN SYSIOCB IN CASE WE NEED IT LATER!
	STAA	SYSIOCB+IOCB:CURLSN
	LDD	RDSI:LSN+1,X
	STD	SYSIOCB+IOCB:CURLSN+1
	LDD	RDSI:SECTORBASE,X	ADD SECTOR BASE TO DIR DISPLACEMENT
	LDX	FCBPOINTER	TO GET DIRENTRY ADDRESS
	ADDD	FCB:DIRDISP,X
	STD	DIRENTRY
	LDD	FCB:DIRDISP,X	SET UP DIRECTORY ENTRY DISPLACEMENT
	STD	DIRENTRYDISP
	JSR	WAITRDSI	WAIT FOR SECTOR TO ARRIVE
	LDX	DIRENTRY
	OKRTS
	PAGE
*
*	UPDATEDIRFROMFCB -- UPDATE DIRECTORY FROM FCB(FCBPOINTER)
*	UPDATE DIRECTORY ENTRY IF FCB IS MARKED AS "UPDATEDIR" AND NOT "DELETED"
*
UPDATEDIRFROMFCB	EQU	*
	LDX	FCBPOINTER	PEEK AT FCB
	LDAA	FCB:FLAGS,X	DELETED FILE ?
	BITA	#FCBFLG::DELETED	...?
	BNE	UPDATEDIROKRTS	B/ YES, DON'T BOTHER UPDATING DIRECTORY
	BITA	#FCBFLG::UPDATEDIR	MUST WE UPDATE THE DIRECTORY ?	
	BEQ	UPDATEDIROKRTS	B/ DON'T HAVE TO UPDATE DIRECTORY
LOCUPDATEDIRENTRY	; ENTRY POINT TO LOCATE AND UPDATE DIRECTORY ENTRY
	BSR	LOCATEDIRENTRY	RATS, MUST UPDATE DIRECTORY
UPDATEDIRENTRY	; ENTRY FOR DIRENTRY ALREADY LOCATED
	STX	TOPOINTER	COPY ALL PERTINANT FCB DATA TO DIRECTORY ENTRY
	LDX	FCBPOINTER	FCB DATA IS STORED IN SAME FORMAT...
	LDAB	#DIR:FCBDATASIZE	(# FCB BYTES TO COPY TO DIR)
UPDATEDIRL	EQU	*
	LDAA	FCB:HLCN,X	AS DIRECTORY ENTRY DATA
	INX		UPDATE "FROM" POINTER
	STX	FROMPOINTER
	LDX	TOPOINTER	STORE FCB ENTRY INTO DIRECTORY ENTRY
	STAA	DIR:HLCN,X	
	INX		UPDATE "TO" POINTER
	STX	TOPOINTER
	LDX	FROMPOINTER
	DECB		DOWN COUNT # BYTES TO COPY
	BNE	UPDATEDIRL
	JSR	MARKMODIFIED	FORCE DIRECTORY SECTOR BACK TO DISK
	LDX	FCBPOINTER	RESET THE "UPDATEDIR" FLAG
	LDAA	FCB:FLAGS,X
	ANDA	#(\FCBFLG::UPDATEDIR)&$FF
	STAA	FCB:FLAGS,X
UPDATEDIROKRTS	EQU	*
	OKRTS
	PAGE
*
*	COMPARENAMES -- COMPARES (X) TO (DIRENTRY) FOR DIR:NAMESIZE BYTES
*		DOES NOT DISTURB BUFFER
*		RETURNS Z SET IF MATCH
*		RESET IF NO MATCH
*
COMPARENAMES	EQU	*
	IF	M6800!M6801
	STX	TOPOINTER	SAVE "COMPARE TO" POINTER
	LDX	DIRENTRY	GET COMPARE AGAINST POINTER
	ELSE	(M6809)
	LDY	DIRENTRY	GET COMPARE AGAINST POINTER
	FIN
	LDAB	#DIR:NAMESIZE	NUMBER OF BYTES TO COMPARE
*
*	BLOCKCOMPARE -- COMPARE (X) TO (Y) FOR (B) BYTES
*	(ON 6800/6801, (Y) IS LOCATION 0)
*	RETURN CC "Z" BIT SET IF MATCH, ELSE RESET IF NO MATCH
*
BLOCKCOMPARE	; COMPARE (X) TO (Y) FOR (B) BYTES
	IF	M6800!M6801
	LDA	,X+	GET BYTE TO COMPARE
	STX	FROMPOINTER	SAVE "FROM" POINTER
	LDX	TOPOINTER	= "TO" POINTER
	CMPA	,X	COMPARE BYTES
	BNE	BLOCKCOMPARERTS	B/ STRINGS DON'T MATCH
	INX
	STX	TOPOINTER	SAVE UPDATE "TO" POINTER
	LDX	FROMPOINTER	GET SOURCE POINTER BACK
	ELSE	(M6809)
	LDA	,X+	GET A BYTE TO COMPARE
	CMPA	,Y+	COMPARE THEM
	BNE	BLOCKCOMPARERTS	B/ NO MATCH
	FIN
	DECB		DOWN COUNT # BYTES TO COMPARE
	BNE	BLOCKCOMPARE	B/ MORE TO COMPARE
BLOCKCOMPARERTS
	RTS
	PAGE
*
*	HASHFOLD -- SUM NEXT 4 BYTES(X), MASK TO 6 BITS
*		RETURN SIX BIT SUM IN (A)
*		BUMP (X) BY 4
*
HASHFOLD	EQU	*
	LDAA	,X	SUM NEXT 4 BYTES
	ADDA	1,X
	ADDA	2,X
	ADDA	3,X
	ANDA	#%00111111	MASK OFF ASCII ZONE BITS
	LEAX	4,X	BUMP (X) BY 4
	RTS
*
*	HASHINSERT6 -- LEFT SHIFT (IOCB:CURBYTE+1,+2,(A)) 6 BITS
*
HASHINSERT6	EQU	*
	LDAB	#6	GET COUNT OF 6
HASHINSERT6L	EQU	*
	ASLA		SHIFT LEFT 1 BIT
	ROL	IOCB:CURBYTE+2,X
	ROL	IOCB:CURBYTE+1,X
	DECB		DO IT 6 TIMES
	BNE	HASHINSERT6L
	RTS
	PAGE
*
*	HASHNAME --  CONVERT NAME INTO INITIAL PROBE INTO DIRECTORY
*		FOLDS NAME(X) INTO 3 BYTES
*		DIVIDES BY DIRECTORYSIZE TO GET REMAINDER
*		AND PLACES REMAINDER IN IOCB:CURBYTE(SYSIOCB)
*		GUARANTEES INITIAL PROBE IS TO START OF SECTOR
*
HASHNAME	EQU	*
	BSR	HASHFOLD	FOLD LEFT MOST 4 BYTES...
	PSHA		TO GET RIGHTMOST 6 BITS
	BSR	HASHFOLD	FOLD MIDDLE LEFT 4 BYTES...
	PSHA		TO GET BITS 11-6
	BSR	HASHFOLD	FOLD MIDDLE RIGHT 4 BYTES...
	PSHA		TO GET BITS 17-12
	BSR	HASHFOLD	FOLD RIGHTMOST 4 BYTES...
	LDX	#SYSIOCB	TO GET LEFTMOST 6 BITS
	BSR	HASHINSERT6	INSERT BITS 17-12
	PULB
	ABA
	BSR	HASHINSERT6	INSERT BITS 11-7
	PULB
	ABA
	BSR	HASHINSERT6	INSERT BITS 6-0
	PULB
	ABA
	STAA	IOCB:CURBYTE+3,X	STORE RESULT IN IOCB:CURBYTE,X
*	CLR	IOCB:CURBYTE,X	NOW WE HAVE 24 BITS OF FOLDED NAME
	LDX	IOCB:DCB,X	NOW COMPUTE # SECTORS IN DIRECTORY
	LDAA	DSKINFO:LOG2NBPS,X	= DIRSIZE/NBPS
	STAA	COUNT	= DIRSIZE/2^LOG2NBPS
	PSHA		WE'LL NEED THIS LATER
	LDX	FCBPOINTER	GET ADDRESS OF DIRECTORY.SYS FCB
	LDD	FCB:FILESIZE+2,X	COPY DIRSIZE TO (A,B,TEMP.DIVISOR,TEMP.DIVISOR+1)
	STD	TEMP.DIVISOR
	LDD	FCB:FILESIZE,X
HASHNAMEL1	EQU	*
	LSRD		DIVISOR:=DIVISOR/2...
	ROR	TEMP.DIVISOR
	ROR	TEMP.DIVISOR+1	NOTE: NUMBER OF SECTORS IN DIRECTORY <= 32767
	DEC	COUNT	LOG2NBPS TIMES
	BNE	HASHNAMEL1	LEAVING (A,B) = 0
*	NOW DIVIDE FOLDED NAME BY # OF SECTORS IN DIRECTORY TO GET REMAINDER
	LDX	#SYSIOCB	GET ADDRESS OF WORK IOCB
	LDAB	#24	# QUOTIENT BITS TO GENERATE
	STAB	COUNT	(AND IGNORE)
	CLRA		ZERO OUT THE REMAINDER
	CLRB
HASHNAMEL2	EQU	*
	ASL	IOCB:CURBYTE+3,X	SHIFT NEW DIVIDEND BIT INTO REMAINDER
	ROL	IOCB:CURBYTE+2,X	NOTE: CARRIES ARE NOT IMPORTANT...
	ROL	IOCB:CURBYTE+1,X	BECAUSE WE ONLY WANT REMAINDER, NOT QUOTIENT
	ROLD
	SUBD	TEMP.DIVISOR	DOES DIVISOR GO IN?
	BCC	HASHNAME2	B/ YEP, LEAVE IT IN
	ADDD	TEMP.DIVISOR	NO, RESTORE BACK
HASHNAME2	EQU	*
	DEC	COUNT	DOWN COUNT # QUOTIENT BITS TO IGNORE
	BNE	HASHNAMEL2	B/ MORE Q BITS TO IGNORE
	STAA	IOCB:CURBYTE+2,X	STORE FOLDEDNAME MOD NUMBERSECTORS...
	CLR	IOCB:CURBYTE+1,X	INTO (IOCB:CURBYTE,B)
	CLR	IOCB:CURBYTE,X
	PULA		NOW MULTIPLY BY 2^LOG2NBPS...
HASHNAMEL3	; TO GET LOGICAL BYTE NUMBER OF DESIRED SECTOR
	ASLB
	ROL	IOCB:CURBYTE+2,X
	ROL	IOCB:CURBYTE+1,X
	ROL	IOCB:CURBYTE,X
	DECA		DOWN COUNT # TIMES TO LEFT SHIFT
	BNE	HASHNAMEL3
	STAB	IOCB:CURBYTE+3,X	STORE LAST BYTE OF :CURBYTE
	RTS		AND EXIT
	PAGE
*
*	FINDFREEFCB -- LOOKS FOR FCB WITH ZEROED REF COUNT
*		OKRTS IF FOUND
*			FCB IS INITIALIZED FROM DIRENTRY
*			REF COUNT IS SET TO ONE
*		FCBPOINTER, (X), (A,B) ARE LEFT POINTING TO FCB
*		ERRET IF NONE AVAIALABLE
*
FINDFREEFCB	EQU	*
	LDAA	NFCBS	GET # FCBS TO SEARCH
	IF	M6800!M6801
	STAA	COUNT
	LDX	CODE+SDOS:CONFIGURATION
	LDD	[CNFG:IOCBPOINTERS,X]	GET ADDRESS OF 1ST IOCB
FINDFREEFCBL	EQU	*
	SUBD	#FCB:SIZE	BACK UP TO PREVIOUS FCB
	TDX
	TST	FCB:REFCOUNT,X	IS THIS FCB BUSY?
	BEQ	SETUPFCB	B/ NO
	DEC	COUNT	YES, DOWN COUNT # FCB'S TO CHECK
	ELSE	(M6809)
	LDX	CODE+SDOS:CONFIGURATION
	LDX	[CNFG:IOCBPOINTERS,X]	GET ADDRESS OF 1ST DCB
FINDFREEFCBL
	LEAX	-FCB:SIZE,X	FIND PREVIOUS FCB
	TST	FCB:REFCOUNT,X	IS THIS FCB BUSY?
	BEQ	SETUPFCB	B/ NO
	DECA			YES, DOWN COUNT # FCBS TO SEARCH
	FIN
	BNE	FINDFREEFCBL	B/ MORE TO CHECK
	JSR	ERRET	OOPS... NO MORE FCBS???
	FDB	ERR:NOFREEFCBS
	PAGE
*	SETUPFCB -- COPY CRITICAL DIRENTRY DATA TO FCB(X)
*		SET REFCOUNT ON FCB(X) TO 1
*		ALSO COMPUTES FCB:HLSN FROM FCB:HLCN
*
SETUPFCB	EQU	*
	STX	FCBPOINTER	SAVE FCB ADDRESS
	STX	TOPOINTER	SET UP TARGET POINTER
	LDX	DIRENTRY	COPY MOST OF DIRECTORY ENTRY...
	LDAB	#DIR:FCBDATASIZE	INTO THE FCB
SETUPFCBCL	; COPY A DIRECTORY ENTRY BYTE
	LDAA	DIR:HLCN,X
	INX
	STX	FROMPOINTER
	LDX	TOPOINTER	INTO FCB BYTE
	STAA	FCB:HLCN,X	NOTE: DIR AND FCB FORMATS...
	INX		ARE ISOMETRIC WRT DIRECTORY INFO
	STX	TOPOINTER
	LDX	FROMPOINTER
	DECB
	BNE	SETUPFCBCL	B/ MORE BYTES TO COPY
	LDD	DCBPOINTER	NOW FINISH FILLING THE FCB
	LDX	FCBPOINTER
	STD	FCB:DISKINFO,X
	LDAA	#1	MARK FCB AS 'BUSY'
	STAA	FCB:REFCOUNT,X
	CLR	FCB:FLAGS,X	RESET ALL THE FLAG BITS
	LDD	FCB:HLCN,X	NOW COMPUTE FCB:HLSN = FCB:HLCN * DSKINFO:NSPC
	JSR	CONVERTLCNTO1ST
	LDX	FCBPOINTER	MOVE LSN OF 1ST HEADER SECTOR INTO FCB
	STD	FCB:HLSN+1,X
	LDAA	SYSSECTORDB+SECTORDB:LSN
	STAA	FCB:HLSN,X
SETUPFCBDIRPOS	; SETUP DIRENTRY DATA IN FCB
	LDAA	SYSIOCB+IOCB:CURLSN	SAVE LSN OF DIRECTORY SECTOR
	STAA	FCB:DIRLSN,X
	LDD	SYSIOCB+IOCB:CURLSN+1
	STD	FCB:DIRLSN+1,X
	LDD	DIRENTRYDISP	AND DISPLACEMENT TO ENTRY
	STD	FCB:DIRDISP,X
	LDD	FCBPOINTER	SET (A,B):=FCBPOINTER BECAUSE THIS IS SOMETIMES CONVENIENT
	OKRTS
	PAGE
*
*	SEARCHFCBINIT -- INITIALIZE FOR SEARCH OF FCBS
*		SETS UP FCBPOINTER, COUNT
*		SO THAT "SEARCHFCBS" MAY BE CALLED REPEATEDLY
*		UNTIL IT FAILS
*
SEARCHFCBINIT	EQU	*
	LDAA	NFCBS	GET # FCBS TO SEARCH
	STAA	COUNT
	LDX	CODE+SDOS:CONFIGURATION	GET ADDRESS OF FIRST IOCB
	LDX	[CNFG:IOCBPOINTERS,X]
	STX	FCBPOINTER
	RTS
*
*	SEARCHFCBS -- SEARCH FCBS FOR MATCHING SYSIOCB+IOCB:CURLSN AND DIRENTRYDISP
*		OKRTS IF FOUND; DOES NOT BUMP REFCOUNT, SETS FCBPOINTER
*		WILL NOT MATCH AGAINST FCBS WHOSE "DELETED" BIT IS SET
*		ERRET IF NO MATCH
*
SEARCHFCBS	EQU	*
	LDD	FCBPOINTER	FIND NEXT FCB
	SUBD	#FCB:SIZE	= THIS FCB - FCB:SIZE
	STD	FCBPOINTER	REMEMBER FCB ADDRESS
	LDX	FCBPOINTER	IS FCB FREE ?
	LDAA	FCB:REFCOUNT,X
	BEQ	SEARCHFCBCMPNO	B/ YES, CAN'T MATCH
	LDD	SYSIOCB+IOCB:CURLSN+1	(THESE TESTS ORDERED ACCORDING TO PROBABILTY OF FAILURE)
	CMPD	FCB:DIRLSN+1,X	DIRECTORY LSN = FCB:DIRLSN ?
	BNE	SEARCHFCBCMPNO	B/ NOPE
	LDAA	SYSIOCB+IOCB:CURLSN
	CMPA	FCB:DIRLSN,X
	BNE	SEARCHFCBCMPNO	B/ WRONG LSN
	LDD	DIRENTRYDISP	CORRECT DIR ENTRY DISPLACMENT ?
	CMPD	FCB:DIRDISP,X
	BNE	SEARCHFCBCMPNO	B/ WRONG DISPLACEMENT
	LDAA	FCB:FLAGS,X	IS THIS FCB MARK AS DELETED ?
	BITA	#FCBFLG::DELETED	(IF SO, IGNORE IT)
	BNE	SEARCHFCBCMPNO	B/ NO
	LDX	FCB:DISKINFO,X	LAST BUT NOT LEAST,
	CPX	DCBPOINTER	IS FCB FOR FILE ON THE CORRECT DISK ?
	BEQ	SEARCHFCBRTS	B/ NO, WE FOUND A MATCH !
SEARCHFCBCMPNO	; THIS FCB WASN'T IT!
	DEC	COUNT	DOWN COUNT # FCBS LEFT TO EXAMINE
	BNE	SEARCHFCBS	B/ MORE FCBS TO SEARCH
	JSR	ERRET
	FDB	ERR:NOMATCHFCB

SEARCHFCBRTS	EQU	*
	LDX	FCBPOINTER
	LDD	FCBPOINTER	BECAUSE THIS IS USEFUL, SOMETIMES
	OKRTS
	PAGE
*
*	PARSEFILENAME -- PASSED POINTER IN SYSCALL BLOCK TO FILENAME
*		BUILDS FILE NAME INTO "FILENAME" BLOCK
*		COLLECTS SPACE ALLOCATION IF SPECIFIED
*	OKRTS IF LEGAL NAME
*	SETS @SCBLK:RDBUF = # BYTES OF FILENAME ACTUALLY SCANNED
*	RETURNS (X) = POINTER TO FILENAME BLOCK
*	ERRET IF ILLEGAL NAME
*
PARSEFILENAME	EQU	*
	LDX	#0	ZERO OUT 4 BYTE REQUESTED FILE SIZE
	STX	FILESIZERQSTD
	STX	FILESIZERQSTD+2
	JSR	CHECKRDLEN	MAKE SURE THERE'S ENOUGH ROOM TO READ BACK THE FILE NAME SIZE
	FDB	2
	LDB	#CREATE:FILESIZESCLEN	IS INITIAL FILE ALLOCATION INCLUDED IN CREATE BLOCK ?
	JSR	CHECKSCLEN	...?
	BCS	PARSENAME0	B/ NO, USE USER SPECIFIED SIZE OR 0
	LDD	CREATE:FILESIZE,X	YES, GET SIZE FROM 1ST 4 BYTES OF DATA
	STD	FILESIZERQSTD
	LDD	CREATE:FILESIZE+2,X
	STD	FILESIZERQSTD+2
PARSENAME0 ; DEFAULT FILE SIZE SET, NOW PARSE THE NAME ITSELF
	LDX	CODE+SDOS:IOBLOCKPTR
	LDD	SCBLK:WRLEN,X	GET SIZE OF USER-SPECIFIED FILE NAME
	LDX	SCBLK:RDBUF,X	SUBTRACT NUMBER OF CHARACTERS EXTRACTED FOR DEVICE NAME
	SUBD	0,X
	STD	PARSECOUNT	SAVE SIZE OF FILENAME
	BEQD	ERRBADFILENAMEJ	B/ ZERO LENGTH FILE NAME
	PAGE
	LDX	CODE+SDOS:IOBLOCKPTR	GRAB POINTER TO USER-SPECIFIED FILE NAME
	LDD	SCBLK:WRBUF,X
	LDX	SCBLK:RDBUF,X	SKIP PAST DEVICE NAME
	ADDD	0,X
	STD	NAMESCANPTR	SET UP TO SCAN FILE NAME
	LDA	[NAMESCANPTR]	GRAB THE FIRST BYTE OF THE FILE NAME
	CMPA	#'.	A PERIOD ?
	BEQ	ERRBADFILENAMEJ	B/ YES, ILLEGAL FILE NAME
	JSR	ISDIG	NO, A DIGIT ?
	BCC	ERRBADFILENAME	B/ YES, MUST START WITH ALPHA OR '$'!
	LDX	#FILENAMEBUF
	STX	TOPOINTER
	CLR	FILENAMELENGTH	= # CHARACTERS ACTUALLY COMPRISING FILENAME
	LDAB	#DIR:NAMESIZE
PARSENAME1	EQU	*
	JSR	PARSENAMEB
	LDX	TOPOINTER
	STA	,X+
	STX	TOPOINTER	SAVE NEXT PLACE TO STORE BYTE
	DECB
	BNE	PARSENAME1
*	NOW SCAN PAST REST OF NAME
*
PARSESCANL	EQU	*
	JSR	PARSENAMEB
	CMPA	#ASCII:SPACE
	BNE	PARSESCANL	B/ VALID CHARACTER FOR FILENAME, IGNORE IT
	LDAA	FILENAMELENGTH	IS NAME OF REASONABLE LENGTH ?
ERRBADFILENAMEJ
	BEQ	ERRBADFILENAME	B/ NAME IS OF ZERO LENGTH!
	CMPA	#DIR:NAMESIZE	...?
	BHI	ERRBADFNAMESIZE	B/ NO, ITS TOO LONG!
	PAGE
	JSR	PARSENAMEB1
	CMPA	#'(	START OF REQUESTED FILESIZE ?
	BNE	PARSEQUIT	COLLECT # OF BYTES TO ALLOCATE FOR FILE
	LDX	#0	OVERRIDE THE DEFAULT GIVEN IN SYSCALL BLOCK
	STX	FILESIZERQSTD
	STX	FILESIZERQSTD+2
PARSESCAND	EQU	*
	JSR	PARSENAMEB1	LOOKS LIKE DEFAULT FILESIZE
	CMPA	#')	END OF FILESIZE
	BEQ	PARSEQUIT	B/ YES
	JSR	ISDIG	NO, A DIGIT ?
	BCS	ERRILLFILESIZE	NO, OOPS!
	SUBA	#'0	CONVERT TO BINARY
	STAA	FILESIZE2+3	SAVE THE DIGIT
	LDX	FILESIZERQSTD	MULTIPLY OLD FILESIZE BY 10
	STX	TEMP	FIRST COPY FILESIZERQSTD TO TEMP
	LDX	FILESIZERQSTD+2
	STX	TEMP+2
	LDX	#FILESIZERQSTD	COMPUTE...
	BSR	ADDTOFILESIZE	2*FILESIZERQSTD
	BSR	ADDTOFILESIZE	4*FILESIZERQSTD
	LDX	#TEMP
	BSR	ADDTOFILESIZE	5*FILESIZERQSTD
	LDX	#FILESIZERQSTD
	BSR	ADDTOFILESIZE	10*FILESIZERQSTD
	LDX	#FILESIZE2	= (0,0,0,DIGIT)
	BSR	ADDTOFILESIZE	+ DIGIT
	BRA	PARSESCAND

ERRBADFILENAME	EQU	*
	BSR	PARSEQUIT	GO SET LENGTH OF FILENAME INTO REPLY BUFFER
	JSR	ERRET
	FDB	ERR:BADFILENAME

PARSEQUIT	; COMPUTE AND STORE LENGTH OF FILENAME
	LDD	NAMESCANPTR	= NAMESCANPTR - WRBUF FROM SYSCALL BLOCK
	LDX	CODE+SDOS:IOBLOCKPTR
	SUBD	SCBLK:WRBUF,X
	STD	[SCBLK:RDBUF,X]	THIS IS READ-BACK BUFFER ADDRESS
	LDX	#FILENAMEBUF	AS PROMISED
	OKRTS
	PAGE
ERRBADFNAMESIZE	EQU	*
	JSR	PARSEQUIT	SET FILE NAME LENGTH INTO REPLY BUFFER
	JSR	ERRET
	FDB	ERR:BADFNAMESIZE
*
*	ADDTOFILESIZE -- ADDS 4 BYTE NUMBER(X) TO FILESIZERQSTD
*	IF SUM >= 2^31, POPS RETURN OFF STACK AND EXITS TO ERRILLFILESIZE
*
ADDTOFILESIZE	EQU	*
	LDD	FILESIZERQSTD+2
	ADDD	2,X
	STD	FILESIZERQSTD+2
	LDD	FILESIZERQSTD
	ADCB	1,X
	ADCA	0,X
	BMI	ADDTOFILEOV
	BCS	ADDTOFILEOV
	STD	FILESIZERQSTD
	RTS

ADDTOFILEOV	; ANSWER IS TOO BIG TO BE LEGAL
	LEAS	2,S
ERRILLFILESIZE	EQU	*
	BSR	PARSEQUIT	SET FILE NAME LENGTH INTO REPLY BUFFER
	JSR	ERRET
	FDB	ERR:ILLFILESIZE
	PAGE
*
*	PARSENAMEB -- GET BYTE OF FILE NAME PROPER
*		RETURN ASCII:SPACE IF END OF NAME, OR NOT ., $, A-Z, 0-9
*
PARSENAMEB	EQU	*
	LDX	PARSECOUNT	CHECK FOR NO MORE BYTES IN SOURCE OF FILENAME
	BEQ	PARSENAMEBLANK	B/ NO MORE, USE A BLANK TO PAD
	LDX	NAMESCANPTR	GRAB NEXT BYTE FROM SOURCE STRING
	LDAA	0,X
	CMPA	#'.	VALID CHARACTER FOR FILENAME ?
	BEQ	PARSENAMEBOK	B/ YES
	CMPA	#'$
	BEQ	PARSENAMEBOK	B/ YES
	JSR	ISALPHANUM	CHECK FOR ALPHA-NUMERIC AND FOLD
	BCC	PARSENAMEBOK	B/ IS OK.
PARSENAMEBLANK	EQU	*
	LDAA	#ASCII:SPACE	USE A BLANK TO PAD NAME
	RTS
*
PARSENAMEBOK	EQU	*
	INC	FILENAMELENGTH	SO WE KNOW HOW LONG THE ORIGINAL NAME WAS
PARSENAMEBX	; UPDATE POINTER TO SOURCE STRING
	INX
	STX	NAMESCANPTR
	LDX	PARSECOUNT	DOWN COUNT # CHARS REMAINING IN SOURCE STRING
	DEX
	STX	PARSECOUNT
	RTS
	PAGE
*
*	PARSENAMEB1 -- GET BYTE OF FILENAME
*		RETURN NEXT BYTE OF FILENAME OR BLANK
*
PARSENAMEB1	EQU	*
	LDX	PARSECOUNT	ANY BYTES OF SOURCE STRING UNPROCESSED ?
	BEQ	PARSENAMEBLANK	B/ NO, USE A BLANK INSTEAD
	LDX	NAMESCANPTR	YES, GRAB THE NEXT SOURCE STRING BYTE
	LDAA	0,X
	BRA	PARSENAMEBX	AND EXIT WITH IT
*
ERRNODISKMAP	EQU	*
	JSR	ERRET
	FDB	ERR:NODISKMAP
	PAGE	DISK FILE DRIVERS -- DISK SPACE MANAGEMENT
*	ALLOCATECLUSTERS -- ALLOCATES (A,B) DATA CLUSTERS TO FILE
*		SPECIFIED IN IOCB, STARTING AT IOCB:RDCN
*		(A,B) MUST BE <> 0 ON ENTRY
*		CLUSTER SPECIFIED BY IOCB:RDCN MUST NOT CURRENTLY BE ALLOCATED
*		UPDATES FCB:NCLUSTERS, FCB:HCSIC
*		IOCBPOINTER POINTS TO IOCB WHOSE FILE IS TO BE IMPROVED
*		FCBPOINTER MUST SELECT FCB OF FILE TO BE IMPROVED
*		IF FCB:HCSIC = 0, A HEADER CLUSTER IS ALLOCATED
*		OKRTS TAKEN IF AT LEAST 1 DATA CLUSTER WAS ALLOCATED
*		DESTROYS RDCN!
*		ERRET TAKEN IF NO DISK SPACE AVAILABLE
*		ZEROES ALLOCATED DATA CLUSTERS IF IOCB:EOFFLAG IS RESET
*
ERRALLOC0CLUSTERS
	JSR	ERRET
	FDB	ERR:ALLOC0CLUSTERS

ALLOCATECLUSTERS	EQU	*
	STD	ALLOCATIONCOUNT	SAVE DESIRED ALLOCATION
	BEQD	ERRALLOC0CLUSTERS	B/ ALLOCATE ZERO CLUSTERS!!??
	LDX	DCBPOINTER
	LDAA	DSKINFO:MAPFCB,X	MAKE SURE DISK HAS A DISKMAP
	BEQ	ERRNODISKMAP	B/ NOPE, I GIVE UP!
	LDX	FCBPOINTER	NOW CHECK EXISTENCE OF HEADER
	LDAA	FCB:HCSIC,X	HCSIC = 0 ?
	BNE	ALLOC2	B/ HEADER CLUSTER IS ALLOCATED
	CLR	FCB:NCLUSTERS,X	MARK '0 CLUSTERS'
	CLR	FCB:NCLUSTERS+1,X	IN FILE
	LDX	ALLOCATIONCOUNT	BUMP DESIRED DATA CLUSTER COUNT...
	INX		TO INSURE HEADER CLUSTER...
	STX	ALLOCATIONCOUNT	GETS INCLUDED IN MAP SEARCH
	LDD	#DUMMYLCN	GET 'CHOOSE RANDOM START POINT' CODE AS PREVIOUSLY ALLOCATED LCN
	JSR	SEARCHMAP	GO LOOK FOR DISK SPACE
	LDX	ALLOCATIONCOUNT	REMOVE HEADER CLUSTER...
	DEX		FROM DESIRED ALLOCATION
	STX	ALLOCATIONCOUNT
	LDX	FCBPOINTER	STORE LCN OF HEADER CLUSTER
	STD	FCB:HLCN,X
	LDX	IOCBPOINTER
	CLR	IOCB:DRSN,X	INVENT THE 1ST SECTOR OF HEADER...
	JSR	CONVERTLCNTOLSN	SO WE CAN INITIALIZE IT
	JSR	INVENTSECTOR
	JSR	ALLOCINITHS	INITIALIZE THE 1ST SECTOR OF HEADER CLUSTER
	LDX	FCBPOINTER	BUMP # SECTORS IN HEADER THAT ARE INITZED
	INC	FCB:HCSIC,X	(=1)
	INC	FCB:NCLUSTERS+1,X	MAKE SURE HEADER CLUSTER IS COUNTED (=1)
	LDAA	FCB:FLAGS,X	FORCE FCB CONTENTS BACK TO DIRECTORY, ALSO
	ORAA	#FCBFLG::UPDATEDIR
	STAA	FCB:FLAGS,X
	LDD	FCB:HLCN,X	PUT SELF POINTER INTO 1ST BYTES OF SECTOR
	LDX	RDSIPOINTER
	LDX	RDSI:SECTORBASE,X
	STD	HEADER:LCN,X	NOW 1ST SECTOR OF HEADER CLUSTER IS SET UP
	LDX	IOCBPOINTER	COPY LSN OF 1ST HEADER SECTOR...
	LDAA	IOCB:CURLSN,X	INTO FCB FOR FILE
	PSHA		COPY LSN TO (TOS),(A,B)
	LDD	IOCB:CURLSN+1,X
	LDX	FCBPOINTER	THEN INTO FCB
	STD	FCB:HLSN+1,X
	PULA
	STAA	FCB:HLSN,X
	PAGE
ALLOC2	; HEADER CLUSTER EXISTS, CHECK FOR INITIALIZED
	LDX	IOCBPOINTER
	LDD	IOCB:RDCN,X	FIND OUT WHICH HEADER CLUSTER SECTOR...
	ADDD	ALLOCATIONCOUNT	CONTAINS LAST CLUSTER DESCRIPTOR WE'LL ALLOCATE
	SUBD	#1	= RDCN+ALLOCATIONCOUNT-1
	ASLD		CONVERT # CLUSTERS DESIRED INTO # BYTES REQUIRED TO HOLD CLUSTER NUMBERS
	LDX	DCBPOINTER	MAKE SURE WE DON'T OVERRUN THE HEADER CLUSTER
	CMPD	DSKINFO:NBPC,X	(I.E., # BYTES NEEDED IN HEADER CLUSTER < NBPC )
	BCS	ALLOC2A	B/ WILL FIT IN HEADER CLUSTER
ALLOC2BIG	; ALLOCATION REQUEST WILL RUN OFF END OF HEADER CLUSTER
	LDAB	DSKINFO:NSPC,X	MAKE SURE THIS MANY HEADER SECTORS ARE INITZED
	BRA	ALLOC2B	GO DO IT!
	PAGE
ALLOC2A	EQU	*
	JSR	COMPUTERSN	COMPUTE RELATIVE SECTOR NUMBER
	INCB		(B) NOW CONTAINS # HEADER SECTORS THAT NEED TO BE INITZ'D
ALLOC2B	EQU	*
	LDX	FCBPOINTER	CHECK TO MAKE SURE ALL OF THEM ARE INITZ'D
	CMPB	FCB:HCSIC,X	(I.E., IS HCSIC >= NUMBER NEEDED ?)
	BCS	ALLOC3	B/ ALL NEEDED HEADER SECTORS ARE INITZ'D
	BEQ	ALLOC3	B/ # NEEDED = # INITZED, ALL IS OK
	LDAA	#FCBFLG::UPDATEDIR	*** MUST INITZ HEADER CLUSTER SECTORS ***
	ORAA	FCB:FLAGS,X	MAKE SURE DIRECTORY GETS UPDATED
	STAA	FCB:FLAGS,X	SINCE WE'LL CHANGE FCB:HCSIC
	LDAA	FCB:HCSIC,X	GRAB THE CURRENTLY INITZ'D COUNT
	STAB	FCB:HCSIC,X	STORE THE NEW VALUE
	LDX	IOCBPOINTER	STUFF VALUE INTO IOCB
	STAA	IOCB:HRSN,X	SO WE CAN USE GENHLSNPLUSHRSN
ALLOCINITHC	; NOW INITIALIZE HEADER SECTORS
	JSR	GENHLSNPLUSHRSN	GEN HLSN PLUS RELATIVE HEADER SECTOR NUMBER
	JSR	INVENTSECTOR	DON'T BOTHER READING THE SECTOR FROM THE DISK
	JSR	ALLOCINITHS	GO INITIALIZE THE HEADER SECTOR
	LDX	IOCBPOINTER	RETRIEVE NEXT RELATIVE SECTOR NUMBER TO INITZ
	INC	IOCB:HRSN,X	= LAST RELATIVE SECTOR NUMBER + 1
	LDAA	IOCB:HRSN,X
	LDX	FCBPOINTER	DONE INITIALIZING HEADER SECTORS ?
	CMPA	FCB:HCSIC,X	...?
	BNE	ALLOCINITHC	B/ NO, GO INIT ANOTHER ONE
	PAGE
ALLOC3	; REQUIRED HEADER SECTORS ARE ALL INITIALIZED
	JSR	LOCATERDCN	JUST TO MAKE SURE THAT RDCN SLOT EXISTS IN HEADER
* WE'RE TRYING TO GAURANTEE AT LEAST 1 DATA CLUSTER GETS ALLOCATED TO FILE!
	LDX	IOCBPOINTER	TO MAKE SURE ITS RIGHT
	LDD	IOCB:RDCN,X	FIND CLUSTER # ALLOCATED...
*	JUST PREVIOUS TO THE SLOT WE'RE ABOUT TO FILL
	SUBD	#1	FIND RELATIVE DATA CLUSTER NUMBER OF PREVIOUS DATA CLUSTER
	STD	IOCB:RDCN,X	AND SAVE IT (WE'LL BUMP IT BACK TO ORIGINAL VALUE LATER)
	JSR	LOCATERDCN	GO FIND PREVIOUS LCN
	LDD	HEADER:LCN,X	NOW GET THE PREVIOUSLY ALLOCATED CLUSTER NUMBER
	JSR	SEARCHMAP	LET'S ALLOCATE THE FIRST DATA CLUSTER
	LDX	IOCBPOINTER	SET RDCN BACK TO ENTRY VALUE
	INC	IOCB:RDCN+1,X
	BNE	ALLOCDC2
	INC	IOCB:RDCN,X
	BRA	ALLOCDC2	FOUND A FREE CLUSTER, SKIP INTO LOOP

ALLOCERRED	EQU	*
	RTS

ALLOCDC5	; SEARCHMAP FAILED ON 2ND OR LATER DATA CLUSTER
	CPX	#ERR:NODISKSPACE	OUT OF DISK SPACE ?
	BNE	ALLOCERRED	B/ NO, PROBLEM IS MORE SERIOUS
	JMP	ALLOCDONE	ELSE WE CAN QUIT; WE ALLOCATED A SECTOR
	PAGE
ALLOCDATACLUSTERS	; ALLOCATE MORE DATA CLUSTERS TO FILE
	LDD	ALLOCATIONPLCN	GET PREVIOUSLY ALLOCATED LCN
	JSR	SEARCHMAP	GO FIND ME A PLACE!
	BCS	ALLOCDC5	NO MORE SECTORS AVAILABLE FOR ALLOCATION
ALLOCDC2	; ENTRY POINT FOR FIRST DATA CLUSTER ALLOCATION
	STD	ALLOCATIONPLCN	SAVE LCN JUST FOUND FOR USE NEXT ROUND (POETIC, HUH?)
	JSR	LOCATERDCN	GET POINTER TO RDCN IN SECTOR
	LDD	ALLOCATIONPLCN	STORE NEWLY ALLOCATED LCN INTO HEADER SECTOR OF FILE
	STD	HEADER:LCN,X
	LDX	IOCBPOINTER	MARK HEADER SECTOR AS MODIFIED
	LDX	IOCB:HRDSI,X	LOCATERDCN MAKES THIS VALID!
	LDAA	#1
	STAA	RDSI:MODIFIED,X
	LDX	IOCBPOINTER	IS THIS ALLOCATION...
	LDAA	IOCB:EOFFLAG,X	OCCURRING IN THE MIDDLE OF THE FILE ?
	BNE	ALLOCDC4	B/ NO, ALLOCATION OCCURRING PAST END OF FILE
	CLR	IOCB:LOCATEDF,X	FORCE RE-LOCATION TO OCCUR AGAIN
	CLR	IOCB:DRSN,X	SELECT SECTOR ZERO OF CLUSTER
	LDD	ALLOCATIONPLCN	GET CLUSTER NUMBER
	JSR	ZEROCLUSTER	GO ZERO THE CLUSTER
ALLOCDC4	; DONE ZEROING CLUSTER
	LDX	FCBPOINTER	BUMP # CLUSTERS ALLOCATED TO FILE
	INC	FCB:NCLUSTERS+1,X
	BNE	ALLOCDC3
	INC	FCB:NCLUSTERS,X
ALLOCDC3	EQU	*
	LDAA	FCB:FLAGS,X	FORCE FCB BACK TO DIRECTORY
	ORAA	#FCBFLG::UPDATEDIR
	STAA	FCB:FLAGS,X
	LDX	ALLOCATIONCOUNT	REMEMBER THAT WE ALLOCATED A CLUSTER
	DEX		BY DECREMENTING NUMBER OF CLUSTERS LEFT TO ALLOCATE
	STX	ALLOCATIONCOUNT	REMEMBER NUMBER OF CLUSTERS LEFT TO ALLOCATE
	BEQ	ALLOCDONE	B/ ALL DONE ALLOCATING
	LDX	IOCBPOINTER	BUMP RELATIVE DATA CLUSTER NUMBER...
	INC	IOCB:RDCN+1,X	SO WE WILL FILL THE NEXT ONE
	BNE	ALLOCDC1	B/ NO CARRY TO PROPOGATE
	INC	IOCB:RDCN,X
ALLOCDC1	EQU	*
	LDD	IOCB:RDCN,X	WILL WE RUN OFF END OF HEADER CLUSTER ?
	ASLD		(I.E., IS RDCN*2 >= NBPC ? )
	LDX	DCBPOINTER
	SUBD	DSKINFO:NBPC,X	(I.E., IS RDCN-NBPC >= 0?)
	BCC	ALLOCDONE	B/ YES, QUIT ALLOCATING NOW
	JSR	LOCATERDCN	LOCATE NEXT LCN FOR DATA CLUSTER
	LDX	HEADER:LCN,X	IS LCN FOR :RDCN UNALLOCATED ?
	INX		...?
	BEQ	ALLOCDATACLUSTJ	B/ YES, GO FILL UNALLOCATED SLOT
ALLOCDONE	; AT LEAST ONE DATA CLUSTER WAS ALLOCATED
	LDD	ALLOCATIONPLCN	UPDATE RANDOM MAP SEARCH START POINT
	LDX	DCBPOINTER	TO LAST ALLOCATED SECTOR...
	STD	DSKINFO:RANDMAP,X	WHICH IS NEAR A REGION OF FREE CLUSTERS
	OKRTS

ALLOCDATACLUSTJ	EQU	*
	JMP	ALLOCDATACLUSTERS
	PAGE
*
*	ALLOCINITHS -- INITIALIZE HEADER CLUSTER SECTOR...
*		WHOSE RDSI IS IN RDSIPOINTER
*		RETURN (X)= IOCBPOINTER
*		MARKS SECTOR AS MODIFIED
*
ALLOCINITHS	EQU	*
	LDX	DCBPOINTER	GET NEGATIVE OF NUMBER OF BYTES PER SECTOR
	CLRA
	CLRB
	SUBD	DSKINFO:NBPS,X
	STAA	TEMPA
	LDX	RDSIPOINTER	TAG SECTOR AS MODIFIED
	LDAA	#1
	STAA	RDSI:MODIFIED,X
	LDX	RDSI:SECTORBASE,X	GET SECTOR BUFFER ADDRESS
	NEGA		GET -1 CODE TO STORE INTO HEADER SECTOR
*
*	STAASECTORL -- COPY (A) INTO SECTOR BUFFER (X)
*		(TEMPA,B) = - # BYTES TO COPY
*
STAASECTORL	; INITZ A SECTOR BYTE
	STA	,X+	FILL SECTOR WITH -1'S
	INCB
	BNE	STAASECTORL	B/ MORE TO FILL
	INC	TEMPA	= UPPER HALF OF COUNTER
	BNE	STAASECTORL	B/ MORE TO FILL
	LDX	IOCBPOINTER	AS PROMISED
	RTS
	PAGE
*
*	ZEROSECTOR -- ZERO OUT THE SECTOR BUFFER OF RDSIPOINTER
*	DOES NOT MARK SECTOR AS MODIFIED
*	(BECAUSE IT MIGHT BE A READ OF A SPARSE SECTOR)
*
ZEROSECTOR	EQU	*
	LDX	DCBPOINTER	GET NEGATIVE OF NUMBER OF BYTES PER SECTOR
	CLRA		= 0 - DSKINFO:NBPS
	CLRB
	SUBD	DSKINFO:NBPS,X
	STAA	TEMPA	SINCE (A) WILL BE BUSY WITH ZERO
	LDX	RDSIPOINTER	MARK SECTOR AS MODIFIED
	LDX	RDSI:SECTORBASE,X	GET SECTOR BUFFER ADDRESS
	CLRA		MAKE A ZERO TO STORE
	BRA	STAASECTORL	AND GO STORE A SECTORFUL
*
*	ZEROCLUSTER -- ZEROS OUT CLUSTER WHOSE LCN IS IN (A,B)
*
ZEROCLUSTER	EQU	*
	JSR	CONVERTLCNTOLSN	AND GO FIND FIRST SECTOR OF CLUSTER !
ZEROCLUSTERL	; ZERO OUT A DATA CLUSTER LOOP
	LDX	IOCBPOINTER	GET SECTOR DESCRIPTOR ADDRESS
	JSR	INVENTSECTOR	INVENT THE DATA SECTOR ADDRESS
	BSR	ZEROSECTOR	GO ZERO IT!
	JSR	MARKMODIFIED	MARK THIS SECTOR AS MODIFIED
	JSR	INCIOCB:CURLSN	FIND NEXT DATA SECTOR ADDRESS
	INC	IOCB:DRSN,X	BUMP RELATIVE SECTOR NUMBER WITHIN CLUSTER
	LDAA	IOCB:DRSN,X	ZEROED ALL THE SECTORS IN THE CLUSTER ?
	LDX	IOCB:DCB,X	...?
	CMPA	DSKINFO:NSPC,X	...?
	BNE	ZEROCLUSTERL	B/ NO, GO ZERO ANOTHER!
	OKRTS
	PAGE
*	FREECLUSTER -- FREE CLUSTER (A,B) (I.E., RESET MAP BIT)
*	ERROR EXIT IF THAT CLUSTER WAS NOT PREVIOUSLY ALLOCATED
*
FREECLUSTER	EQU	*
	JSR	FINDLCNMAPBIT	GO LOCATE THE MAP BIT
	BITA	DISKMAP:BYTE,X	DOUBLE-CHECK: WAS MAP BIT SET ?
	BEQ	FREEERROR	B/ NO, TAKE ERROR EXIT
	COMA		YES, MAKE MASK TO RESET BIT
	ANDA	DISKMAP:BYTE,X	FREE THE CLUSTER
	STAA	DISKMAP:BYTE,X
	JMP	MARKMODIFIED	MAKE SURE MAP SECTOR GOES BACK TO DISK

FREEERROR	EQU	*
	JSR	ERRET	LCN WASN'T MARKED AS ALLOCATED
	FDB	ERR:LCNWASNTALLOCATED
*
ERRNODISKMAPJ1	EQU	*
	JMP	ERRNODISKMAP
*
*	FREECLUSTERS -- FREE (D) DATA CLUSTERS FROM FILE(IOCB)
*		IF (D)=0, DELETES UNTIL END OF HEADER CLUSTER ENCOUNTERED ("CHOP")
*		DELETES STARTING AT IOCB:RDCN (0 IF DELETING FILE)
*		UPDATES IOCB:NCLUSTERS
*		IF FCB:NCLUSTERS DROPS TO 0 (HLCN DEALLOCATED)
*		THEN FCB:HCSIC IS ZEROED
*		IF DELETING FILE, DOESN'T WRITE HEADER BLOCKS BACK TO DISK
*		ERRET TAKEN IF ILLEGAL LCN OR UNALLOCATED LCN SEEN
*
FREECLUSTERS	EQU	*
	STD	ALLOCATIONCOUNT	REMEMBER HOW MANY CLUSTERS TO DELETE
	LDX	DCBPOINTER	MAP FILE EXIST ?
	LDAA	DSKINFO:MAPFCB,X	...?
	BEQ	ERRNODISKMAPJ1	B/ NOPE, CAN'T DEALLOCATE
FREECLUSTERL	EQU	*
	JSR	LOCATERDCN	ALSO CHECK FOR LEGAL RDCN
	BCS	FREECLUSTERE	B/ OFF END OF HEADER CLUSTER!
	LDD	HEADER:LCN,X	GET LCN FROM HEADER SECTOR
	CMPD	#DUMMYLCN	IS LCN UNALLOCATED?
	BEQ	FREECLUSTERN	B/ NEVER WAS ALLOCATED, PROCESS NEXT
	CLR	HEADER:LCN,X	ZAP LCN IN HEADER SECTOR
	DEC	HEADER:LCN,X	(I.E., SET TO DUMMYLCN (=-1))
	CLR	HEADER:LCN+1,X
	DEC	HEADER:LCN+1,X
	LDX	FCBPOINTER	DELETING THE FILE ?
	LDX	FCB:NCLUSTERS,X	(I.E., IS # CLUSTERS TO DELETE...
	CPX	ALLOCATIONCOUNT	= # CLUSTERS REMAINING IN FILE ?)
	BEQ	FREECLUSTER0	B/ DON'T BOTHER MOVING HEADER SECTOR BACK TO DISK
	LDX	IOCBPOINTER	MAKE SURE HEADER SECTOR GOES BACK TO DISK
	LDX	IOCB:HRDSI,X
	CLR	RDSI:MODIFIED,X	MARK RDSI AS MODIFIED
	INC	RDSI:MODIFIED,X
FREECLUSTER0	EQU	*
	JSR	FREECLUSTER	GO FREE THE CLUSTER WE FOUND
	LDX	FCBPOINTER	DOWN COUNT # CLUSTERS ALLOCATED TO FILE
	LDAA	FCB:NCLUSTERS+1,X
	BNE	FREECLUSTER3
	DEC	FCB:NCLUSTERS,X
FREECLUSTER3	EQU	*
	DEC	FCB:NCLUSTERS+1,X
	LDAA	FCB:FLAGS,X	MARK FCB AS REQUIRING...
	ORAA	#FCBFLG::UPDATEDIR	A DIRECTORY UPDATE
	STAA	FCB:FLAGS,X
	LDX	ALLOCATIONCOUNT	DOWN COUNT # CLUSTERS LEFT TO DEALLOCATE
	BEQ	FREECLUSTERN	B/ 0, DELETE TILL END OF HEADER CLUSTER
	DEX
	STX	ALLOCATIONCOUNT
	BEQ	FREECLUSTERDONE	B/ DEALLOCATION COMPLETED
FREECLUSTERN	; PROCESS NEXT CLUSTER
	LDX	IOCBPOINTER	COMPUTE RELATIVE CLUSTER #
	INC	IOCB:RDCN+1,X
	BNE	FREECLUSTER2
	INC	IOCB:RDCN,X
FREECLUSTER2	EQU	*
	BRA	FREECLUSTERL	GO FREE ANOTHER CLUSTER
	PAGE
FREECLUSTERE	; ERROR ENCOUNTERED
	LDD	ALLOCATIONCOUNT	DELETEING TILL END OF HEADER CLUSTER ?
	BNED	FREECLUSTERE1	B/ NO, WE GOOFED SOMEHOW!
	CPX	#ERR:HCSICTOOSMALL	YES, VALID REASON TO QUIT?
	BEQ	FREECLUSTERDONE	B/ YES
	CPX	#ERR:CLUSTERSIZELIMITSFILE	A DIFFERENT, BUT ALSO VALID REASON?
	BEQ	FREECLUSTERDONE	B/ YES
FREECLUSTERE1 ; FATAL ERROR IN FREE CLUSTER
	JMP	ERRORED

FREECLUSTERDONE	EQU	*
	LDX	FCBPOINTER
	IF	M6800
	LDAA	FCB:NCLUSTERS,X	IS NCLUSTERS = 0?
	ORAA	FCB:NCLUSTERS+1,X
	ELSE	(M6801!M6809)
	LDD	FCB:NCLUSTERS,X	IS NCLUSTERS = 0 ?
	FIN
	BNE	FREECLUSTEREXIT	B/ NO, FILE JUST SMALLER
	STAA	FCB:HCSIC,X	YES, ZAP :HCSIC
FREECLUSTEREXIT	EQU	*
	OKRTS
*	SEARCHMAP -- SEARCHES DISK MAP FOR ALLOCATIONCOUNT CONTIGUOUS CLUSTERS
*		STARTS WITH FORWARD SEARCH FROM LCN(A,B)
*		IF LCN(A,B) = :FFFF, CHOOSES RANDOM PLACE IN MAP
*		SEARCHES FOR CONTIGUOUS AREA OF ALLOCATIONCOUNT CLUSTERS
*		USES 1ST FREE CLUSTER FOUND IF ALLOCATION SPACE NOT AVAILABLE
*		QUITS IF FIRST FREE CLUSTER = 1 UNIT FORWARD FROM LCN(A,B)
*		IF FREE GROUP FOUND, FINDS BEGINNING OF GROUP
*		THEN TRIES BACKWARD SEARCH
*		ALSO RECORDS 1ST FREE CLUSTER IF CLOSER THAN FWD SEARCH POINT
*		UPDATES DISKMAP BY SETTING THE CORRESPONDING BIT
*		USES IOCB SELECTED BY IOCBPOINTER; DCB SELECTED BY DCBPOINTER
*	OKRTS: RETURNS ALLOCATED LCN IN (A,B); UPDATES MAP
*	ERRET: NO DISK BLOCK AVAILABLE
*
SEARCHMAP	EQU	*
	STD	DESIREDLCN	SAVE # OF (DESIRED LCN -1)
	LDX	#DUMMYLCN	SET BEST DISCOVERED LCN TO "DUMMY"
	STX	AVAILABLELCN
	CPX	DESIREDLCN	IS (A,B) = DUMMYLCN ?
	BNE	SEARCHMAP1	(UNALLOCATED LCN AS START POINT?)
	LDX	DCBPOINTER	YES, CHOOSE "RANDOM" POINT IN MAP
	LDD	DSKINFO:RANDMAP,X	= LAST PLACE WE FOUND SOME FREE SPACE
SEARCHMAP1	EQU	*
	STD	SEARCHLCN	REMEMBER SEARCH STARTING POINT
SEARCHM5	EQU	*
	LDD	SEARCHLCN	GET DESIRED START POINT
	JSR	FINDLCNMAPBIT
SEARCHM2	EQU	*
	BITA	DISKMAP:BYTE,X	ARE WE IN MIDDLE OF GROUP OF FREE CLUSTERS?
	BNE	SEARCHMF	B/ NO, START FORWARD SEARCH
	LDAB	SEARCHLCN+1	MUST SCAN BACKWARDS TO BEGINNING OF GROUP
	BNE	SEARCHM3	BACK UP LCN WE'RE LOOKING AT
	DEC	SEARCHLCN	DON'T YOU HATE DBL PRECISION DECREMENTS?
SEARCHM3	EQU	*
	DEC	SEARCHLCN+1
	LSRA		MOVE BIT MASK DOWN
	BCC	SEARCHM2	B/ NOT ALL BITS IN DISKMAP BYTE PROCESSED
SEARCHM4	; ASSERT: SEARCHLCN MOD 8 = 7 HERE
	DEX		BACK UP THE BYTE POINTER
	CPX	DISKMAPSECTORSTART	WILL WE RUN OFF BOTTOM OF MAP SECTOR?
	BEQ	SEARCHM5	B/ YEP, TIME TO READ ANOTHER MAP SECTOR
	LDAA	#$80	BE OPTIMISTIC, GET NEW MASK
	LDAB	DISKMAP:BYTE,X	DO QUICK CHECK ON MAP BYTE
	BNE	SEARCHM2	B/ WE'LL FIND AN ALLOCATED CLUSTER
	LDD	SEARCHLCN	SKIP UNALLOCATED CLUSTERS
*	*** ASSUMES LCN 0 ALWAYS ALLOCATED!
	SUBD	#NMBPB	NUMBER OF MAP BITS PER BYTE
	STD	SEARCHLCN	UPDATE NEXT LCN TO LOOK AT
	JMP	SEARCHM4	A SPEEDUP IS SPOSD TO BE FAST!

SEARCHMFQJ1	EQU	*
	JMP	SEARCHMFQ
	PAGE
SEARCHMFE	; EXHAUSTED MAP BYTE WHILE LOOKING FOR FREE LCN
	LDAA	#1	GET MASK FOR NEXT LCN TO CHECK
	INX		BUMP MAP BYTE POINTER
	CPX	DISKMAPSECTOREND	RUN OFF END OF MAP SECTOR?
	BEQ	SEARCHMFE1	B/ YES
SEARCHMFE2	EQU	*
	LDAB	DISKMAP:BYTE,X	NO, CHECK WHOLE MAP BYTE...
	INCB		TO SEE IF ALL ENTRIES IN IT ARE ALLOCATED
	BNE	SEARCHMF1A	B/ NOPE, SEARCH MAP BYTE THE HARD WAY
	LDAB	SEARCHLCN+1	ENTIRE BYTE IS ALLOCATED
	ADDB	#NMBPB	SKIP SEARCHLCN FORWARD THAT MANY LCN'S
	STAB	SEARCHLCN+1
	BNE	SEARCHMFE	AND GO CHECK NEXT MAP BYTE
	INC	SEARCHLCN	(PROPOGATE CARRY TO UPPER HALF)
	BRA	SEARCHMFE

SEARCHMFE1	; RAN OFF END OF MAP SECTOR LOOKING FOR FREE LCN
	LDD	SEARCHLCN	GO FIND THE MAP SECTOR...
	JSR	FINDLCNMAPBIT	WHICH CONTAINS THE BIT WE WANT
	BCS	SEARCHMFQJ1	OOPS, RAN OFF END OF MAP
	LDX	IOCBPOINTER	INITIATE READ-AHEAD OF NEXT MAP SECTOR
	JSR	READAHEAD
	LDX	DISKMAPBYTEPOINTER	GET POINTER TO MAP BYTE
	LDAA	DISKMAPMASK	AND BIT MASK FOR MAP BIT
	BRA	SEARCHMFE2	GO CHECK BYTE CONTENTS

SEARCHMFQXITJ1	EQU	*
	JMP	SEARCHMFQXIT
	PAGE
SEARCHMF	; NOW START FORWARD MAP SEARCH
	LDAB	SEARCHLCN	REMEMBER DIVIDING POINT IN MAP...
	STAB	DIVIDEPOINTLCN	SO WE CAN START BACKWARDS SEARCH LATER
	LDAB	SEARCHLCN+1
	STAB	DIVIDEPOINTLCN+1
SEARCHMF0	EQU	*
	INC	SEARCHLCN+1	BUMP LCN WE'RE ABOUT TO LOOK AT
	BNE	SEARCHMF1
	INC	SEARCHLCN
SEARCHMF1	; LOOK AT NEXT BIT IN BIT MAP
	ASLA		SWITCH BIT MASK'S ATTN TO PROPER BIT
	BCS	SEARCHMFE	B/ EXHAUSTED BYTE
SEARCHMF1A	EQU	*
	BITA	DISKMAP:BYTE,X	IS THIS LCN ALSO ALLOCATED?
	BNE	SEARCHMF0	B/ YES, SKIP OVER IT
	STX	DISKMAPBYTEPOINTER	SAVE MAP BYTE ADDRESS
	LDX	AVAILABLELCN	FOUND A FREE CLUSTER!
	INX		SEE IF ANOTHER HAS ALREADY BEEN FOUND
	BNE	SEARCHMF4	B/ YES, DON'T RECORD THIS ONE
	LDX	SEARCHLCN	NO, SAVE LCN IN CASE...
	STX	AVAILABLELCN	WE CAN'T FIND CONTIGUOUS ALLOCATION
	LDX	#1	SET GROUPSIZE TO 1
	STX	AVAILABLECOUNT
	LDAB	SEARCHLCN+1	IS JUST-FOUND CLUSTER...
	SEC		= DESIRED CLUSTER + 1 ?
	SBCB	DESIREDLCN+1	(I.E., IS SEARCHLCN-DESIREDLCN=1?)
	BNE	SEARCHMF4	B/ NO
	LDAB	SEARCHLCN	(I.E. IS SEARCHLCN-DESIREDLCN-1=0?)
	SBCB	DESIREDLCN	...?
	BEQ	SEARCHMFQXITJ1	B/ YES, TAKE QUICK EXIT
	PAGE
SEARCHMF4	EQU	*
	LDX	DISKMAPBYTEPOINTER	GET ADDRESS OF MAP BYTE CONTAINING FREE CLUSTER
	CLR	LCNGROUPLENGTH	FIND OUT HOW LONG...
	CLR	LCNGROUPLENGTH+1	THE GROUP OF FREE LCN'S IS
SEARCHMF2	EQU	*
	INC	LCNGROUPLENGTH+1	BUMP LENGTH OF FREE GROUP OF LCNS
	BNE	SEARCHMF3
	INC	LCNGROUPLENGTH
SEARCHMF3	; LOOK AT NEXT BIT
	LDAB	LCNGROUPLENGTH	IS SIZE OF THIS FREE GROUP...
	CMPB	ALLOCATIONCOUNT	>= TO ALLOCATION COUNT?
	BCS	SEARCHMF5	B/ NO (ELSE MUST BE EQUAL!)
	LDAB	LCNGROUPLENGTH+1	= COMPARE IS SUFFICIENT, SINCE...
	CMPB	ALLOCATIONCOUNT+1	LCN GROUPLENGTH IS MONOTONICALLY INCREASING
	BEQ	SEARCHMFG	AND WAS < ALLOCATION COUNT LAST TIME WE CHECKED
SEARCHMF5	EQU	*
	INC	SEARCHLCN+1	BUMP LCN TO LOOK AT
	BNE	SEARCHMF6	B/ EXHAUSTED MAP BYTE
	INC	SEARCHLCN	BUMP LCN WE'LL BE LOOKING AT
SEARCHMF6	EQU	*
	ASLA		SHIFT BIT MASK OVER
	BCS	SEARCHMF8	B/ EXHAUSTED MAP BYTE
SEARCHMF7	EQU	*
	BITA	DISKMAP:BYTE,X	LOOK AT NEXT LCN BIT
	BEQ	SEARCHMF2	B/ ITS FREE, COUNT IT
	BRA	SEARCHMF0	FREE GROUP NOT LONG ENOUGH TO USE
	PAGE
SEARCHMF8	; EXHAUSTED MAP BYTE WHILE SCANNING FREE GROUP OF LCN'S
	LDAA	#1	GET MASK FOR MAP BIT IN NEXT BYTE
	INX		BUMP MAP BYTE POITER
	CPX	DISKMAPSECTOREND	EXHAUST MAP SECTOR?
	BNE	SEARCHMF7	B/ NO, KEEP SEARCHING
	LDD	SEARCHLCN	DISK MAP SECTOR EXHAUSTED, GET NEXT LCN TO CHECK
	JSR	FINDLCNMAPBIT	SET UP TO SEARCH NEXT MAP SECTOR
	BCS	SEARCHMFQ	QUIT IF WE RUN OFF END OF MAP
	LDX	IOCBPOINTER	INITIATE PRE-FETCH...
	JSR	READAHEAD	OF NEXT MAP SECTOR
	LDX	DISKMAPBYTEPOINTER	GET POINTER TO MAP BYTE...
	LDAA	DISKMAPMASK	AND BIT MASK
	BRA	SEARCHMF7	GO CHECK BIT TO SEE IF FREE

SEARCHMFG	; FORWARD SEARCH FOUND ALLOCATION COUNT FREE CLUSTERS
	LDD	LCNGROUPLENGTH	RECORD SIZE OF FREE GROUP OF LCN'S
	STD	AVAILABLECOUNT
	COMD		COMPUTE LCN OF FIRST LCN IN THIS FREE GROUP
	ADDD	SEARCHLCN	= LCN OF CLUSTER TERMINATING GROUP
	ADDD	#2	- SIZE OF GROUP +1 = COMPLEMENT(GROUPLENGTH) + 2
	STD	AVAILABLELCN	REMEMBER WHERE THE GROUP STARTS
SEARCHMFQ	; QUIT FORWARD SEARCH
	LDD	DIVIDEPOINTLCN	START BACKWARDS SEARCH...
	STD	SEARCHLCN	AT DIVIDING POINT IN MAP
	JSR	FINDLCNMAPBIT
	PAGE
	JSR	READBEHIND	INITIATE THE READ BEHIND
	LDX	DISKMAPBYTEPOINTER	GET MAP BYTE POINTER AND MASK
	LDAA	DISKMAPMASK
SEARCHMR	EQU	*
	BITA	DISKMAP:BYTE,X	SCAN MAP BACKWARDS FOR FREE LCN
	BEQ	SEARCHMR1	B/ FOUND FREE LCN
SEARCHMR0	EQU	*
	LDAB	SEARCHLCN+1	THIS LCN NOT FREE
	BNE	SEARCHMR0A	COMPUTE PREVIOUS LCN
	DEC	SEARCHLCN
SEARCHMR0A	EQU	*
	DEC	SEARCHLCN+1
	LSRA		ADJUST BIT MASK FOR PREVIOUS LCN
	BCC	SEARCHMR	B/ MORE BITS TO CHECK IN MAP
SEARCHMRE	EQU	*
	DEX		PROCESS PREVIOUS MAP BYTE
	CPX	DISKMAPSECTORSTART	HIT BOTTOM OF MAP SECTOR?
	BEQ	SEARCHMRE1	B/ YES, MUST PROCESS PRECEDING SECTOR
SEARCHMRE2	EQU	*
	LDAA	#$80	SET UP MASK FOR NEXT MAP BYTE
	LDAB	DISKMAP:BYTE,X	TAKE QUICK LOOK AT ENTIRE BYTE
	INCB		ALL LCN'S IN BYTE ALLOCATED?
	BNE	SEARCHMR	B/ NO, LET'S GO FIND UNALLOCATED LCN
	LDAB	SEARCHLCN+1	YES, BACK UP OVER BYTE OF LCN'S
	CMPB	#NMBPB-1	NEED TO PROPOGATE A BORROW ?
	BNE	SEARCHMRE3	ADJUST SEARCHLCN
	DEC	SEARCHLCN
SEARCHMRE3	EQU	*
	SUBB	#NMBPB
	STAB	SEARCHLCN+1
	JMP	SEARCHMRE	CHECK NEXT MAP BYTE OUT
	PAGE
SEARCHMRE1	; RAN OFF BOTTOM OF MAP SECTOR
	LDD	SEARCHLCN	GO FIND MAP SECTOR CONTAINING PREVIOUS BYTE
	JSR	FINDLCNMAPBIT
	BCS	SEARCHMREBJ1	B/ RAN OFF BOTTOM OF MAP
	JSR	READBEHIND	KEEP THE READ BEHIND RUNNING
	LDX	DISKMAPBYTEPOINTER	GET MAP BYTE POITER...
	LDAA	DISKMAPMASK	AND MASK
	BRA	SEARCHMRE2

SEARCHMREBJ1	EQU	*
	JMP	SEARCHMREB
	PAGE
SEARCHMR1	; FOUND FREE LCN DURING BACKWARDS SEARCH
	STX	DISKMAPBYTEPOINTER	SAVE ADDRESS OF MAP BYTE CONTAINING FREE CLUSTER
	LDX	AVAILABLELCN	EFFECTIVELY THE SAME CODE AS SEARCHMF
	INX		HAVE WE ALREADY FOUND A FREE CLUSTER ?
	BEQ	SEARCHMR4C	B/ NO, RETAIN JUST-FOUND LCN
	LDX	AVAILABLECOUNT	FOUND FREE LCN, AVAILABLE LCN IS TAKEN
	INX		IS IT TAKEN BY A FIRST FREE LCN FOUND ?
	BNE	SEARCHMR4	B/ AVAILABLELCN TAKEN BY ALLOCATIONCOUNT GROUP
*			(WHICH MEANS A SINGLE FREE LCN DOESN'T OVERRIDE IT)
	PSHA		DON'T LOSE MAP MASK!
	JSR	SEARCHOOSE	DECIDE WHICH LCN IS CLOSEST
	PULA		GET MAP MASK BACK
	BCS	SEARCHMR4	B/ AVAILABLELCN WAS CLOSEST
SEARCHMR4C	EQU	*
	LDX	SEARCHLCN	RETAIN JUST-FOUND CLUSTER
	STX	AVAILABLELCN
	LDX	#1	SET GROUP SIZE TO "1"
	STX	AVAILABLECOUNT
SEARCHMR4	EQU	*
	LDX	#0	FIND OUT HOW LONG THIS GROUP OF FREE CLUSTERS IS
	STX	LCNGROUPLENGTH
	LDX	DISKMAPBYTEPOINTER	GET POINTER TO MAP BYTE BACK
SEARCHMR2	EQU	*
	INC	LCNGROUPLENGTH+1
	BNE	SEARCHMR3
	INC	LCNGROUPLENGTH
SEARCHMR3	EQU	*
	LDAB	LCNGROUPLENGTH
	CMPB	ALLOCATIONCOUNT
	BCS	SEARCHMR5
	BHI	SEARCHMRG
	LDAB	LCNGROUPLENGTH+1
	CMPB	ALLOCATIONCOUNT+1
	BEQ	SEARCHMRG	*** NOTE: ASSUMES LCN 0 IS ALWAYS ALLOCATED ***
SEARCHMR5	EQU	*
	LDAB	SEARCHLCN+1	DECREMENT POSITION IN MAP
	BNE	SEARCHMR6	B/ DON'T HAVE TO DECREMENT UPPER HALF
	DEC	SEARCHLCN
SEARCHMR6	EQU	*
	DEC	SEARCHLCN+1
	LSRA
	BCS	SEARCHMR8
SEARCHMR7	EQU	*
	BITA	DISKMAP:BYTE,X
	BEQ	SEARCHMR2
	JMP	SEARCHMR0
	PAGE
SEARCHMR8	; RAN OFF END OF BYTE
	LDAA	#$80	PICK UP MASK FOR NEXT BYTE
	DEX		BACK UP POINTER
	CPX	DISKMAPSECTORSTART	HIT BEGIN SECTOR ?
	BNE	SEARCHMR7	B/ NO, GO PROCESS BYTE
	LDD	SEARCHLCN	YES, MUST PROCESS PREVIOUS SECTOR
	JSR	FINDLCNMAPBIT	GO FIND IT
	BCS	SEARCHMREB	OOPS...
	LDX	IOCBPOINTER	INITIATE READ-BEHIND
	JSR	READBEHIND
	LDX	DISKMAPBYTEPOINTER	RESTORE BIT MASK
	LDAA	DISKMAPMASK	AND MAP BYTE ADDRESS
	BRA	SEARCHMR7	GO PROCESS NEXT BYTE

SEARCHMRG	; FOUND ALLOCATION COUNT FREE CLUSTERS
	LDX	AVAILABLELCN	ON BACKWARDS SEARCH
	INX		FIRST DECIDE IF ANY ALLOCATION FOUND YET
	BEQ	SEARCHUS	B/ NO DISK SPACE FOUND PREVIOUSLY
	LDX	AVAILABLECOUNT	IF AVAILABLE COUNT = ALLOCATION COUNT,
	CPX	ALLOCATIONCOUNT	THEN CHOOSE CLOSEST FREE GROUP
	BNE	SEARCHUS	ELSE USE JUST FOUND FREE GROUP
	BSR	SEARCHOOSE	CHOOSE CLOSER OF (SEARCHLCN,AVAILABLELCN)
	BCC	SEARCHFLCN	AVAILABLELCN WAS CLOSER
SEARCHUS	; USE SEARCHLCN AS RESULT
	LDD	SEARCHLCN	COPY TO AVAILABLELCN
	STD	AVAILABLELCN
SEARCHFLCN	; FOUND AVAILABLE LCN IN MAP
	LDD	AVAILABLELCN	LOCATE IT AGAIN SINCE IT IS PROBABLY NOT...
	JSR	FINDLCNMAPBIT	IN CURRENT MAP BLOCK
SEARCHMFQXIT	; QUICK EXIT
	LDX	DISKMAPBYTEPOINTER	GET POINTER TO MAP BYTE BACK
	ORAA	DISKMAP:BYTE,X	SET BIT IN MAP...
	STAA	DISKMAP:BYTE,X	MARKING SECTOR AS ALLOCATED
	LDX	IOCBPOINTER	TAG SECTOR AS MODIFIED
	LDX	IOCB:DRDSI,X	GET RDSI OF THIS MAP SECTOR
	STAA	RDSI:MODIFIED,X	SINCE READAHEAD BOMBED RDSIPOINTER (A<>0)
	LDD	AVAILABLELCN	AND RETURN THE LCN ALLOCATED
	OKRTS

SEARCHMREB	; BOTTOM OF MAP HIT
	CPX	#ERR:ILLEGALLCN	DOUBLE CHECK TO MAKE SURE
	BNE	SEARCHMERRED3	B/ WASN'T BOTTOM OF MAP!!?
	LDX	AVAILABLELCN	ANY AVAILABLE LCN FOUND ?
	INX		...?
	BNE	SEARCHFLCN	B/ YES, TAKE SUCCESS EXIT
	JSR	ERRET	NO, NO DISK SPACE AVAILABLE
	FDB	ERR:NODISKSPACE

SEARCHMERRED3	EQU	*
	JMP	ERRORED
	PAGE
*
*	SEARCHOOSE -- DECIDE WHETHER AVAILABLELCN OR SEARCHLCN...
*		IS CLOSEST TO DIVIDEPOINTLCN
*		RETURN CARRY RESET IF AVAILABLELCN IS THE ONE TO KEEP
*
SEARCHOOSE	EQU	*
	LDD	DIVIDEPOINTLCN	IS ABS(DIVIDEPOINTLCN-AVAILABLELCN)
	SUBD	AVAILABLELCN	> ABS(DIVIDEPOINTLCN-SEARCHLCN)?
	BCC	SEARCHOOSE1
	NEGD		(TAKE -(A,B) TO GET ABS VALUE)
SEARCHOOSE1	EQU	*
	STD	TEMPX	SAVE ABS(DIVIDEPOINTLCN-AVAILABLELCN)
	LDD	DIVIDEPOINTLCN	(COMPUTE ABS(DIVIDEPOINTLCN-SEARCHLCN))
	SUBD	SEARCHLCN
	BCC	SEARCHOOSE2	B/ ITS ALREADY NEGATIVE
	NEGD		COMPUTE ABS(A,B)
SEARCHOOSE2	EQU	*
	SUBD	TEMPX	= ABS(DIVIDEPOINTLCN-AVAILABLELCN)...
	RTS		-ABS(DIVIDEPOINTLCN-SEARCHLCN)

FINDLCNERREXIT	EQU	*
	JSR	ERRET	ILLEGAL LCN
	FDB	ERR:ILLEGALLCN
	PAGE
*	FINDLCNMAPBIT -- TAKES (D) AS LCN IN DISKMAP
*		RETURNS VIA ERRET IF ILLEGAL LCN
*		COMPUTES DESIRED LSN OF MAP, READS IT IN
*		RETURNS BYTE POINTER IN (X) AND BIT MASK IN (A) FOR LCN
*		ASSUMES IOCBPOINTER SET UP TO PROCESS MAP CLUSTER
*		SETS UP DISKMAPSECTORSTART TO BEGINNING OF BUFFER-1
*		SETS UP DISKMAPSECTOREND TO END OF BUFFER
*		RETURNS RDSIPOINTER, IOCB:RDSI POINTING TO MAP SECTOR
*
FINDLCNMAPBIT	EQU	*
	LDX	DCBPOINTER	CHECK LCN(A,B)...
	CMPD	DSKINFO:NLCN,X	TO MAKE SURE ITS LEGAL
	BCC	FINDLCNERREXIT	B/ ILLEGAL LCN
	STAB	DISKMAPMASK	SAVE BIT NUMBER DESIRED
	LSRD		SHIFT OFF BIT NUMBER...
	LSRD		TO GET BYTE ADDRESS WITHIN DISK MAP
	LSRD
	STD	DISKMAPBYTEPOINTER	SAVE BYTE NUMBER DESIRED
	JSR	COMPUTERSN	COMPUTE RELATIVE SECTOR NUMBER
	STAB	IOCB:DRSN,X	SAVE RELATIVE SECTOR # WITHIN MAP CLUSTER
	LDX	DCBPOINTER	COMPUTE LSN OF DESIRED MAP FILE SECTOR
	ADDB	DSKINFO:MAPLSN+2,X	= LSN(MAP CLUSTER)+RSN
	PSHB		(THIS IS VALID BECUZ ALLOC CHECKS ON ENTRY)
	LDD	DSKINFO:MAPLSN,X
	ADCB	#0	PROPOGATE ANY CARRY
	ADCA	#0
	LDX	IOCBPOINTER	STORE DESIRED LSN INTO IOCB
	STD	IOCB:CURLSN,X
	PULA
	STAA	IOCB:CURLSN+2,X
	JSR	READSECTOR	AND GO GET MAP SECTOR
	LDX	IOCBPOINTER	REMEMBER RDSI FOR MAP SECTOR
	STD	IOCB:DRDSI,X	SINCE READAHEAD WILL DESTROY RDSIPOINTER
	LDD	DISKMAPBYTEPOINTER	GET BYTE NUMBER DESIRED BACK
	LDX	DCBPOINTER	NOW MASK BYTE NUMBER...
	ANDA	DSKINFO:NBPSM1,X	TO GET DESIRED BYTE DISPLACMENT INTO MAP SECTOR
	ANDB	DSKINFO:NBPSM1+1,X
	LDX	RDSIPOINTER	ADD SECTOR BUFFER BASE...
	ADDD	RDSI:SECTORBASE,X	TO GET REAL POINTER TO MAP BYTE
	STD	DISKMAPBYTEPOINTER
	LDX	RDSI:SECTORBASE,X	COMPUTE START AND END OF
	DEX		AS A CONVENIENCE TO SEARCHMAP
	STX	DISKMAPSECTORSTART	DISK BUFFERS FOR THIS BLOCK
	LDX	DCBPOINTER
	LDD	DSKINFO:NBPS,X
	SEC		TO OFFSET THE "DEX"
	ADCB	DISKMAPSECTORSTART+1
	ADCA	DISKMAPSECTORSTART
	STD	DISKMAPSECTOREND
	LDAB	DISKMAPMASK	GET DESIRED BIT # BACK
	ANDB	#NMBPB-1	TAKE MODULO # BITS PER MAP BYTE
	CLRA		CONVERT INTO A BIT MASK
	SEC
FINDLCNMAPBITL	EQU	*
	ROLA		BY SHIFTING
	DECB
	BPL	FINDLCNMAPBITL
	STAA	DISKMAPMASK
	JSR	WAITRDSI	WAIT FOR MAP SECTOR TO ARRIVE
	LDAA	DISKMAPMASK	GET BIT MASK FOR DISKMAP.SYS BYTE
	LDX	DISKMAPBYTEPOINTER	GET POINTER TO MAP BYTE
	OKRTS
	PAGE	DISK FILE DRIVERS -- SUBROUTINES
*	LOCATECURBYTE -- PROCESSES IOCB:CURBYTE
*	TO SET UP VALUE FOR IOCB:CURLSN
*	ALSO SETS UP IOCB:FILESIZE, IOCB:BYTECOUNT, IOCB:CURLCN, IOCB:RSN
*	ASSUMES THAT IOCB:CURLCN IS VALID
*	ZEROS IOCB:DRDSI UNLESS BYTE IS IN LSN SELECTED BY RDSI
*	ASSUMES THAT IOCBPOINTER, DCBPOINTER ARE SET UP
*	(A,B) CONTAIN # BYTES REQUESTED FOR TRANSFER ON ENTRY
*	RETURNS VIA OKRTS:
*		IOCB:CURLCN<>-1 --> LOCATED BYTE
*		IOCB:CURLCN=-1  --> CLUSTER FOR BYTE NOT ALLOCATED
*		USEDCOUNT = # BYTES AVAILABLE TO BE MOVED
*		( 0 < USEDCOUNT <= REQUESTED )
*		USEDCOUNT MUST BE ADJUSTED IF ALL THE BYTES ARE NOT USED
*		(X) = IOCBPOINTER
*	RETURN VIA ERRET:
*		DISK SPACE EXHAUSTED ON WRITE ATTEMPT
*	LOCATECURBYTE ENSURES DESIRED SECTOR IS BROUGHT INTO MEMORY
*	IF WRITEFLAG=0 (READ MODE) AND NON-EXISTENT DATA CLUSTER IS ENCOUNTERED,
*	AN IMAGINARY SECTOR IS ZEROED AND USED
*	NOBODY MAY MODIFY THIS IMAGINARY SECTOR!!!
*	IF WRITEFLAG<>0, AND LOCATE IS DIRECTED TO
*	BYTE BEYOND CURRENT LENGTH OF FILE, THEN
*	A ZEROED SECTOR IS INVENTED

LOCATECURBYTE	EQU	*
	STD	USEDCOUNT	STORE # BYTES REQUESTED FOR THIS TRANSFER
	LDX	IOCBPOINTER	LOCATE ALREADY DONE ?
	LDAA	IOCB:LOCATEDF,X	...?
	BEQ	LOCATECURBYTE0	B/ NO, GO DO IT IN ALL OF ITS GLORY
	LDX	IOCB:BYTECOUNT,X	ANY BYTES REMAINING TO BE PROCESSED ?
	BEQ	LOCATECURBYTEE	B/ NO, GO FIGURE OUT WHY
	LDX	IOCBPOINTER	YES, IS THE DATA SECTOR STILL HERE ?
	LDX	IOCB:DRDSI,X	... ?
	BEQ	LOCATECURBYTEE3	B/ NO, GO FIGURE OUT STUFF THE HARD WAY!
	JSR	REQUERDSI	MAKE SURE THIS SECTOR DOESN'T GET KICKED OUT SOON
	LDAA	WRITEFLAG	DATA SECTOR IS STILL AROUND, IS THIS A WRITE ?
	BNE	LOCATECURBYTEWJ	B/ YES, GO PROCESS
	JMP	LOCATECURBYTED	B/ READ MODE, GO SET UP XFER COUNT

LOCATECURBYTEWJ	EQU	*
	JMP	LOCATECURBYTEW

LOCATECURBYTE9J	EQU	*
	JMP	LOCATECURBYTE9

LOCATECURBYTEE	; EXHAUSTED BYTECOUNT
	LDX	IOCBPOINTER	LOADING :BYTECOUNT INTO (X) DESTROYED THIS...
	LDAA	WRITEFLAG	IS THIS A WRITE ?
	BNE	LOCATECURBYTEE2	B/ YES
	LDAA	IOCB:EOFFLAG,X	NO, ARE WE AT EOF ?
	BNE	LOCATECURBYTE9J	B/ YES, GO COMPLAIN!
LOCATECURBYTEE2	EQU	*
	LDD	IOCB:CURBYTE+2,X	HMMM... ARE WE ON A SECTOR BOUNDARY ?
	LDX	IOCB:DCB,X
	BITB	DSKINFO:NBPSM1+1,X
	BNE	LOCATECURBYTEGJ	B/ NO, GO RE-READ THE SECTOR
	BITA	DSKINFO:NBPSM1,X
	BNE	LOCATECURBYTEGJ	B/ NO, GO RE-READ THE SECTOR
	LDX	IOCBPOINTER	MUST BE AT END OF SECTOR
	INC	IOCB:DRSN,X	FIGURE OUT DRSN OF NEXT SECTOR IN CLUSTER
	LDAA	IOCB:DRSN,X	GET NEXT SECTOR NUMBER WITHIN CLUSTER
	LDX	IOCB:DCB,X	CLUSTER EXHAUSTED ?
	CMPA	DSKINFO:NSPC,X	...?
	BEQ	LOCATECURBYTEE4	B/ YES
	LDX	IOCBPOINTER	NO, IS THIS CLUSTER ALLOCATED ?
	LDX	IOCB:CURLCN,X
	INX		... ?
	BEQ	LOCATECURBYTE4	B/ NO, GO DECIDE WHAT TO DO
	JSR	INCIOCB:CURLSN	SELECT NEXT SECTOR NUMBER
	JMP	LOCATECURBYTE8	GO ISSUE THE READ TO GET THAT DATA SECTOR
	PAGE
LOCATECURBYTEE4	; CLUSTER IS EXHAUSTED
	LDX	IOCBPOINTER	YES, CLUSTER IS EXHAUSTED
	CLR	IOCB:RBN,X	SET RELATIVE BYTE # WITHIN CLUSTER...
	CLR	IOCB:RBN+1,X	TO ZERO
	INC	IOCB:RDCN+1,X	BUMP TO NEXT DATA CLUSTER NUMBER
	BNE	LOCATECURBYTEE1
	INC	IOCB:RDCN,X
LOCATECURBYTEE1	EQU	*
	JMP	LOCATECURBYTE3	GO LOCATE LCN FOR DATA CLUSTER

LOCATECURBYTEE3	; DATA SECTOR DISAPPEARED!
	LDX	IOCBPOINTER	BECAUSE X IS TRASHED
	LDX	IOCB:CURLCN,X	IS CURRENT CLUSTER ALLOCATED ?
	INX		... ?
	BEQ	LOCATECURBYTE4	B/ NO, GO DECIDE WHAT TO DO
LOCATECURBYTEGJ	EQU	*
	JMP	LOCATECURBYTEG	AND GO GET THE SECTOR AGAIN
	PAGE
LOCATECURBYTE0	; START WITH IOCB:CURBYTE AND DO A FULL LOCATE
	JSR	COMPUTERDCN	COMPUTE RELATIVE DATA CLUSTER NUMBER
	CMPD	IOCB:RDCN,X	IN SAME DATA CLUSTER IOCB HAS ALREADY SELECTED?
	BEQ	LOCATECURBYTE5	B/ YES, DON'T NEED TO READ HEADER CLUSTER
*** This is where Segment size stuff should be inserted!
LOCATECURBYTE1	; MUST READ BLOCK OF HEADER CLUSTER (SIGH!)
	STD	IOCB:RDCN,X	SAVE NEW RELATIVE DATA CLUSTER NUMBER
	BRA	LOCATECURBYTE3	GO FIND LCN OF DATA CLUSTER

LOCATECURBYTE4	; NO DATA LCN YET ALLOCATED
	LDAA	WRITEFLAG	IS THIS A WRITE ATTEMPT?
	BNE	LOCATECURBYTE2	B/ WRITE ATTEMPTED
	LDD	#DUMMYLCN	READ ATTEMPT, SET CURLCN:=DUMMY
	LDX	IOCBPOINTER	THIS SEEMS TO BE IMPORTANT
	STD	IOCB:CURLCN,X
	LDX	#0	WATCH ME PULL A RABBIT OUT OF A HAT!
	STX	SYSSECTORDB+SECTORDB:DISKINFO	INVENT A ZERO SECTOR
	STX	SYSSECTORDB+SECTORDB:LSN+1	USE AN IMAGINARY DISK DRIVE
	LDX	#SYSSECTORDB	AND "READ" SECTOR ZERO FROM THAT DRIVE
	CLR	SECTORDB:LSN,X	THIS IMAGINARY SECTOR MATCHES ALL IMAGINARY SECTORS
	JSR	INVENTSECTOR	*** POOF ***
	CLRA		NOW ZERO IT OUT
	CLRB
	SUBD	MAXSECTORSIZE	(GET - # BYTES TO ZAP)
	STAA	TEMPA	FOR STAASECTORL
	LDX	RDSI:SECTORBASE,X	GET SECTOR ADDRESS
	CLRA		MAKE THE ZERO FOR STAASECTORL TO STORE
	JSR	STAASECTORL	AND GO ZERO THE SECTOR OUT
	BRA	LOCATECURBYTE5A	GO SET UP THE WORLD
	PAGE
LOCATECURBYTE3A	; CAN'T LOCATE RDCN
	CPX	#ERR:HCSICTOOSMALL	IS THIS WHY ?
	BEQ	LOCATECURBYTE4	B/ YES, MUST ALLOCATE MORE SPACE
	JMP	ERRORED	NO, GO CAUSE AN ERROR

LOCATECURBYTE2	; WRITE TO UNALLOCATED CLUSTER
	LDX	DCBPOINTER	ALLOCATE CLUSTERS IN MIDDLE OF FILE
	LDD	DSKINFO:MIDALLOC,X	GET # TO ALLOCATE
	JSR	ALLOCATECLUSTERS
	JSR	COMPUTERDCN	ALLOC BOMBED IOCB:RDCN...
	STD	IOCB:RDCN,X	SO WE NEED TO RE-COMPUTE IT
LOCATECURBYTE3	; GET LCN FROM HEADER CLUSTER
	JSR	LOCATERDCN	GO LOCATE THE DATA CLUSTER NUMBER
	BCS	LOCATECURBYTE3A	B/ ERROR OCCURRED
	LDD	HEADER:LCN,X	GRAB DESIRED LCN
	LDX	IOCBPOINTER	AND SAVE IT FOR NEXT ROUND
	STD	IOCB:CURLCN,X
LOCATECURBYTE5	EQU	*
	LDD	IOCB:RBN,X	NOW DIVIDE RELATIVE BYTE # BY SECTOR SIZE
	JSR	COMPUTERSN	TO GET RELATIVE SECTOR #
	STAB	IOCB:DRSN,X
	LDX	IOCB:CURLCN,X	IS DATA LCN ALLOCATED ?
	INX		...= DUMMYLCN ?
	BEQ	LOCATECURBYTE4	B/ DUMMY LCN, GO DECIDE WHAT TO DO
*	THE FOLLOWING IS THE "FAST PATH" FOR HANDLING WRITING AT END OF FILE
	LDAA	WRITEFLAG	ARE WE WRITING ?
	BEQ	LOCATECURBYTE5B	B/ NO, DON'T HAVE TO ZERO CLUSTERS OF SPARSE FILE
	LDX	IOCBPOINTER	AT END OF FILE ?
	LDA	IOCB:EOFFLAG,X	...?
	BEQ	LOCATECURBYTE5B	B/ NO, DON'T HAVE TO ZERO CLUSTERS OF SPARSE FILE
	JSR	CHECKMUSTZERO	DOES SECTOR AT EOF CONTAIN IOCB:CURBYTE ?
	LBCS	ZEROTOCURBYTE	B/ NO, MUST ZERO CLUSTERS AT END OF SPARSE FILE
LOCATECURBYTE5B ; NOW COMPUTE LSN OF DESIRED SECTOR
	LDX	IOCBPOINTER	NOW FIND LSN OF DESIRED SECTOR
	LDD	IOCB:CURLCN,X
	JSR	CONVERTLCNTOLSN
	PAGE
LOCATECURBYTE8	;  DATA CLUSTER CONTAINING CURBYTE EXISTS!
*	SEE IF WE CAN OPTIMIZE THE FETCH
	LDX	IOCBPOINTER	SIGH!
	LDAA	IOCB:EOFFLAG,X	AT OR PAST END OF FILE ?
	BEQ	LOCATECURBYTEG	B/ NO, MUST FETCH DATA SECTOR
	LDX	FCBPOINTER	IS FILESIZE ON A SECTOR BOUNDARY ?
	LDD	FCB:FILESIZE+2,X	...?
	LDX	DCBPOINTER	(I.E., IS FILESIZE MOD NBPS = 0 ?)
	BITB	DSKINFO:NBPSM1+1,X	... ?
	BNE	LOCATECURBYTEG	B/ NO, MUST FETCH DATA SECTOR
	BITA	DSKINFO:NBPSM1,X	... ?
	BNE	LOCATECURBYTEG	B/ NO, MUST FETCH DATA SECTOR
	LDX	IOCBPOINTER	YES! INVENT A ZEROED SECTOR INSTEAD OF READING
	JSR	INVENTSECTOR
	JSR	ZEROSECTOR	GO ZERO THE SECTOR INVENTED
	BRA	LOCATECURBYTE5A	AND CONTINUE AS THOUGH WE DID A READ

LOCATECURBYTEG	; GET THE DATA SECTOR
	LDX	IOCBPOINTER	GET POINTER TO SECTOR DESCRIPTOR
	JSR	READSECTOR	AND INITIATE THE READ
LOCATECURBYTE5A ; SECTOR HAS BEEN FETCHED
	LDX	IOCBPOINTER	SAVE RDSI ADDRESS IN IOCB
	LDD	RDSIPOINTER
	STD	IOCB:DRDSI,X
	JSR	COMPUTENEXTBYTE	SET UP POINTER INTO DATA SECTOR
	LDAA	WRITEFLAG	ARE WE WRITING ?
	LBNE	LOCATECURBYTEW	B/ YES
LOCATECURBYTER	; READING FROM THE FILE
	LDAA	IOCB:EOFFLAG,X	HIT EOF?
	BNE	LOCATECURBYTE9	B/ YES, GO BITCH
	LDX	IOCB:FCB,X	NO, COMPUTE FILESIZE - CURBYTE
	LDD	FCB:FILESIZE,X
	PSHD
	LDD	FCB:FILESIZE+2,X
	LDX	IOCBPOINTER	DO THE SUBTRACT
	SUBD	IOCB:CURBYTE+2,X
	STD	TEMPX	WE NEED DISTANCE TO EOF LATER
	PULD		(RETREIVE UPPER HALF OF FCB:FILESIZE)
	SBCB	IOCB:CURBYTE+1,X
	SBCA	IOCB:CURBYTE,X
	BNE	LOCATECURBYTE7	B/ > 2^24 BYTES FROM END OF FILE
	TSTB
	BNE	LOCATECURBYTE7	B/ > 65536 BYTES FROM END OF FILE
	LDD	TEMPX	"CLOSE" TO END OF FILE
	CMPD	IOCB:BYTECOUNT,X	< 65536 BYTES FROM END OF FILE
	BHI	LOCATECURBYTE7	B/ USE NBPS-DISPLACEMENT
LOCATECURBYTE6	EQU	*
	STD	IOCB:BYTECOUNT,X	SAVE # BYTES TO MOVE MAX
LOCATECURBYTE7	EQU	*
	JSR	WAITRDSI	WAIT FOR I/O COMPLETED
	LDX	IOCBPOINTER
	LDAA	#1	FLAG "CURBYTE HAS BEEN LOCATED"
	STAA	IOCB:LOCATEDF,X
	JSR	READAHEAD	FIRE UP THE READ-AHEAD
LOCATECURBYTED	; ALMOST DONE!
	LDX	IOCBPOINTER	COMPUTE MIN(BYTECOUNT,REQUESTED)
	LDD	USEDCOUNT
	CMPD	IOCB:BYTECOUNT,X
	BCS	LOCATECURBYTED2	B/ USE REQUESTED
	LDD	IOCB:BYTECOUNT,X	USE BYTECOUNT
	STD	USEDCOUNT	SAVE # BYTES USED AT THIS STEP
LOCATECURBYTED2	EQU	*
	LDX	IOCBPOINTER	AS PROMISED
	OKRTS

LOCATECURBYTE9	; WHEN WE GET HERE, IOCB:EOF IS ALREADY SET
	CLR	IOCB:BYTECOUNT,X	ZAP THE BYTECOUNT
	CLR	IOCB:BYTECOUNT+1,X
	JMP	ERREOFHIT
	PAGE
LOCATECURBYTEW	; WRITING TO FILE
	LDX	IOCBPOINTER	JUST IN CASE
	LDX	IOCB:DRDSI,X	DID WE END UP...
	LDAA	RDSI:DISKINFO,X	TRYING TO WRITE ON THE IMAGINARY SECTOR ?
	LBEQ	LOCATECURBYTE2	B/ YES, GO ALLOCATE THE ACTUAL DATA CLUSTER
	STAA	RDSI:MODIFIED,X	MAKE SURE THIS DATA SECTOR GOES BACK TO DISK
	LDX	IOCBPOINTER
	LDD	USEDCOUNT	COMPUTE MIN OF REQUESTED COUNT,
	CMPD	IOCB:BYTECOUNT,X	AND NBPS-DISPLACMENT INTO SECTOR
	BCS	LOCATECURBYTEW2	B/ REQUESTED COUNT IS SMALLER
	LDD	IOCB:BYTECOUNT,X	USE NBPS-DISPLACEMENT AS # BYTES
	STD	USEDCOUNT	SAVE # BYTES TO MOVE, MAX
LOCATECURBYTEW2	EQU	*
	ADDD	IOCB:CURBYTE+2,X	COMPUTE CURBYTE + # BYTES TO BE WRITTEN
	STD	TEMP+2
	LDX	IOCB:CURBYTE,X
	BCC	LOCATECURBYTEW5
	INX
LOCATECURBYTEW5	EQU	*
	STX	TEMP
	LDX	FCBPOINTER	NOW COMPARE TO CURRENT FILESIZE
	LDD	FCB:FILESIZE+2,X	BY SUBTRACTING NEW FILE SIZE FROM FCB:FILESIZE
	SUBD	TEMP+2
	LDD	FCB:FILESIZE,X
	SBCB	TEMP+1
	SBCA	TEMP
	BCC	LOCATECURBYTEW4	B/ NEW "END OF FILE" < OLD, DON'T UPDATE
	LDD	TEMP	NEW EOF > OLD
	STD	FCB:FILESIZE,X	UPDATE FILESIZE
	LDD	TEMP+2
	STD	FCB:FILESIZE+2,X
	LDAA	FCB:FLAGS,X	MAKE SURE DIRECTORY GETS UPDATED
	ORAA	#FCBFLG::UPDATEDIR
	STAA	FCB:FLAGS,X
LOCATECURBYTEW4	EQU	*
	JSR	WAITRDSI	WAIT FOR DISK SECTOR TO ARRIVE
	LDX	IOCBPOINTER	NOW MARK CURBYTE...
	LDAA	#1	AS "LOCATED"
	STAA	IOCB:LOCATEDF,X
	JSR	READAHEAD	GET READAHEAD FIRED UP.
	LDD	USEDCOUNT	GET # BYTES TO MOVE
	JMP	LOCATECURBYTED2
	PAGE
*	ZEROTOCURBYTE -- ZEROS OUT SECTORS BETWEEN FILESIZE AND IOCB:CURBYTE
*		ONLY USED WHEN WRITING AT END-OF-FILE...
*		AND IOCB:CURBYTE IS FAR PAST FILESIZE
*
ZEROTOCURBYTE ; CURBYTE > 1 SECTOR PAST FILESIZE
*	JSR	CHECKMUSTZERO	MUST WE ZERO A SECTOR ?
*	BCS	ZEROTOCURBYTE3	B/ NO, GET OUT FAST !
	LDX	FCBPOINTER	IS FILESIZE **NOT** ON A SECTOR BOUNDARY ?
	LDD	FCB:FILESIZE+2,X	(I.E., IS SECTOR@FILESIZE ALREADY ZEROED?)
	LDX	DCBPOINTER	...?
	BITB	DSKINFO:NBPSM1+1,X
	BNE	ZEROTOCURBYTEA	B/ NOT ON SECTOR BOUNDARY, GO ADVANCE TO NEXT
	BITA	DSKINFO:NBPSM1,X
	BNE	ZEROTOCURBYTEA	B/ NOT ON SECTOR BOUNDARY, GO ADVANCE TO NEXT
ZEROTOCURBYTEZ ; MUST ZERO SECTOR SELECTED BY FILESIZE
	JSR	COMPUTERDCNF	LOCATE LSN OF SECTOR TO BE ZEROED
	BCS	ZEROTOCURBYTE3	B/ FILESIZE = MAX LEGAL SIZE FOR THIS DISK !!
******** CAN FILESIZE BECOME > MAX LEGAL SIZE FOR THIS DISK ??????
	STD	IOCB:RDCN,X	SAVE RELATIVE DATA CLUSTER #
	LDD	IOCB:RBN,X	COMPUTE RELATIVE SECTOR # NEEDED
	JSR	COMPUTERSN
	STAB	IOCB:DRSN,X	AND SAVE IT
	JSR	LOCATERDCN	FIND LCN OF DATA CLUSTER
	BCS	ZEROTOCURBYTE5	B/ ERROR IN LOCATING LCN OF CLUSTER
	LDD	HEADER:LCN,X	GET CLUSTER # FROM HEADER
	LDX	IOCBPOINTER	TO BE COMPATIBLE...
	STD	IOCB:CURLCN,X	WITH LOCATECURBYTE0
	LDX	IOCB:CURLCN,X	IS THIS CLUSTER ALLOCATED ?
	INX		... ?
	BEQ	ZEROTOCURBYTEA	B/ NO, THIS SECTOR IS EFFECTIVELY ZEROED ALREADY !
	JSR	CONVERTLCNTOLSN	YES, CONVERT LCN TO DESIRED SECTOR #
	JSR	INVENTSECTOR	OUT OF NOWHERE SO WE CAN ZERO IT!!
	JSR	ZEROSECTOR	THEN ZERO IT...
	JSR	MARKMODIFIED	AND MAKE THIS SECTOR GO TO THE DISK
ZEROTOCURBYTEA ; ADVANCE FILESIZE PAST ZEROED SECTOR
	LDX	FCBPOINTER
	LDD	FCB:FILESIZE+2,X	(FIND LAST BYTE OF SECTOR)
	LDX	DCBPOINTER
	ORA	DSKINFO:NBPSM1,X
	ORB	DSKINFO:NBPSM1+1,X
	LDX	FCBPOINTER	(RESULT MUST GO BACK TO FCB)
	ADDD	#1		(ADD 1 TO GET 1ST BYTE OF NEXT SECTOR)
	STD	FCB:FILESIZE+2,X
	BCC	ZEROTOCURBYTEA1	B/ DON'T HAVE TO PROPOGATE CARRY
	INC	FCB:FILESIZE+1,X
	BNE	ZEROTOCURBYTEA1	B/ DON'T HAVE TO PROPOGATE CARRY
	INC	FCB:FILESIZE,X
ZEROTOCURBYTEA1
	LDA	FCB:FLAGS,X	MARK FCB AS MODIFIED
	ORA	#FCBFLG::UPDATEDIR
	STA	FCB:FLAGS,X
	BSR	CHECKMUSTZERO	MUST WE ZERO MORE SECTORS AT EOF ?
	BCS	ZEROTOCURBYTEZ	B/ YES, GO ZERO ANOTHER ONE!
ZEROTOCURBYTE3
	JMP	LOCATECURBYTE0	SINCE ZEROTOCURBYTE DESTROYS LOCATOR INFORMATION

ZEROTOCURBYTE5	EQU	*
	CPX	#ERR:HCSICTOOSMALL	VALID REASON PREVENTING US FROM LOCATING CLUSTER?
	BEQ	ZEROTOCURBYTE3	B/ YES, JUST QUIT
	JMP	ERRORED	NO, GO BITCH AND SCREAM AND SHOUT!
	PAGE
*	CHECKMUSTZERO -- CHECK IF WE MUST ZERO ANOTHER SECTOR
*	USED TO HELP HANDLE SPARSE FILE ZEROING AT END OF FILE
*	CHECKS TO SEE IF SECTOR SELECTED BY FCB:FILESIZE...
*	CONTAINS THE BYTE SELECTED BY IOCB:CURBYTE
*	RETURNS CARRY RESET IF YES, NO NEED TO ZERO
*	RETURNS CARRY SET IF NO, MUST ZERO ANOTHER SECOTR
*
CHECKMUSTZERO	; CHECK IF WE MUST ZERO SECTORS
	LDX	FCBPOINTER	FETCH FILESIZE FOR COMPARE
	LDD	FCB:FILESIZE+2,X
	LDX	FCB:FILESIZE,X
	STX	TEMPX		TEMPX,D:=FILESIZE
	LDX	DCBPOINTER	FIND LAST BYTE OF SECTOR SELECTED BY FILESIZE
	ORA	DSKINFO:NBPSM1,X
	ORB	DSKINFO:NBPSM1+1,X
	LDX	IOCBPOINTER	NOW COMPARE TO IOCB:CURBYTE
	SUBD	IOCB:CURBYTE+2,X
	LDD	TEMPX
	SBCB	IOCB:CURBYTE+1,X
	SBCA	IOCB:CURBYTE,X
	RTS		CARRY IS RESET IF CURBYTE IN SECTOR AT FILESIZE
	PAGE
*	UPDATECURBYTE -- BY ADDING USEDCOUNT
*		ALSO ADJUSTS BYTECOUNT, NEXTBYTE
*		CAN BE CALLED ONLY IF IOCB:LOCATEDF IS VALID
*
UPDATECURBYTE	EQU	*
	LDX	IOCBPOINTER	ADD # BYTES USED TO CURBYTE
	LDD	IOCB:CURBYTE+2,X
	ADDD	USEDCOUNT
	STD	IOCB:CURBYTE+2,X
	BCC	UPDATECURBYTE1	B/ NO NEED TO UPDATE UPPER 16 BITS
	INC	IOCB:CURBYTE+1,X
	BNE	UPDATECURBYTE1
	INC	IOCB:CURBYTE,X
UPDATECURBYTE1	EQU	*
	LDD	IOCB:NEXTBYTE,X	NOW ADJUST POINTER TO NEXT BYTE IN SECTOR BUFFER
	ADDD	USEDCOUNT	BY ADVANCING IT BY # USED BYTES
	STD	IOCB:NEXTBYTE,X
	LDD	IOCB:BYTECOUNT,X	NOW ADJUST # BYTES REMAINING IN FILE/SECTOR
	SUBD	USEDCOUNT
	STD	IOCB:BYTECOUNT,X
	IF	M6800!M6801
	BNE	UPDATECURBYTERTS	B/ THERE ARE MORE IN THIS SECTOR
	TSTA		ANY BYTES LEFT IN THIS SECTOR ?
UPDATECURBYTERTS	EQU	*
	FIN
	RTS
	PAGE
*
*	UPDATECURBYTEF -- UPDATE CURBYTE FOR DISK FILES
*
UPDATECURBYTEF	EQU	*
	BSR	UPDATECURBYTE	GO DO THE STUFF WE HAVE IN COMMON WITH DISK DEVICE
	BNE	UPDATECURBYTEOKRTS	B/ THERE ARE SOME BYTES LEFT IN THIS SECTOR, GET OUT!
	LDAA	IOCB:EOFFLAG,X	NO MORE, WERE WE AT EOF ?
	BNE	UPDATECURBYTEOKRTS	B/ YES, LEAVE EOF FLAG SET
	JMP	DSKFEOFTEST	NO, GO SEE IF WE ARE AT EOF!
*			WE USUALLY ONLY GET HERE IF WE'RE READING!
UPDATECURBYTEOKRTS	EQU	*
	OKRTS
*
*	UPDATECURBYTED -- UPDATECURBYTE FOR DISK DEVICE
*
UPDATECURBYTED	EQU	*
	BSR	UPDATECURBYTE	GO DO THE COMMON STUFF
	BNE	UPDATECURBYTEOKRTS	B/ MORE BYTES LEFT IN SECTOR, GET OUT!
	JMP	DSKDEOFTEST	SECTOR EXHAUSTED, GO CHECK FOR EOF
	PAGE
ERRDISKWRITELOCKED	EQU	*
	JSR	ERRET
	FDB	ERR:DISKWRITELOCKED

ERREOFHITJ2	EQU	*
	JMP	ERREOFHIT
*
*	LOCDSKD -- LOCATE CURBYTE FOR DISK DEVICE
*		ENTERED WITH CURLSN PRE-COMPUTED BY DSKDEOFTEST
*		(A,B) CONTAIN REQUEST COUNT
*		IF WRITE REQUEST, AND DISK IS WRITE LOCKED...
*			CAUSES A "DISK WRITE LOCKED" ERROR
*		IF WRITING AT LEAST NBPS BYTES, STARTING ON A SECTOR BOUNDARY,
*			"INVENTSECTOR" IS USED INSTEAD OF "READSECTOR"
*			TO OPTIMIZE DISK I/O TIME
*			THIS ALSO ALLOWS WRITES TO DISK SECTORS...
*			WHICH CANNOT BE READ
*		IF EOF HIT, TAKES "EOF HIT" ERROR EXIT
*		OTHERWISE, SETS BYTECOUNT = NBPS - (CURBYTE MOD NBPS)
*			USEDCOUNT = MIN(BYTECOUNT,REQUESTED)
*		CALLER MUST CALL UPDATECURBYTED WHEN DONE COPYING BYTES
*
LOCDSKD	EQU	*
	STD	USEDCOUNT	SAVE THE REQUESTED COUNT
	LDX	IOCBPOINTER	HIT EOF ?
	LDAA	IOCB:EOFFLAG,X	... ?
	BNE	ERREOFHITJ2	B/ YES
	LDAA	IOCB:DISKWRITELOCKED,X	DISK DEVICE WRITE LOCKED ?
	BEQ	LOCDSKD1	B/ NO, LET HIM DO ANYTHING
	LDAA	WRITEFLAG	YES, DOES HE WANT TO WRITE ?
	BNE	ERRDISKWRITELOCKED	B/ TOO BAD!
LOCDSKD1	EQU	*
	LDAA	IOCB:DRDSI,X	IS DISK SECTOR STILL AROUND ?
	BEQ	LOCDSKD0	B/ NO, GO READ IT IN AGAIN
	LDD	IOCB:BYTECOUNT,X	YES, ANY BYTES LEFT IN THIS SECTOR ?
	BNED	LOCATECURBYTEDJ	B/ YES, TAKE QUICK EXIT
LOCDSKD0	EQU	*
	LDAA	WRITEFLAG	WRITING TO SECTOR ?
	BEQ	LOCDSKDR	B/ NO
	LDX	IOCB:DCB,X	YES, WRITING AT LEAST A SECTOR'S WORTH...
	LDD	DSKINFO:NBPSM1,X	TO A SECTOR BOUNDARY ?
	LDX	IOCBPOINTER	(WE NEED THIS AGAIN)
	BITA	IOCB:CURBYTE+2,X	IF NOT ON SECTOR BOUNDARY, READ TO UPDATE SECTOR
	BNE	LOCDSKDR	B/ NOT ON A SECTOR BOUNDARY
	BITB	IOCB:CURBYTE+3,X
	BNE	LOCDSKDR	B/ NOT ON A SECTOR BOUNDARY
	SUBD	USEDCOUNT	IS USEDCOUNT >= NBPS ?
	BCC	LOCDSKDR	B/ NO, MUST READ TO UPDATE
	JSR	INVENTSECTOR	YES, OPTIMIZE SECTOR WRITE!
	STAA	RDSI:MODIFIED,X	MARK SECTOR AS "DIDDLED"
	LDX	IOCBPOINTER	REMEMBER WHERE THE SECTOR IS
	STD	IOCB:DRDSI,X
	JSR	COMPUTENEXTBYTE	SET UP BYTECOUNT FOR TRANSFER
	BRA	LOCATECURBYTEDJ	DON'T DO READ-AHEAD!
	PAGE
LOCDSKDR	; MUST READ SECTOR (WITH POSSIBLE UPDATE)
	JSR	READSECTOR	GO READ IN THE DATA SECTOR
	TST	WRITEFLAG	WRITING ?
	BEQ	LOCDSKD2	B/ NO
	STAA	RDSI:MODIFIED,X	YES, MARK RDSI AS MODIFIED
LOCDSKD2	EQU	*
	LDX	IOCBPOINTER	REMEMBER THE RDSI ADDRESS
	STD	IOCB:DRDSI,X
	JSR	COMPUTENEXTBYTE	SET UP BYTECOUNT
	JSR	WAITRDSI	WAIT FOR ARRIVAL OF DATA SECTOR
	LDAA	NRDSIS	ENOUGH BUFFERS AVAILABLE TO START READ-AHEAD ?
	CMPA	#READAHEADRDSIMIN	... ?
	BLS	LOCATECURBYTEDJ	B/ NO, SO DON'T INITIATE READ-AHEAD
	JSR	INCIOCB:CURLSN	COMPUTE NEXT SECTOR ADDRESS
	LDAA	IOCB:CURLSN,X	WILL READAHEAD LSN BE LEGAL ?
	PSHA		(I.E., IS CURRENT LSN + 1 < NLSN ?)
	LDD	IOCB:CURLSN+1,X	COPY CURRENT LSN TO (TOS,A,B)
	LDX	IOCB:DCB,X	SUBTRACT DSKINFO:NLSN
	SUBD	DSKINFO:NLSN+1,X	(I,E., IS CURRENT LSN < NLSN ?)
	PULA
	SBCA	DSKINFO:NLSN,X
	BCC	LOCDSKD4	B/ NO, DON'T DO READ-AHEAD
	LDX	IOCBPOINTER	SIGH!
	JSR	READSECTOR	INITIATE ITS FETCH
	BCS	LOCDSKD3	B/ AN I/O ERROR ??
LOCDSKD4	EQU	*
	JSR	DECIOCB:CURLSN	PUT LSN BACK LIKE IT WAS
LOCATECURBYTEDJ	EQU	*
	JMP	LOCATECURBYTED	GO COMPUTE USEDCOUNT AND TRANSFERCOUNT

LOCDSKD3	EQU	*
	JMP	READAHEADCERRED	GO BACK UP CURLSN AND ANNOUNCE ERROR
	PAGE
*	LOCATERDCN -- READS IN HEADER SECTOR CONTAINING LCN FOR IOCB:RDCN
*		OKRET: RETURNS (X) POINTING TO DESIRED LCN IN HEADER SECTOR
*		ERRET: DISK I/O ERROR OCCURRED
*
LOCATERDCN	EQU	*
	LDX	IOCBPOINTER	GRAB RDCN
	LDD	IOCB:RDCN,X
	JSR	COMPUTERHSN	GET RELATIVE HEADER SECTOR NUMBER DESIRED
	CMPB	IOCB:HRSN,X	SAME HEADER SECTOR AS LAST TIME ?
	BNE	LOCATERDCN0	B/ NO, MUST READ HEADER SECTOR
	LDAA	IOCB:HRDSI,X	IS THE HEADER SECTOR FROM LAST TIME STILL AROUND ?
	BNE	LOCATERDCN2	B/ YES, DON'T BOTHER READING IT IN!
LOCATERDCN0	; MUST READ IN HEADER SECTOR
	CLR	IOCB:HRDSI,X	MARK CURRENT HEADER SECTOR AS "WRONG ONE!"
	CLR	IOCB:HRDSI+1,X	(IN CASE WE GET AN ERROR)
	STAB	IOCB:HRSN,X	SAVE SO WE CAN COMPUTE DESIRED LSN
	LDX	IOCB:FCB,X	IS HEADER INITIALIZED FAR ENOUGH ?
	CMPB	FCB:HCSIC,X	... ?
	BCC	LOCATERDCN3	B/ NO, INVALID RDCN
	JSR	GENHLSNPLUSHRSN	COMPUTE DESIRED LSN
	JSR	READSECTOR	READ SECTOR CONTAINING PREVIOUS LCN
	LDX	IOCBPOINTER
	STD	IOCB:HRDSI,X	SAVE HEADER SECTOR POINTER
LOCATERDCN2	; HEADER SECTOR IS (BEING) READ IN
	LDD	IOCB:RDCN,X	GET IOCB:RDCN AGAIN
	ASLD		CONVERT TO A BYTE DISPLACEMENT INTO HEADER CLUSTER SECTOR
	LDX	DCBPOINTER	MASK TO GET DISPLACEMENT...
	ANDA	DSKINFO:NBPSM1,X	INTO HEADER SECTOR OF PREVIOUS RDCN
	ANDB	DSKINFO:NBPSM1+1,X
	LDX	IOCBPOINTER	ADD SECTOR BUFFER ADDRESS...
	LDX	IOCB:HRDSI,X	(OF HEADER SECTOR)
	ADDD	RDSI:SECTORBASE,X	TO GET POINTER TO PREVIOUS LOGICAL CLUSTER NUMBER
	STD	LOCATERDCNX	SAVE THE POINTER
	JSR	WAITSECTOR	WAIT FOR HEADER SECTOR TO ARRIVE
	LDX	LOCATERDCNX	GET POINTER TO PREVIOUS LOGICAL CLUSTER NUMBER
	OKRTS

LOCATERDCN3	; INVALID RDCN ENCOUNTERED
	LDX	DCBPOINTER	IS IT EVER POSSIBLE FOR THIS RDCN TO BE VALID ?
	CMPB	DSKINFO:NSPC,X	(I.E., WILL RDCN BE FOUND SOMEWHERE IN HEADER CLUSTER?)
	BCS	ERRHCSICTOOSMALL	B/ YES, INVALID BECAUSE OF HCSIC
ERRCLUSTERSIZELIMITSFILE	EQU	*
	JSR	ERRET
	FDB	ERR:CLUSTERSIZELIMITSFILE

ERRHCSICTOOSMALL	EQU	*
	JSR	ERRET
	FDB	ERR:HCSICTOOSMALL
	PAGE
*
*	COMPUTENEXTBYTE -- GENERATE VALUE FOR IOCB:NEXTBYTE
*		= SECTORBASE(RDSIPOINTER)+(IOCB:CURBYTE MOD DSKINFO:NBPS)
*		SETS IOCB:BYTECOUNT = NBPS - (IOCB:CURBYTE MOD DSKINFO:NBPS)
*		(X) = IOCBPOINTER ON EXIT
*
COMPUTENEXTBYTE	EQU	*
	LDX	DCBPOINTER	GET MASK TO COMPUTE "MOD NBPS"
	LDD	DSKINFO:NBPSM1,X
	LDX	IOCBPOINTER
	ANDA	IOCB:CURBYTE+2,X
	ANDB	IOCB:CURBYTE+3,X	(A,B) = IOCB:CURBYTE MOD DSKINFO:NBPS
	STD	IOCB:BYTECOUNT,X	WE'LL NEED THIS AGAIN SOON
	LDX	IOCB:DRDSI,X
	ADDD	RDSI:SECTORBASE,X
	LDX	IOCBPOINTER	SAVE NEXTBYTE IN IOCB
	STD	IOCB:NEXTBYTE,X
	LDX	DCBPOINTER	NOW COMPUTE NBPS - DISPLACMENT INTO SECTOR
	LDD	DSKINFO:NBPS,X	AND SAVE AS BYTECOUNT
	LDX	IOCBPOINTER
	SUBD	IOCB:BYTECOUNT,X
	STD	IOCB:BYTECOUNT,X
	RTS
	PAGE
*
*	COMPUTERDCNF -- DIVIDES FCB:FILESIZE BY DSKINFO:NBPC
*		EXITS EXACTLY THE SAME AS COMPUTERDCN
*
COMPUTERDCNF ; DIVIDE FCB:FILESIZE BY DSKINFO:NBPC
	LDX	FCBPOINTER	COPY FCB:FILESIZE TO (A,B,X)
	LDD	FCB:FILESIZE,X
	LDX	FCB:FILESIZE+2,X
	BRA	COMPUTERDCNS	GO COMPUTE RELATIVE DATA CLUSTER NUMBER
*
*	COMPUTERDCN -- DIVIDES IOCB:CURBYTE BY NUMBER OF BYTES PER CLUSTER
*		RETURN RELATIVE DATA CLUSTER NUMBER (QUOTIENT) IN (A,B)
*		DEFINITION: 1 --> FIRST DATA CLUSTER
*		TAKES ERRET IF QUOTIENT >= DSKINFO:NBPC/2
*		LEAVES REMAINDER IN IOCB:RBN
*		RETURNS (X)=IOCBPOINTER
*
COMPUTERDCN	EQU	*
	LDX	IOCBPOINTER	COPY IOCB:CURBYTE TO (A,B,X)
	LDD	IOCB:CURBYTE,X
	LDX	IOCB:CURBYTE+2,X
COMPUTERDCNS	; ENTRY POINT IF DIVIDEND IN (A,B,X)
	STX	TEMP.DIVIDEND	SAVE LOWER 16 BITS OF DIVIDEND
	PSHA		SAVE (A) SO WE CAN SET UP NUMBER OF ITERATIONS
	LDX	DCBPOINTER	GET DIVISOR
*	ASSERT: NBPC*2^16 > # BYTES IN LARGEST POSSIBLE FILE...
*	SINCE # BYTES/FILE <= (NBPC/2-1)*NBPC, AND NBPC < 2^16
*	THIS MEANS 1ST QUOTIENT BIT WOULD ALWAYS BE ZERO!
*	SO RATHER THAN GENERATE IT, WE SIMPLY SHIFT IN ZERO
	LDAA	#16	# QUOTIENT BITS TO GENERATE
	STAA	COUNT	# QUOTIENT BITS TO GENERATE
	PULA		GET UPPER 16 BITS OF DIVIDEND TO (D)
COMPUTERDCNL	; GENERATE NEXT QUOTIENT BIT
	ASL	TEMP.DIVIDEND+1	SHIFT A BIT INTO (A,B) ...
	ROL	TEMP.DIVIDEND	FROM REST OF DIVDEND
	ROLD
	CMPD	DSKINFO:NBPC,X	IS (A,B) ...
	BCS	COMPUTERDCN0	B/ < DIVISOR, CARRY IS 1
	SUBD	DSKINFO:NBPC,X	QUOTIENT BIT IS ONE (CARRY IS 0)
COMPUTERDCN0	; CARRY = COMPLEMENT QUOTIENT BIT
	ROL	TEMP.QUOTIENT+1	MOVE QUOTIENT BIT INTO QUOTIENT
	ROL	TEMP.QUOTIENT
	DEC	COUNT	DOWN COUNT # QUOTIENT BITS TO GENERATE
	BNE	COMPUTERDCNL	LOOP IF MUST GEN MORE
	LDX	IOCBPOINTER	OOOPS! DON'T FORGET THIS!
	STD	IOCB:RBN,X	STORE REMAINDER FOR LATER USE
	LDD	TEMP.QUOTIENT	GET QUOTIENT INTO (A,B)
	NEGD		CONVERT QUOTIENT TO TRUE FORM
*			AND ADD 1 SINCE ZERO MEANS "HEADER CLUSTER"
	BCS	COMPUTERDCNLIMIT	IS QUOTIENT+1 >= DSKINFO:NBPC/2 ?
	ASLD		NOT > 65535; (DOUBLE QUOTIENT+1)
	LDX	DCBPOINTER	I.E., IS ( QUOTIENT+1 )*2 >= DSKINFO:NBPC ?
	CMPD	DSKINFO:NBPC,X	...?
	BCC	COMPUTERDCNLIMIT	B/ YES
	LSRD		HALVE DOUBLED QUOTIENT+1 TO GET QUOTIENT+1 BACK
	LDX	IOCBPOINTER
	OKRTS

COMPUTERDCNLIMIT	EQU	*
	JMP	ERRCLUSTERSIZELIMITSFILE
	PAGE
*
*	COMPUTERHSN -- COMPUTE RELATIVE HEADER SECTOR NUMBER
*	DIVIDES (A,B) BY (NBPS/2)
*	RETURNS QUOTIENT IN (B)
*	RETURNS (X)=IOCBPOINTER

COMPUTERHSN	EQU	*
	PSHA
	LDX	DCBPOINTER
	LDAA	DSKINFO:LOG2NBPS,X	GET NBPS POWER OF TWO
	DECA		DIVIDE BY TWO
COMPUTERHSN0	EQU	*
	STAA	TEMPA	SAVE # TIMES TO RIGHT SHIFT
	PULA
COMPUTERHSN1	EQU	*
	LSRD		DIVIDE BY 2^LOG2(NBPS)
	DEC	TEMPA
	BNE	COMPUTERHSN1
	LDX	IOCBPOINTER
	RTS		EXIT WITH QUOTIENT
*
*	COMPUTERSN  -- COMPUTE SECTOR NUMBER WITHIN CLUSTER
*		DIVIDES (A,B) BY NBPS
*		RETURNS QUOTIENT IN (B)
*		RETURNS (X)=IOCBPOINTER
*
COMPUTERSN	EQU	*
	PSHA
	LDX	DCBPOINTER
	LDAA	DSKINFO:LOG2NBPS,X
	BRA	COMPUTERHSN0
	PAGE
*
*	CONVERTLCNTOLSN  -- CONVERTS LCN IN (A,B) TO CORRESPONDING LSN
*		ADDS IOCB:DRSN TO RESULT
*		FINAL LSN PLACED IN IOCB:CURLSN
*		DESTROYS SYSSECTORDB
*		RETURNS (X)=IOCBPOINTER
*
CONVERTLCNTOLSN	EQU	*
	BSR	CONVERTLCNTO1ST	FIND LSN OF FIRST SECTOR IN CLUSTER
	LDX	IOCBPOINTER	FOR CONVENIENCE
	ADDB	IOCB:DRSN,X	ADD RELATIVE SECTOR # ...
	ADCA	#0	TO RESULT
	BCC	CONVERTLCN2
	INC	SYSSECTORDB+SECTORDB:LSN
CONVERTLCN2	EQU	*
	STD	IOCB:CURLSN+1,X	UPDATE IOCB
	LDAA	SYSSECTORDB+SECTORDB:LSN	COPY MSB OF DESIRED LSN TO IOCB
	STAA	IOCB:CURLSN,X
	RTS		AND SPLIT
	PAGE
*
*	CONVERTLCNTO1ST -- CONVERTS LCN IN (A,B) TO 1ST LSN IN CLUSTER
*		RESULT PLACED IN SYSSECTORDB+SECTORDB:LSN
*		LOWER 16 BITS RETURNED IN (A,B)
*		(X) POINTS TO SYSSECTORDB, ALL SET TO READ ON EXIT
*
CONVERTLCNTO1ST	EQU	*
	STD	TEMP.MPCND	SAVE THE MULTIPLICAND
	LDX	DCBPOINTER	GET MULTIPLIER FROM DISKINFO
	STX	SYSSECTORDB+SECTORDB:DISKINFO	SET UP TO READ THIS SECTOR
	LDAB	DSKINFO:NSPC,X	(I.E., THE CLUSTER SIZE)
	STAB	TEMP.MPR	SAVE THE MULTIPLIER
	LDAA	#8	# MULTIPLIER BITS TO PROCESS
	STAA	COUNT
	CLRA		ZERO THE PARTIAL PRODUCT
	CLRB		ACCUMULATE PRODUCT IN SYSSECTOR+SECTORDB:LSN
CONVERTLCNL	EQU	*
	ASLD
************** USE MUL INSTRUCTION IN HERE?????
	ROL	SYSSECTORDB+SECTORDB:LSN	NO OVERFLOW POSSIBLE
	ASL	TEMP.MPR	CHECK NEXT MULTIPLIER BIT
	BCC	CONVERTLCN1	B/ =0, DON'T ADD MULTIPLICAND
	ADDD	TEMP.MPCND	=1, ADD MULTIPLICAND TO PARTIAL PRODUCT
	BCC	CONVERTLCN1	(PROPOGATE CARRY TO 3RD BYTE)
	INC	SYSSECTORDB+SECTORDB:LSN
CONVERTLCN1	EQU	*
	DEC	COUNT	DOWN COUNT UNPROCESSED MULTIPLIER BITS
	BNE	CONVERTLCNL	B/ MORE TO PROCESS
	STD	SYSSECTORDB+SECTORDB:LSN+1	SAVE LOWER 16 BITS OF LSN
	LDX	#SYSSECTORDB	AS PROMISED
	RTS
	PAGE
*
*	GENHLSNPLUSHRSN -- ADD FCB:HLSN TO IOCB:HRSN
*		AND COPY SUM TO IOCB:CURLSN
*		RETURNS (X) = IOCBPOINTER
*
GENHLSNPLUSHRSN	EQU	*
	LDX	FCBPOINTER	GET LSN OF 1ST SECTOR...
	LDAA	FCB:HLSN,X	OF HEADER CLUSTER...
	PSHA		TO (TOS),(A,B)
	LDD	FCB:HLSN+1,X
	LDX	IOCBPOINTER	ADD HRSN...
	ADDB	IOCB:HRSN,X
	ADCA	#0	PROPOGATE CARRY
	STD	IOCB:CURLSN+1,X	AND COPY TO IOCB:CURLSN
	PULA
	ADCA	#0
	STAA	IOCB:CURLSN,X
	CLR	IOCB:LOCATEDF,X	FORCE "LOCATECURBYTE" AGAIN LATER
	RTS
	PAGE	DISK FILE DRIVERS -- DISK I/O
*	READBEHIND -- INITIATES PRE-FETCH OF PREVIOUS LSN IN FILE
*		(X) POINTS TO IOCB
*		INITIATES READ FOR IOCB:CURLSN-1
*		IF IOCB:CURLSN IS OUTSIDE OF IOCB:CURLCN,
*		DOES NOTHING AT ALL
*		DCBPOINTER SELECTS PROPER DISK INFO TABLE

READBEHIND	EQU	*
	LDX	IOCBPOINTER
	LDAA	IOCB:DRSN,X	AT 1ST SECTOR OF CLUSTER?
	BEQ	READBEHINDOKRTS	B/ YES, NO READ-BEHIND TO DO!
	BSR	DECIOCB:CURLSN	BACK UP LSN TO PREVIOUS LSN
	JSR	READSECTOR	GO INITIATE THE READ
	BCS	READBEHINDERRED	B/ I/O ERROR
*	BSR	INCIOCB:CURLSN	PUT IOCB:LSN BACK LIKE IT WAS
*
*	INCIOCB:CURLSN -- INCREMENTS IOCB:CURLSN
*		NO IDIOT CHECKS
*
*
INCIOCB:CURLSN	EQU	*
	LDX	IOCBPOINTER
	INC	IOCB:CURLSN+2,X	BUMP LSB
	BNE	INCIOCB:CURLSNX
	INC	IOCB:CURLSN+1,X	PROPOGATE CARRY
	BNE	INCIOCB:CURLSNX
	INC	IOCB:CURLSN,X	AND AGAIN
READBEHINDOKRTS	EQU	*
INCIOCB:CURLSNX	EQU	*
	OKRTS

READBEHINDERRED	EQU	*
	BSR	INCIOCB:CURLSN	RESTORE CURLSN TO ORIGINAL VALUE
	JMP	ERRORED
	PAGE
*
*	READAHEAD -- INITIATES PRE-FETCH FOR NEXT LSN OF FILE
*		(INITIATES WRITE OF LAST SECTOR BUFFER ON LRU QUEUE IF NEEDED [WRITE-AHEAD])
*		INITIATES READ FOR IOCB:CURLSN+1 UNLESS THAT IS OUTSIDE OF IOCB:CURLSN
*		OTHERWISE INITIATES READ FOR APPROPRIATE HEADER CLUSTER SECTOR
*		ASSUMES A LOCATECURBYTE HAS RECENTLY SET UP THE IOCB
*
READAHEAD	EQU	*
*	IF LAST SECTOR IN LRU QUEUE IS NOT BUSY,...
*	AND DISK IT COMES FROM IS NOT BUSY,...
*	AND LAST SECTOR IN LRU QUEUE IS MODIFIED,...
*	INITIATE A WRITE AND RESET THE MODIFIED FLAG; DON'T ISSUE A READ!!
	LDX	LASTSECTORREADQ+RDSI:BLINK	GET POINTER TO LAST SECTOR RDSI
	LDAA	RDSI:STATE,X	IS SECTOR IN I/O STATE ?
	ORAA	RDSI:MODIFIED,X	OR IS IT MODIFIED ?
	BEQ	READAHEAD0	B/ NEITHER, GO ISSUE A READ!
	LDAA	RDSI:STATE,X	IS THIS SECTOR BUSY?
	BNE	READBEHINDOKRTS	B/ SECTOR IS IN I/O STATE, DON'T USE IT!
*	THIS IS WRITEAHEAD --> RDSI:MODIFIED IS SET!
	LDX	RDSI:DISKINFO,X	GET DISK DCB ADDRESS (ASSERT: RDSK:DISKINFO <>0)
	LDX	DSKINFO:SECTORDB,X	IS DISK FOR LAST SECTOR BUSY ?
	BNE	READBEHINDOKRTS	B/ YES, DO NADA
	LDX	LASTSECTORREADQ+RDSI:BLINK	GET POINTER TO LAST SECTOR RDSI AGAIN
	STX	RDSIPOINTER	IN CASE WE GET A WRITE ERROR
	LDAA	#RDSISTATE:WRITING	MARK SECTOR AS IN I/O STATE
	STAA	RDSI:STATE,X
	CLR	RDSI:MODIFIED,X	REMOVE THE MODIFIED FLAG
	JSR	DISKWRITE	ISSUE THE WRITE REQUEST
	BCC	READBEHINDOKRTS	B/ NO ERRORS
	JMP	WAITSECTORERRED	GOT WRITE ERROR, GO CLEAN UP THE MESS!

READAHEAD0	; TRY TO DO READ-AHEAD
	LDAA	NRDSIS	SHOULD WE DO READ-AHEAD ?
	CMPA	#READAHEADRDSIMIN	(ARE THERE ENOUGH BUFFERS ?)
	BLT	READBEHINDOKRTS	B/ NOPE, DON'T DO READ-AHEAD
	LDX	DCBPOINTER	IS THIS DISK DRIVE ALREADY BUSY ?
	LDX	DSKINFO:SECTORDB,X	(IF SO, DON'T MAKE US WAIT BY CAUSING MORE DISK I/O!)
	BNE	READBEHINDOKRTS	B/ YES, GIVE UP NOW!
	LDX	IOCBPOINTER
	LDAA	IOCB:EOFFLAG,X	AT EOF ?
	BNE	READBEHINDOKRTS	B/ YES, NO POINT IN FETCHING FROM DISK!
	LDAA	IOCB:DRSN,X	GET RELATIVE SECTOR NUMBER OF :CURLSN
	LDX	IOCB:DCB,X	=LAST SECTOR IN CLUSTER?
	SUBA	DSKINFO:NSPC,X	(IOCN:RSN-DSKINFO:NSPC=-1)
	INCA
	BEQ	READAHEADH	B/ YES, SO READ AHEAD ON HEADER SECTOR INSTEAD
	LDX	IOCBPOINTER	IS THIS CLUSTER ALLOCATED ?
	LDX	IOCB:CURLCN,X	...?
	INX			...?
	BEQ	READBEHINDOKRTS	B/ NO, DON'T DO A READ-AHEAD
	BSR	INCIOCB:CURLSN	NO, COMPUTE NEXT LSN IN CLUSTER
	JSR	READSECTOR	INITIATE PRE-FETCH
	BCS	READAHEADCERRED	IF AN ERROR OCCURRED, REPORT IT!
*	JSR	DECIOCB:CURLSN
*	OKRTS
*
*	DECIOCB:CURLSN -- DECREMENTS IOCB:CURLSN
*		NO IDIOT CHECKS
*
DECIOCB:CURLSN	EQU	*
	LDX	IOCBPOINTER
	LDAA	IOCB:CURLSN+2,X	CHECK LEAST SIG BYTE
	BNE	DECIOCB:CURLSN1	B/ NOT ZERO, NO BORROW
	LDD	IOCB:CURLSN,X	RATS, MUST PROPOGATE BORROW
	SUBD	#1	SUBTRACT 1 FROM UPPER 16 BITS OF IOCB:CURLSN
	STD	IOCB:CURLSN,X
DECIOCB:CURLSN1	EQU	*
	DEC	IOCB:CURLSN+2,X	DECREMENT LSB OF LSN
	OKRTS

READAHEADCERRED	EQU	*
	BSR	DECIOCB:CURLSN	PUT CURLSN BACK LIKE IT WAS
	JMP	ERRORED
	PAGE
READAHEADH	; DO PREFETCH ON HEADER CLUSTER SECTOR
	LDX	IOCBPOINTER	WE CAN'T GET HERE UNLESS IOCB:RDCN IS VALID
	LDD	IOCB:RDCN,X	COMPUTE (IOCB:RDCN+1)*2
	ADDD	#1
	ASLD		...*2
	LDX	IOCB:DCB,X	= DSKINFO:NBPC ?
	CMPD	DSKINFO:NBPC,X	... ?
	BEQ	READAHEADOKRTS	B/ YES, WOULD RUN OFF END OF HEADER CLUSTER
	ANDA	DSKINFO:NBPSM1,X	TAKE MOD NBPS
	ANDB	DSKINFO:NBPSM1+1,X	BY MASKING AGAINST (2^N)-1
	BNE	READAHEAD1	B/ HAVEN'T RUN OFF END ...
	TSTA		OF CURRENT HEADER SECTOR
	BEQ	READAHEADNX	B/ DID RUN OFF END OF HEADER SECTOR
READAHEAD1	EQU	*
	LDX	IOCBPOINTER
	LDX	IOCB:HRDSI,X	DESIRED HEADER SECTOR IN MEMORY?
	BEQ	READAHEADHA	B/ NO, MUST READ IN
	ADDD	RDSI:SECTORBASE,X	YES, FETCH DATA CLUSTER NUMBER
*	COMPUTE POINTER TO DATA CLUSTER NUMBER
	PSHD		COPY POINTER FROM (A,B) TO (X)
	JSR	REQUERDSI	MOVE HEADER SECTOR TO TOP OF LRU QUEUE...
*	BECAUSE IT WORKS WELL
*	IN SPITE OF THE FACT THAT WE DON'T KNOW IF USER WANTS HEADER SECTOR AGAIN!
	PULX
	LDD	HEADER:LCN,X	GET LCN OF NEXT DATA CLUSTER
	LDX	HEADER:LCN,X	UNALLOCATED DATA CLUSTER?
	INX		(I.E., IS LCN = DUMMYLCN ?)
	BEQ	READAHEADOKRTS	B/ YES, NO DATA TO READ AHEAD ON
	JSR	CONVERTLCNTO1ST	FIND 1ST LSN IN NEXT DATA CLUSTER
	BCS	READAHEADOKRTS	IF OFF END OF WORLD, IGNORE IT!
READAHEADS	EQU	*
	LDX	#SYSSECTORDB
	JSR	READSECTOR	READ IN THE DATA SECTOR
READAHEADOKRTS	EQU	*
	OKRTS

READAHEADNX	; MUST READ NEXT HEADER SECTOR
	LDX	IOCBPOINTER
	LDAA	IOCB:HRSN,X
	INCA
	BEQ	READAHEADOKRTS	B/ HRSN+1=256, INVALID
	LDX	IOCB:FCB,X
	CMPA	FCB:HCSIC,X	HRSN+1>=HCSIC?
	BLS	READAHEADH1	NO, GO ISSUE THE READ
	BRA	READAHEADOKRTS	WOULD FETCH UNINITZD HEADER SECTOR

READAHEADHA	; MUST RE-READ CURRENT HEADER SECTOR
	LDX	IOCBPOINTER
	LDAA	IOCB:HRSN,X
READAHEADH1	; READ HEADER SECTOR; RSN IS IN (A)
	LDX	FCBPOINTER	COMPUTE FCB:LSN+(A)
	ADDA	FCB:HLSN+2,X
	STAA	SYSSECTORDB+SECTORDB:LSN+2
	LDAA	FCB:HLSN+1,X
	ADCA	#0
	STAA	SYSSECTORDB+SECTORDB:LSN+1
	LDAA	FCB:HLSN,X
	ADCA	#0
	STAA	SYSSECTORDB+SECTORDB:LSN	AND COPY INTO SYSSECTOR
	LDX	DCBPOINTER	SET UP TO READ FROM CURRENT DISK
	STX	SYSSECTORDB+SECTORDB:DISKINFO
	BRA	READAHEADS	GO INITIATE THE SECTOR READ
*	DON'T SAVE RDSIPOINTER RETURNED IN IOCB:HRDSI...
*	BECUZ IT MIGHT BE FOR "NEXT" HEADER SECTOR!
	PAGE
*
*	INVENTSECTOR -- ALLOCATES A SECTOR BUFFER WITHOUT READING SECTOR
*		LIKE READSECTOR, BUT DOES NOT DO READ!
*		HOWEVER, IT DOES DO A WAITRDSI TO MAKE SURE THE SECTOR HAS ARRIVED
*		NOTE: CAN BE CALLED TO INVENT SECTOR FOR IMAGINARY DISK (DISKINFO=0)
*
INVENTSECTOR	EQU	*
	CLRA		FLAG "DON'T DO THE READ!"
	BRA	READSECTOR0

*
*	READSECTOR -- INITIATE READ OF DISK SECTOR INTO MEMORY
*		(X) POINTS TO SECTOR DESCRIPTOR (SECTORDB)
*		1)  SEARCH RDSIS TO SEE IF ALREADY IN CORE
*		2)  IF NOT, FIND OLDEST BLOCK; IF MODIFIED, WRITE
*		3)  INIT SECTOR FETCH
*		4)  ZAP ALL IOCBS THAT REFER TO OLDEST BLOCK
*	RETURNS (A,B), (X), RDSIPOINTER POINTING TO RDSI CONTAINING DISK SECTOR
*	NOTE: CALLER MUST LATER DO A "WAITRDSI" TO ENSURE ARRIVAL OF SECTOR!
*	NOTE: CANNOT BE CALLED TO READ A SECTOR FROM IMAGINARY DISK (DISKINFO=0)
*	ERRET INDICATES SOME KIND OF DISK I/O PROBLEM
*
READSECTOR	EQU	*
	LDAA	#1	FLAG "DOING A READSECTOR"
READSECTOR0	EQU	*
	STAA	READFLAG
	STX	READSECTORDBP	SAVE POINTER TO DESCRIPTOR BLOCK
	LDD	SECTORDB:LSN+1,X	GET LEAST SIG 16 BITS OF LSN
	LDX	LASTSECTORREADQ+RDSI:FLINK	SEARCH RDSI CHAIN
READSECTORL	EQU	*
	CMPD	RDSI:LSN+1,X	COMPARE LSB'S FIRST...
	BNE	READSECTORNM	SINCE THEY HAVE HIGHEST PROBABILITY OF NOT MATCHING
	STX	RDSIPOINTER	SAVE X A MOMENT...
	LDD	RDSI:DISKINFO,X	DO DISKINFO POINTERS MATCH ?
	LDX	READSECTORDBP	...?
	CMPD	SECTORDB:DISKINFO,X	...?
	BNE	READSECTORNMA	B/ NO
	LDAB	SECTORDB:LSN,X	YES, GRAB MSB OF LSN OF DESIRED SECTOR
	LDX	RDSIPOINTER	DOES IT MATCH DESIRED LSN ?
	CMPB	RDSI:LSN,X	... ?
	BNE	READSECTORL1	B/ DIDN'T MATCH, AND AFTER ALL THAT EFFORT!
	JMP	READSECTORF	WE FOUND IT !!

READSECTORL1	EQU	*
	LDX	READSECTORDBP	GET POINTER TO SECTOR DESCRIPTOR
READSECTORNMA	; NO MATCH, RELOAD 16 LSBS OF DESIRED LSN
	LDD	SECTORDB:LSN+1,X	GET 16 LSBS OF DESIRED LSN AGAIN
	LDX	RDSIPOINTER	GRAB ADDRESS OF RDSI THAT DIDN'T MATCH
READSECTORNM	; THIS RDSI ISN'T IT!
	LDX	RDSI:FLINK,X	FIND NEXT RDSI
	CPX	#LASTSECTORREADQ	END OF RDSI LIST?
	BNE	READSECTORL	B/ NO, CHECK NEXT RDSI
*	MUST DO DISK I/O
*	0) VERIFY THAT LSN IS LEGAL FOR THIS DRIVE
*	1) WAIT FOR DISK SPECIFIED BY TARGET READ BUFFER TO BE DONE
*	2) WRITE TARGET READ BUFFER CONTENTS BACK TO DISK IF MODIFIED
*	3) WAIT FOR DISK OF DESIRED SECTOR TO BE DONE
*	4) ISSUE READ TO GET DISK SECTOR
READSECTORV	; VERIFY LSN IS LEGAL FOR THIS DRIVE
	LDX	READSECTORDBP	GET THE SECTOR NUMBER...
	LDAA	SECTORDB:LSN,X
	PSHA
	LDD	SECTORDB:LSN+1,X
	LDX	RDSI:DISKINFO,X	SUBTRACT NLSN FROM IT..
	BEQ	READSECTORVD	B/ READ FROM DUMMY DISK, DON'T DO ILLEGAL LSN CHECK
	SUBD	DSKINFO:NLSN+1,X
	PULA
	SBCA	DSKINFO:NLSN,X
	BCS	READSECTORVD1	B/ LSN IS LEGAL FOR TARGET DISK
ERRILLLSN
	JSR	ERRET
	FDB	ERR:ILLLSN
	PAGE
READSECTORVD	; READ FROM DUMMY (VIRTUAL) DISK
	LEAS	1,S	POP MSB OF LSN SINCE WE DON'T NEED TO DO ILLEGAL LSN CHECK
READSECTORVD1
	LDX	#LASTSECTORREADQ	FIND AVAILABLE SECTOR BUFFER
READSECTOR1	EQU	*
	LDX	RDSI:BLINK,X	FIND DCB ADDRESS OF DISK OF LAST SECTOR BUFFER IN QUEUE
	LDAA	RDSI:STATE,X	WE ONLY NEED TO WAIT IF TARGET BUFFER IS BUSY...
	ORAA	RDSI:MODIFIED,X	OR IT NEEDS TO BE WRITTEN TO THE DISK
	BEQ	READSECTOR3	B/ DON'T NEED TO WAIT FOR TARGET BUFFER
	LDX	RDSI:DISKINFO,X	GOT IT! (ASSERT: RDSI:DSKINFO <> 0!)
	LDX	DSKINFO:SECTORDB,X	IS TARGET BUFFER'S DISK BUSY ?
	BEQ	READSECTOR2	B/ NO, SEE IF TARGET SECTOR BUFFER IS MODIFIED
	STX	RDSIPOINTER	YES, WAIT FOR IT
	JSR	WAITRDSI	...
READSECTOR2	; CHECK TO SEE IF TARGET SECTOR BUFFER IS MODIFIED
	LDX	LASTSECTORREADQ+RDSI:BLINK	GET ADDRESS OF TARGET RDSI
READSECTOR2L	; WRITE FROM POOL, OLDEST BUFFER TO NEWEST BUFFER,
*	UNTIL DIFFERENT DISK ENCOUNTERED, BUFFER NOT MODIFIED,
*	OR RDSI:CYLINDER DOESN'T MATCH
	LDAA	RDSI:MODIFIED,X	IS IT MODIFIED ?
	BEQ	READSECTOR3	B/ NO, GO WAIT FOR DESIRED DISK TO BECOME FREE
	STX	RDSIPOINTER	YES, ISSUE THE WRITE
	LDAA	#RDSISTATE:WRITING	WHICH MOVES SECTOR BACK TO DISK
	STAA	RDSI:STATE,X	AND FREES UP THIS SECTOR BUFFER
	CLR	RDSI:MODIFIED,X	MARK SECTOR AS UNCHANGED (TAKES EFFECT AFTER WRITE)
	JSR	DISKWRITE	ISSUE THE ACTUAL WRITE COMMAND
	BCS	WAITSECTORERRJ	B/ DISK I/O ERROR OCCURRED
	LDX	RDSIPOINTER	WAIT FOR WRITE TO FINISH
	JSR	WAITSECTOR1	OLD SECTOR CONTENTS ARE NOW GONE!!
	LDX	RDSIPOINTER	SEE IF NEXT OLDEST SECTOR IN POOL NEEDS WRITING
	LDD	RDSI:CYLINDER,X	ONLY WRITE ANOTHER SECTOR IF IN SAME PHYSICAL CYLINDER
	LDX	RDSI:BLINK,X	FIND NEXT OLDEST SECTOR
	CPX	#LASTSECTORREADQ	EXHAUSTED POOL ?
	BEQ	READSECTOR3	B/ ALL MOD'D SECTORS IN POOL WRITTEN TO DISK
	CMPD	RDSI:CYLINDER,X	NO, IS NEXT OLDEST SECTOR IN SAME CYLINDER ?
	BEQ	READSECTOR2L	B/ YES, GO SEE IF MODIFIED AND THEN WRITE IT!
**** THIS LOGIC SHOULD ISSUE JUST WRITES, FOLLOWED BY JUST VERIFIES!
READSECTOR3	; WAIT FOR DESIRED DISK TO BE DONE
	LDX	READSECTORDBP	GET DCB ADDRESS OF DESIRED DISK
	LDX	SECTORDB:DISKINFO,X	...
	BEQ	READSECTOR4	B/ WISH TO READ FROM DUMMY DISK, IT'S DONE...
	LDX	DSKINFO:SECTORDB,X	GET POINTER TO SECTOR BEING PROCESSED
	BEQ	READSECTOR4	B/ DESIRED DISK IS IDLE
	STX	RDSIPOINTER	WAIT FOR DESIRED DISK TO FINISH I/O
	JSR	WAITRDSI	...
READSECTOR4	; NOW (AT LAST!) READ IN THE DESIRED SECTOR
	LDX	READSECTORDBP	GET POINTER TO DESIRED SECTOR ADDRESS
	LDD	SECTORDB:DISKINFO,X	COPY DISKINFO TO (TOS-1,TOS)
	PSHD
	LDAA	SECTORDB:LSN,X	COPY DESIRED SECTOR # TO (TOS),(A,B)
	PSHA
	LDD	SECTORDB:LSN+1,X
	LDX	LASTSECTORREADQ+RDSI:BLINK	GET POINTER TO CHOSEN RDSI AGAIN
	STX	RDSIPOINTER	AND REMEMBER IT FOR EVERYBODY'S CONVENIENCE
	STD	RDSI:LSN+1,X	STORE SECTOR DESCRIPTION INTO RDSI
	PULA
	STAA	RDSI:LSN,X
	PULD
	STD	RDSI:DISKINFO,X
	LDAA	#$FF	MARK RDSI AS "REQUIRES LSN TO PHYSICAL" MAPPING
	STA	RDSI:TRACK,X	(DRIVER CHANGES RDSI:SECTOR, :TRACK, :CYLINDER BY DOING MAPPING)
	CLR	RDSI:CYLINDER,X	SET CYLINDER TO CONSTANT...
	CLR	RDSI:CYLINDER+1,X	IN CASE DRIVER DOES NOT FILL IN CYLINDER NUMBER
	LDAA	READFLAG	INVENTING A SECTOR ? (RDSI:STATE = "IDLE" WHEN WE GET HERE)
	BEQ	READSECTORI	B/ YES, DON'T BOTHER READING IT IN
	LDAA	#RDSISTATE:READING	MARK SECTOR AS BEING READ
	STAA	RDSI:STATE,X
	JSR	DISKREAD	INITIATE SECTOR FETCH
	BCC	READSECTORR	B/ NO ERROR IN STARTING THE DISK I/O
WAITSECTORERRJ	EQU	*
	JMP	WAITSECTORERRED

READSECTORI	; INVENTING A SECTOR
	JSR	WAITRDSI	IN THE RARE CASE THAT WE WISH TO INVENT...
*			A SECTOR THAT SOMEBODY DID A READ-AHEAD ON!
READSECTORR	EQU	*
	BSR	REMOVERDSI	DELETE ALL OLD REFERENCES TO RDSI
READSECTORF	; WE FOUND IT !
	LDX	RDSIPOINTER	MAKE THIS SECTOR THE MOST RECENTLY READ
	BSR	REQUERDSI1
	LDX	RDSIPOINTER	EXIT WITH POINTER IN (X)...
	LDD	RDSIPOINTER	AND (A,B)
	OKRTS
	PAGE
*	REMOVERDSI -- REMOVE RDSIPOINTER FROM ALL IOCBS...
*	THAT HAVE REFERENCES TO IT
*
REMOVERDSI	EQU	*
	LDX	CODE+SDOS:CONFIGURATION
	LDAA	CNFG:NIOCHANNELS,X	FIND OUT HOW MANY IOCBS TO SCAN
	STAA	REMOVESECTORCNT
	LDX	CNFG:IOCBPOINTERS,X	SET UP TO SCAN IOCB POINTER TABLE
	LDD	RDSIPOINTER	GET TARGET RDSI ADDRESS TO (A,B)
REMOVERDSIL	EQU	*
	STX	REMOVEIOCBPTR	SAVE IOCB POINTER LIST SCANNER
	LDX	,X	GET REAL IOCB POINTER
	BSR	READSECTORZAPIOCB	REMOVE RDSI FROM IOCB
	LDX	REMOVEIOCBPTR	GET LIST SCAN POINTER
	LEAX	2,X	BUMP TO NEXT IOCB POINTER
	DEC	REMOVESECTORCNT	DOWN COUNT # IOCBS TO CHECK
	BNE	REMOVERDSIL	B/ MORE TO CHECK
	LDX	#SYSIOCB	CHECK SYSIOCB OUT, TOO!
	BSR	READSECTORZAPIOCB	REMOVE RDSI FROM IT IF IN USE
	LDX	#LOGIOCB	REMOVE RDSI FROM LOG FILE IF IN USE
	BSR	READSECTORZAPIOCB
	LDX	RDSIPOINTER	FOR THE CONVENIENCE OF THE CALLER
	RTS
	PAGE
*	READSECTORZAPIOCB -- REMOVE MATCHING RDSI POINTER REFERENCES
*	(A,B) ON ENTRY CONTAIN RDSI ADDRESS TO REMOVE FROM IOCB(X)
*
READSECTORZAPIOCB	EQU	*
	STX	TEMPX	SAVE IOCB TO TEST
	LDX	IOCB:DRIVER,X	FIND OUT WHICH DRIVER OWNS THIS IOCB
	CPX	#DISKFILEDRIVER	DO HIGH PROBABILITY TEST FIRST
	BEQ	READSECTORZAPH	B/ DISK FILE DRIVER, CHECK HRDSI
	CPX	#DISKDEVICEDRIVER	LOW PROBABILITY TEST
	BEQ	READSECTORZAPD	B/ DISK DEVICE DRIVER, JUST CHECK DRDSI
	RTS		NOT OPEN TO A DISK DRIVER, JUST QUIT!

READSECTORZAPH	; CHECK HRDSI OF IOCB
	LDX	TEMPX
	CMPD	IOCB:HRDSI,X	(A,B) MATCH HRDSI ?
	BNE	READSECTORZAPD1	B/ NO
	CLR	IOCB:HRDSI,X	YES, MARK HRDSI AS INVALID
	CLR	IOCB:HRDSI+1,X
READSECTORZAPD	EQU	*
	LDX	TEMPX	RESTORE (X) TO IOCB POINTER
READSECTORZAPD1	; ENTER HERE IF (X) IS ALREADY SET UP
	CMPD	IOCB:DRDSI,X	(A,B) MATCH DRDSI ?
	BNE	READSECTORZAPRTS	B/ NO
	CLR	IOCB:DRDSI,X	YES, MARK DRDSI AS INVALID
	CLR	IOCB:DRDSI+1,X
READSECTORZAPRTS	EQU	*
	RTS
	PAGE
*	REQUERDSI -- REQUE RDSI(X) ONTO TOP OF QUEUE
*	USED TO IMPLEMENT LRU POLICY OF DISK SECTOR BUFFER MANAGEMENT
*
REQUERDSI	; FIRST, TAKE IT OUT OF THE QUEUE
	STX	RDSIPOINTER	SAVE THE RDSI ADDRESS
REQUERDSI1	; IF RDSIPOINTER AND (X) ARE ALREADY SET UP
	LDD	RDSI:FLINK,X	MAKE FLINK(BLINK(X))=FLINK(X)
	LDX	RDSI:BLINK,X
	STD	RDSI:FLINK,X
	LDX	RDSIPOINTER	MAKE BLINK(FLINK(X))=BLINK(X)
	LDD	RDSI:BLINK,X
	LDX	RDSI:FLINK,X
	STD	RDSI:BLINK,X
	LDX	RDSIPOINTER	SO WE CAN DO THE ENQUE
*	BSR	ENQUELASTSECTORREAD
*	RTS
*
*	ENQUELASTSECTORREAD -- ADD RDSI(X) TO TOP OF LASTSECTORREADQ
*
ENQUELASTSECTORREAD	EQU	*
	LDD	LASTSECTORREADQ+RDSI:FLINK
	STD	RDSI:FLINK,X	MAKE FLINK(X) := FLINK(QHEAD)
	LDD	#LASTSECTORREADQ	MAKE BLINK(X) := ADDRESS(QHEAD)
	STD	RDSI:BLINK,X
	STX	LASTSECTORREADQ+RDSI:FLINK	MAKE FLINK(QHEAD) := X
	LDD	LASTSECTORREADQ+RDSI:FLINK	BLINK(FLINK(X)) := X
	LDX	RDSI:FLINK,X
	STD	RDSI:BLINK,X
	RTS		AND EXIT
	PAGE
*	FETCHSECTOR -- INITIATE READ FOR AND THEN WAIT FOR SECTOR ARRIVAL
*	(X) POINTS TO SECTOR DESCRIPTOR
*
FETCHSECTOR	EQU	*
	JSR	READSECTOR	GO START THE SECTOR READ
	BRA	WAITSECTOR1	THEN GO WAIT FOR IT TO ARRIVE
*
*	WAITSECTOR -- WAIT FOR SECTOR WHOSE RDSI IS IN (X)
*	SETS RDSIPOINTER TO (X)
*	MOVES RDSI TO TOP OF LRU QUEUE
*	DOES WAIT ONLY IF RDSI SAYS "NOT IDLE"
*	RETURNS (X) = RDSIPOINTER
*
WAITSECTOR	EQU	*
	BSR	REQUERDSI	MOVE RDSI TO TOP OF LRU QUEUE
WAITRDSI	; WAIT FOR RDSI SELECTED BY RDSIPOINTER
	LDX	RDSIPOINTER	CHECK OUT STATE OF RDSI
	LDAA	RDSI:DISKINFO,X	DUMMY DISKINFO TABLE ?
	BEQ	WAITSECTORRTS	B/ YES, IGNORE IT.
	LDAA	RDSI:STATE,X	IDLE ?
	BEQ	WAITSECTORRTS	B/ YES, TAKE QUICK EXIT!
WAITSECTOR1	; WAIT FOR ARRIVAL OF SECTOR
	CLR	RDSI:STATE,X	RESET SECTOR STATE TO "IDLE"
	JSR	DISKWAITD	WAIT FOR DISK TRANSFER COMPLETED
	BCS	WAITSECTORERRED	B/ WE GOT A PROBLEM!
	LDX	RDSIPOINTER	SO (X) IS CORRECT ON EXIT
WAITSECTORRTS	EQU	*
	OKRTS

WAITSECTORERRED	; DISK I/O ERROR OCCURRED
	PSHX		SAVE THE ERROR CODE!
	LDX	RDSIPOINTER
	CLR	RDSI:DISKINFO,X	CAN'T DO I/O, LSN IN RDSI IS NOW A LIE!
	CLR	RDSI:DISKINFO+1,X	MARK RDSI AS COMING FROM "MAGIC 0" DISK
	LDAA	#1	(BECAUSE WE WANT SOMETHING NON-ZERO)
	STAA	RDSI:LSN,X	MAKE LSN <> 0 SO WE DON'T THINK ITS THE MAGIC 0 SECTOR
	CLR	RDSI:STATE,X	MARK SECTOR AS BEING INACTIVE
	CLR	RDSI:MODIFIED,X	THIS SHOULD BE REDUNDANT, BUT MAKES ME FEEL SAFE!
	JSR	REMOVERDSI	REMOVE ANY REFERENCES TO RDSI FROM IOCBS
	PULX		GET THE ERROR CODE BACK...
	JMP	ERRORINX	GO TELL SOMEBODY WE HAD A PROBLEM!
	PAGE
*
*	DSKDUMPBUFFERS -- WRITE MODIFIED SECTORS BACK TO DISK
*		WRITE CONTENTS OF MODIFIED FCBS BACK TO CORRESPONDING DIRECTORY ENTRIES
*		WRITES ONLY RDSI'S WHOSE DISKINFO POINTER MATCHES "DCBPOINTER"
*		ALSO FORCES CONTENTS OF MATCHING DISK FCB'S BACK TO DIRECTORY SECTORS
*		ERRET TAKEN IF SOME FCB IS FOR A NEWLY CREATED FILE
*			SECTORS ARE DUMPED ANYWAY
*		WAIT FOR ALL DISK I/O DONE
*		DSKDUMPBUFFERS IS GENERALLY DONE WHEN MAPALGORITHM IS ABOUT TO BE CHANGED
*
DSKDUMPBUFFERS	EQU	*
	LDAA	NFCBS	GET # FCBS TO SEARCH
	STAA	COUNT	AND SAVE AS LOOP COUNTER
	LDX	CODE+SDOS:CONFIGURATION	NOW SCAN ALL THE FCBS
	LDD	[CNFG:IOCBPOINTERS,X]	FIND ADDRESS OF FIRST FCB
DSKDUMPFCBL	EQU	*
	SUBD	#FCB:SIZE	=ADDRESS OF PREVIOUS FCB - FCB:SIZE
	STD	FCBPOINTER	STORE FCB ADDRESS
	LDX	FCBPOINTER	IS THIS FCB ACTIVE ?
	LDAA	FCB:REFCOUNT,X	... ?
	BEQ	DSKDUMPFCB1	B/ NO, IGNORE IT
	LDAA	FCB:FLAGS,X	YES, GET FILE STATUS FLAGS
	BITA	#FCBFLG::NEWFILE	A NEW FILE ?
	BNE	DSKDUMPFCB1	B/ YES, DON'T UPDATE DIRECTORY
	LDX	FCB:DISKINFO,X	IS THIS FCB FOR REQUESTED DISK ?
	CPX	DCBPOINTER	...?
	BNE	DSKDUMPFCB1	B/ NO, IGNORE IT
	LDAA	COUNT		SAVE # FCBS TO LOOK AT
	PSHA
	JSR	UPDATEDIRFROMFCB	YES, UPDATE THE DIRECTORY
	BCS	DSKDUMPERR1	B/ ERROR OCCURRED, YUK!
	PULA		RESTORE COUNT
	STAA	COUNT
DSKDUMPFCB1	; THROUGH WITH THIS FCB
	LDD	FCBPOINTER	GET FCB ADDRESS
	DEC	COUNT
	BNE	DSKDUMPFCBL	B/ MORE FCBS TO SEARCH
*	WE CANNOT DO A DSKDUMPBUFFER SAFELY UNLESS WE KNOW
*	THAT ALL I/O ON THE DRIVE (SUCH AS READ-AHEAD) IS COMPLETED.
	LDX	DCBPOINTER	COMPLETE ANY I/O ON THIS DRIVE
	LDX	DSKINFO:SECTORDB,X	I/O ACTIVE ON THIS DISK DRIVE ?
	BEQ	DSKDUMPBUF0	B/ NO
	STX	RDSIPOINTER	IN CASE WE GO TO WAITSECTORERRED
	JSR	WAITRDSI	NOW WAIT FOR I/O OPERATION COMPLETE
DSKDUMPBUF0	; I/O ON DISK DRIVE IS COMPLETED
	LDX	LASTSECTORREADQ+RDSI:FLINK	SCAN RDSI LIST
DSKDUMPBUFL	EQU	*
	STX	RDSIPOINTER	YES, SAVE POINTER TO RDSI'S
	LDX	RDSI:DISKINFO,X	CORRECT DISK ?
	CPX	DCBPOINTER	...?
	BNE	DSKDUMPBUFN	B/ NO, IGNORE RDSI
	LDX	RDSIPOINTER	NOW DO A WRITE...
	LDAA	RDSI:MODIFIED,X	IF THIS SECTOR MODIFIED
	BEQ	DSKDUMPBUFN	B/ NO, LEAVE RDSI ALONE
	CLR	RDSI:MODIFIED,X	FORCE TRANSITION FROM "MODIFIED" TO "BEING WRITTEN"
	LDAA	#RDSISTATE:WRITING	MARK RDSI AS BEING WRITTEN
	STAA	RDSI:STATE,X
	BSR	DISKWRITE	OF MODIFIED SECTOR
	BCS	WAITSECTORERRJ2	B/ RATS, WE GOT AN I/O ERROR
	JSR	WAITRDSI	NOW WAIT FOR DISK I/O COMPLETE
DSKDUMPBUFN	EQU	*
	LDX	RDSIPOINTER
	LDX	RDSI:FLINK,X	FIND NEXT RDSI
	CPX	#LASTSECTORREADQ	HIT END OF RDSI LIST?
	BNE	DSKDUMPBUFL	B/ NO, GO CHECK NEXT RDSI
	OKRTS		NO

DSKDUMPERR1
	INS		THROW AWAY THE SAVED COUNT
	JMP	ERRORED	AND PASS THE ERROR ON...

WAITSECTORERRJ2	EQU	*
	JMP	WAITSECTORERRED

	PAGE
	IF	SWAPPING	WHICH WE'RE NOT!
*	READDIRECT -- READ IOCB:LSN INTO BUFFER(IOCB:BUFFERP)
*		IF IOCB:LSN DOES NOT EXIST, ZEROS NBPS BYTES
*		IOCB:LOCATEDF MUST BE SET ON ENTRY
*
READDIRECT	EQU	*
	LDX	IOCBPOINTER	CHECK VALIDITY OF CURLSN
	LDX	IOCB:CURLCN,X	IF CURLCN IS VALID,
	INX		SO IS CURLSN
	BNE	READDIRECT1	B/ CURLCN IS VALID (<>-1)
	LDX	DCBPOINTER	OOPS, NO SUCH CLUSTER!
	CLRA		ZERO A SECTOR'S WORTH
	CLRB		GET -NBPS TO (A,B)
	SUBD	DSKINFO:NBPS,X
	LDX	IOCBPOINTER
	LDX	IOCB:BUFFERP,X
	JMP	STAASECTORL	AND LET ZERO SECTOR DO THE WORK

READDIRECT1	; VALID CLUSTER # ENCOUNTERED
	LDX	IOCBPOINTER	SO READ THE LSN INDICATED
*	BSR	DISKREAD
*	OKRTS
	FIN
	PAGE
*
*	DISKREAD -- INITIATE READ DISK SECTOR SPECIFIED BY SECTORDB(X)
*	NOTE: DISKREAD, DISKWRITE, AND DISKWAITD ROUTINES MUST NOT BE
*	CALLED WITH SECTORDB FOR DUMMY DISK!
*	ASSERT: DSKINFO:SECTORDB = 0
*	PRESERVES DCBPOINTER
*
*
DISKREAD	EQU	*
	BSR	DISKRWCOPYARGS	COPY ARGUMENTS TO INFO BLOCK
	LDD	DRIVER:DISKREAD,X	GET ADDRESS OF DISKREAD ENTRY POINT TO (A,B)
	BSR	SECTORDRIVER	SET (X) = DCBPOINTER AND PASS CONTROL TO DISKREAD
	BCC	DISKDCBUNSAVE	B/ NO ERROR
	BRA	DISKWAITE	B/ WE GOT AN ERROR, YUK
*
*	DISKWRITE -- INITIATE WRITE DISK SECTOR OF SECTORDB(X)
*	ASSERT: DSKINFO:SECTORDB = 0
*	PRESERVES DCBPOINTER
*
DISKWRITE	EQU	*
	BSR	DISKRWCOPYARGS
	LDD	DRIVER:DISKWRITE,X	GET ADDRESS OF DISKWRITE ROUTINE TO (A,B)
	BSR	SECTORDRIVER	SET (X) = DCBPOINTER AND PASS CONTROL TO (A,B)
	BCC	DISKDCBUNSAVE	B/ NO ERROR
	BRA	DISKWAITE	B/ WE GOT AN ERROR!
*
*	DISKWAITD -- WAIT FOR DISK I/O COMPLETE ON DRIVE SECTORDB(X)
*	PRESERVES DCBPOINTER
*
DISKWAITD	EQU	*
	BSR	DISKDCBSAVE	SAVE CURRENT VALUE OF DCB AND SET TO SECTORDB:DISKINFO
	LDX	DCB:DRIVER,X	GET ADDRESS OF DISKWAIT ROUTINE TO (A,B)
	LDD	DRIVER:DISKWAIT,X
	BSR	SECTORDRIVER	SET (X) = DCBPOINTER AND PASS CONTROL
	BCC	DISKWAITD2	B/ NO ERROR
DISKWAITE	; DISK I/O ERROR OCCURRED
	JSR	ERRORSAVE	SAVE THE ERROR CODE
	CPX	#ERR:DISKREAD	CHECK FOR "CAN'T GET SECTOR" ERROR
	BEQ	DISKWAITE1	B/ CAN'T READ IT...
	CPX	#ERR:DISKWRITE	CHECK FOR "CAN'T CHANGE IT" ERROR
	BNE	DISKWAITE2	B/ ISN'T READ OR WRITE ERROR, DON'T MARK SECTOR AS BAD
DISKWAITE1	; DISK READ OR DISK WRITE ERROR, MARK SECTOR AS BAD
	LDX	DCBPOINTER	SAVE BAD LSN FROM RDSI
	LDX	DSKINFO:SECTORDB,X	COPY BAD LSN TO (TOS,A,B)
	LDAA	SECTORDB:LSN,X
	PSHA
	LDD	SECTORDB:LSN+1,X
	LDX	DCBPOINTER
	STD	DSKINFO:BADLSN+1,X
	PULA
	STAA	DSKINFO:BADLSN,X
DISKWAITE2	; FINISH UP ERROR HANDLING
	BSR	DISKWAITD2	MARK DISK DRIVER AS "NOT BUSY"
	JMP	ERRORED	AND GO CROAK

DISKWAITD2	; MARK DISK DRIVER AS "NOT BUSY"
	LDX	DCBPOINTER	BY ZEROING DSKINFO:SECTORDB
	CLR	DSKINFO:SECTORDB,X
	CLR	DSKINFO:SECTORDB+1,X
DISKDCBUNSAVE	; RESTORE DCB POINTER TO ORIGINAL VALUE
	LDX	DCBPTRSAVE	NEED I SAY MORE?
	STX	DCBPOINTER	(THIS CODE MUST PRESERVE THE CARRY BIT!)
	RTS
	PAGE
*
*	DISKRWCOPYARGS -- COPY SECTORDB ADDRESS TO DISKINFO BLOCK
*		SAVES CURRENT VALUE OF DCBPOINTER; SETS DCBPOINTER TO SECTORDB:DISKINFO,X
*
DISKRWCOPYARGS	EQU	*
	BSR	DISKDCBSAVE	SAVE CURRENT DCB AND SET UP FROM SECTORDB:
	LDD	TEMPX	INTO DISKINFO TABLEI SO WE CAN FIGURE OUT WHICH RDSI GOT AN ERROR
	STD	DSKINFO:SECTORDB,X	WHEN WE DO A DISKWAITD!
	LDX	DCB:DRIVER,X	GET DRIVER VECTOR ENTRY POINT TO (X)
	RTS
*
*	DISKDCBSAVE -- SAVE CURRENT VALUE OF DCBPOINTER
*		SET TEMPX TO (X)
*		SET DCBPOINTER TO SECTORDB:DISKINFO,X
*		PRESERVE (X)
*
DISKDCBSAVE	EQU	*
	STX	TEMPX	SAVE POINTER TO SECTORDB:...
	LDX	DCBPOINTER	SAVE THE CURRENT VALUE OF THE DCBPOINTER
	STX	DCBPTRSAVE
	LDX	TEMPX	GET SECTORDB: POINTER AGAIN
	LDX	SECTORDB:DISKINFO,X	AND SET UP DCB POINTER FROM SECTORDB:
	STX	DCBPOINTER
	RTS
*
*	SECTORDRIVER -- LOADS (X) WITH DCBPOINTER; PASS CONTROL TO (A,B)
*
SECTORDRIVER	EQU	*
	PSHD		SET UP FAKE RETURN ADDRESS
	LDX	DCBPOINTER	AS PROMISED
	RTS		PASS CONTROL
	PAGE	BLOCK MOVE UP ROUTINE
	IF	M6800!M6801
LIMIT	EQU	4	TRANSFER LIMIT ADDRESS
BLOCKMOVEX	EQU	6	SCRATCH PAD LOCATIONS

BLOCKMOVE	; PRESERVES DCBPOINTER
*	BLOCKMOVEDOWN -- MOVE BLOCK AT (X) TO (Y) FOR (D) BYTES
*	(Y) = LOCATION ZERO
*	ON EXIT, (X) IS OLD (X)+(D)
*		 (Y) IS OLD (Y)+(D)
*	COPIES LARGE BLOCKS AT 17uS. PER BYTE
*	ASSUMES COPY-TO REGION DOES NOT OVERLAP COPY-FROM REGION
*	OR THAT "FROM" >= "TO"
*
BLOCKMOVEDOWN	EQU	*
	STX	FROMPOINTER	SAVE WHERE TO COPY FROM
	ADDD	FROMPOINTER	COMPUTE ADDRESS OF BYTE PAST END OF FROM RE
	STD	LIMIT	SAVE AS LIMIT ADDRESS
	LDD	DCBPOINTER	SAVE DCB POINTER
	PSHD
	LDB	LIMIT+1	RESTORE (B)...
	SUBB	FROMPOINTER+1	(B):= COUNT MOD 256
	BITB	#%00000001	GOING TO MOVE TO AN EVEN NUMBER OF BYTES ?
	BEQ	BLOCKMOVEDOWNE	B/ YES
	LDA	,X+	FETCH ODD BYTE FROM "FROM" AREA
	STX	FROMPOINTER	(TO COPY ODD BYTE TAKES 37 CYCLES)
	LDX	TOPOINTER	STORE BYTE INTO "TO" AREA
	STA	,X+
	STX	TOPOINTER
	LDX	FROMPOINTER	GET SET TO MOVE BYTE PAIR
BLOCKMOVEDOWNE	; EVEN NUMBER OF BYTES LEFT TO MOVE
	BITB	#%00000010	TWO BYTES LEFT TO MOVE BEFORE MULTIPLE OF 4 REACHED ?
	BEQ	BLOCKMOVEDOWNA	B/ NO, READY TO MOVE MULTIPLES OF 4 BYTES
	LDD	,X++	FETCH BYTE PAIR
	STX	FROMPOINTER	(TO COPY BYTE PAIR TAKES 28 CYCLES/BYTE)
	LDX	TOPOINTER	WHERE TO PUT BYTE PAIR
	STD	,X++
	STX	TOPOINTER
	LDX	FROMPOINTER	GET SET TO MOVE 4 BYTES AT A TIME
BLOCKMOVEDOWNA	LDAB	FROMPOINTER+1	DO WE STILL HAVE TO MOVE A MULTIPLE OF 16 BYTES
	SUBB	LIMIT+1	(B):= COUNT MOD 256
	BITB	#%00001111	....?
	BEQ	BLOCKMOVEDOWNB	B/ YEP.
*
*	MOVE 4 BYTES AT A TIME UNTIL A MULTIPLE OF 16 IS LEFT TO MOVE
*	COPY RATE IS 23.5 uS. PER BYTE
*
BLOCKMOVEDOWN4	LDD	2,X	GET 2ND AND 3RD BYTE...
	LDX	,X	AND 1ST AND 2ND BYTES FROM THE "FROM" AREA
	STX	BLOCKMOVEX	SAVE 1ST AND 2ND BYTES
	LDX	TOPOINTER	NO STORE 4 BYTES TO "TO" AREA
	STD	2,X	STORE 2ND AND 3RD BYTE
	LDD	BLOCKMOVEX
	STD	,X	STORE 1ST AND SECOND BYTES
	LDAB	TOPOINTER+1	ADVANCE POINTERS BY 4 BYTES
	ADDB	#4
	STAB	TOPOINTER+1
	BCC	*+5
	INC	TOPOINTER
	LDAB	FROMPOINTER+1
	ADDB	#4
	STAB	FROMPOINTER+1
	BCC	*+5
	INC	FROMPOINTER
	LDX	FROMPOINTER	SET UP FOR NEXT LOOP ITERATION
	SUBB	LIMIT+1	MULTIPLE OF 16 BYTES LEFT TO MOVE ?
	BITB	#%00001111	....?
	BNE	BLOCKMOVEDOWN4	B/ NOPE, MOVE ANOTHER 4 BYTES
BLOCKMOVEDOWNB	CPX	LIMIT	YES, ALL DONE MOVING BYTES ?
	BEQ	BLOCKMOVEDOWND	B/ YES, LEAVE!
*
*	MOVE 16 BYTES AT A TIME UNTIL TRANSFER IS COMPLETE
*	COPY RATE IS 17.1 uS. PER BYTE
*
BLOCKMOVEDOWN16	EQU	*
	LDD	0+2,X	COPY 1ST 4 BYTES
	LDX	0+0,X
	STX	BLOCKMOVEX
	LDX	TOPOINTER
	STD	0+2,X
	LDD	BLOCKMOVEX
	STD	0+0,X
	LDX	FROMPOINTER	COPY 2ND GROUP OF 4 BYTES
	LDD	4+2,X
	LDX	4+0,X
	STX	BLOCKMOVEX
	LDX	TOPOINTER
	STD	4+2,X
	LDD	BLOCKMOVEX
	STD	4+0,X
	LDX	FROMPOINTER	COPY 3RD GROUP OF 4 BYTES
	LDD	8+2,X
	LDX	8+0,X
	STX	BLOCKMOVEX
	LDX	TOPOINTER
	STD	8+2,X
	LDD	BLOCKMOVEX
	STD	8+0,X
	LDX	FROMPOINTER	COPY 4TH GROUP OF 4 BYTES
	LDD	12+2,X
	LDX	12+0,X
	STX	BLOCKMOVEX
	LDX	TOPOINTER
	STD	12+2,X
	LDD	BLOCKMOVEX
	STD	12+0,X
	LDAB	TOPOINTER+1	ADVANCE POINTERS BY 16 BYTES
	ADDB	#16
	STAB	TOPOINTER+1
	BCC	*+5
	INC	TOPOINTER
	LDAB	FROMPOINTER+1
	ADDB	#16
	STAB	FROMPOINTER+1
	BCC	*+5
	INC	FROMPOINTER
	LDX	FROMPOINTER	CHECK TO SEE IF COPY IS COMPLETE
	CPX	LIMIT	AT LIMIT OF "FROM" REGION ?
	BNE	BLOCKMOVEDOWN16	B/ NO, GO MOVE ANOTHER 16 BYTES
BLOCKMOVEDOWND	; BLOCK TRANSFER IS COMPLETE!
	PULD		RESTORE DCBPOINTER
	STD	DCBPOINTER
	RTS
	PAGE
	ELSE	(M6809)
	PAGE
BLOCKMOVE	; ENTRY POINT TO BLOCKMOVE FOR '09, PRESERVES DCBPOINTER
*	BLOCKMOVEDOWN -- 6809 VERSION (5 uS./byte, average)
*	(X) = from address
*	(Y) = to address
*	(D) = count (0..65535)
*	On exit, (X) has old (X)+(D); (Y) has old (Y)+(D)
*	ASSUMES THAT COPY-TO REGION DOES NOT OVERLAP COPY-FROM REGION,
*	OR THAT "FROM" >= "TO".
*
BLOCKMOVEDOWN
	LEAU	D,X	COMPUTE END OF TRANSFER ADDRESS
	PSHS	U	SAVE ON STACK
	BITB	#1	TAKE CARE OF "ODD" BYTE
	BEQ	BLOCKMOVEDOWN0
	LDAA	,X+	MOVE ODD BYTE
	STAA	,Y+	THEN WE CAN IGNORE LSB OF D
BLOCKMOVEDOWN0
	COMB		MAP BITS 3-1: 0->,1->6,...,N->7-N
	ANDB	#%1110	BRANCH INTO COPY LOOP...
	ASLB		AT PROPER POINT
	LDU	#BLOCKMOVEDOWNLOOP+4
	JMP	B,U	MULTIPLE OF 16 TAKES US TO CMPX 0,S
	PAGE
BLOCKMOVEDOWNLOOP
	LDD	,X++	(CAN'T GET HERE DIRECTLY FROM JMP B,U)
	STD	,Y++
	LDD	,X++	1110
	STD	,Y++
	LDD	,X++	1100
	STD	,Y++
	LDD	,X++	1010
	STD	,Y++
	LDD	,X++	1000
	STD	,Y++
	LDD	,X++	0110
	STD	,Y++
	LDD	,X++	0100
	STD	,Y++
	LDD	,X++	0010
	STD	,Y++
	CMPX	0,S	0000
	BNE	BLOCKMOVEDOWNLOOP
	LEAS	2,S	POP LIMIT ADDRESS
	RTS
	FIN	M6800!M6801
	PAGE	CLOCK TICK AND TIMEOUT ROUTINES
*	TASKING AND INTERRUPT HANDLING FOR SDOS
*
*	CLOCKTICKED -- BUMPS SDOS:CLOCK
*	ADJUSTS THE DATE IF NEEDED
*	WAKES UP TIMEOUT TASK
*	INVOKED AS EXIT OF SOME INTERRUPT ROUTINE
*	(A) ON ENTRY = # CLOCK TICKS (60THS SEC) ELAPSED SINCE LAST CALL
*	RUNS WITH INTERRUPTS DISABLED
*
CLOCKTICKED	EQU	*
	TAB
	ADDA	CLOCKTICKEDEVENT	SET UP WAKEUP COUNT FOR TIMEOUT TASK
	STAA	CLOCKTICKEDEVENT
	ADDB	CODE+SDOS:CLOCK+2	ADJUST TIME-OF-DAY...
	STAB	CODE+SDOS:CLOCK+2	AS MEASURED IN 60THS OF A SECOND
	BCC	CLOCKTICK1	B/ DON'T NEED TO UPDATE UPPER 2 BYTES
	LDX	CODE+SDOS:CLOCK	UPDATE UPPER 2 BYTES
	INX
	STX	CODE+SDOS:CLOCK
CLOCKTICK1	; DID WE JUST PASS MIDNITE ?
	LDAA	CODE+SDOS:CLOCK+1	IS CODE+SDOS:CLOCK - "MIDNITE" >= 0 ?
	SUBD	#6656	(6656 = 5184000 MOD 65536; TRY IT!)
	LDAA	CODE+SDOS:CLOCK
	SBCA	#79	( = 5184000 / 65536 )
	BCS	CLOCKTICKEXIT	B/ NOT MIDNITE, GET OUT QUICK!
	LDX	#0	JUST PAST MIDNITE, RESET THE CLOCK
	STX	CODE+SDOS:CLOCK	SET CLOCK:=CLOCK MOD (1 DAY)
	STAB	CODE+SDOS:CLOCK+2
	LDAA	CODE+SDOS:DAY	INCREMENT DAY NUMBER
	ADDA	#1	BECAUSE "INCA" DOESN'T WORK ON BCD STUFF
	DAA
	STAA	CODE+SDOS:DAY
	LDAA	CODE+SDOS:MONTH	EXCEED # DAYS ALLOWED FOR THIS MONTH ?
	BSR	BCDTOBIN	CAUSE I DON'T THINK GOOD IN BCD
	STAA	DAYSPERMONTHX+1	(GO LOOK UP IN TABLE)
	LDX	DAYSPERMONTHX
	LDAA	CODE+SDOS:DAY	(GET CURRENT DAY NUMBER)
	CMPA	(DAYSPERMONTH-1)&$FF,X	(COMPARE TO # DAYS ALLOWED IN THIS MONTH)
	BLS	CLOCKTICKEXIT	B/ LOWER, BYE!
	CMPA	#$29	# DAYS FOR THIS MONTH EXCEEDED
	BNE	CLOCKTICK2	B/ MUST SWITCH TO NEXT MONTH
	LDAA	CODE+SDOS:YEAR	I HATE LEAP YEAR STUFF; IS THIS A LEAP YEAR ?
	BSR	BCDTOBIN	NEED I SAY MORE ?
	BITA	#3	IT MAKES THE "A MULTIPLE OF 4" TEST EASY
	BEQ	CLOCKTICKEXIT	B/ FEB 29 IS LEGAL
CLOCKTICK2	; TIME FOR NEW MONTH
	LDAA	#1	SET DAY TO "1ST OF MONTH"
	STAA	CODE+SDOS:DAY
	ADDA	CODE+SDOS:MONTH	BUMP MONTH
	DAA		IN DECIMAL
	STAA	CODE+SDOS:MONTH
	CMPA	#$12	BET YOU CAN'T GUESS WHAT THIS MAGIC CONSTANT IS...
	BLS	CLOCKTICKEXIT	B/ MONTH IS OK, BYEBYE!
	LDAA	#1	NEW YEAR STRUCK, SET MONTH TO "JANUARY"
	STAA	CODE+SDOS:MONTH
	ADDA	CODE+SDOS:YEAR	BUMP THE YEAR
	DAA		ALSO IN DECIMAL (WHY'D I PICK THAT??)
	STAA	CODE+SDOS:YEAR	THAT'S ALL YOU'LL GET ME TO DO!
CLOCKTICKEXIT	EQU	*
	JMP	FORCESCHEDULE	TO FORCE THE TIMEOUT TASK TO RUN
*
*	BCDTOBIN -- CONVERTS BCD NUMBER IN (A) TO BINARY EQUIVALENT IN (A)
*
BCDTOBIN	EQU	*
	TAB
	ANDB	#$F	= RIGHT DIGIT
	SBA
	PSHB
	LSRA		= LEFT DIGIT * 8
	TAB
	LSRA		= LEFT DIGIT * 4
	LSRA		= LEFT DIGIT * 2
	ABA		= LEFT DIGIT * 10
	PULB		ADD RIGHT DIGIT
	ABA		= LEFT DIGIT * 10 + RIGHT DIGIT
	RTS		LOOK MA! A SUBR OF ALL(MOST) SINGLE BYTE INSTRUCTIONS!

DAYSPERMONTH	FCB	$31,$28,$31,$30,$31,$30,$31,$31,$30,$31,$30,$31
	PAGE
*	TIMEOUT TASK CODE
*	FOR EACH WAKEUP, PROCESS NEXT TIMEOUT LIST ELEMENT
*	SUBTRACTS 1 TICK FOR EACH TIMEOUT LIST ELEMENT FROM TIMEOUT:FUSE
*	IF FUSE GETS ZEROED (OR GOES NEGATIVE), TRIGGERS ASSOCIATED TIMEOUT ROUTINE
*	ALL LIST ELEMENT UPDATING IS DONE WITH INTERRUPTS DISABLED...
*	TO PREVENT NASTY TIMING SPLINTERS
*
TIMEOUTTASKBORED	EQU	*
	LDX	#CLOCKTICKEDEVENT	WAKE ME UP SOMEDAY
	JSR	WAITEVENT$	ZZZZZZZ....
TIMEOUTTASK	EQU	*
	LDX	CODE+SDOS:CONFIGURATION	START AT TOP OF TIMEOUT LIST
	LDX	CNFG:TIMEOUTLIST,X
*	BEQ	TIMEOUTTASKBORED	B/ NO TIMEOUT LIST
*	ASSERT: TIMEOUT LIST IS *NOT* EMPTY!
TIMEOUTTASKL	; PROCESS TIMEOUT LIST ELEMENT
	STX	TIMEOUTLEPTR	SET UP LIST ELEMENT POINTER
	LDX	#TIMEOUTLEH	FIRE UP THE LIST ELEMENT HANDLER
	JSR	STARTIO$
	page
*
*	The following code is the secondary SDOS checksum check.
*	Its purpose is to detect that the first checksum routine has
*	been tampered with, and to blow up unpredictably when this occurs.
*
	ldx	exitscanptr	pick up place where we left off last
	ldb	exitpartialchksum	get partial checksum as of last round
	aslb		do 1 step of incremental checksum check
	adcb	15,x
	stb	exitpartialchksum	save partial checksum value
	inx		bump pointer for next round
	cpx	#exitchksumend-15	reached end of area to be checksummed?
	bne	secondarychecksum1	b/ not yet
	ldx	#exitchksumbase-15	yes, restart pointer from beginning
*	Assert: exitpartialchksum is zero here (if EXIT not tampered)
	addb	Usertasktcb+tcb:stack+1	add checksum value to user stack pointer
	stb	Usertasktcb+tcb:stack+1	which causes user to die mysteriously if <>0!
secondarychecksum1	; end of secondary checksum code
	stx	exitscanptr	save scan pointer for next clock tick
*
*	NOW, FINISH PROCESSING THE CLOCK TICK
*
	LDX	#CLOCKTICKEDEVENT	WAKE ME UP WHEN THE SUN SHINES AGAIN
	DEC	,X	TO TAKE CARE OF THE CLOCK TICK WE JUST PROCESSED
	JSR	WAITEVENT$	ZZZZZZ....
	LDX	TIMEOUTLEPTR	WAKE UP, SLEEPYHEAD!
	LDX	TIMEOUT:LINK,X	FIND NEXT TIMEOUT LIST ELEMENT
	BNE	TIMEOUTTASKL	B/ NOT AT END OF LIST, GO PROCESS THIS ELEMENT
	BRA	TIMEOUTTASK	END OF LIST HIT, START AT TOP OF LIST
	PAGE
*	TIMEOUT LIST ELEMENT PROCESSOR
*	STARTED BY "STARTIO" CALL FROM TIMEOUT TASK
*
TIMEOUTLEH	EQU	*
	LDX	TIMEOUTLEPTR	NOTE: ALL INTERRUPTS ARE DISABLED HERE!
	LDD	TIMEOUT:FUSE,X	DO DOUBLE PRECISION DECREMENT
	BEQD	IORTI1	B/ THIS TIMEOUT LIST ELEMENT IS DE-FUSED
	SUBB	NTIMEOUTBLKS	SUBTRACT # CLOCK TICKS THAT WILL PASS
	SBCA	#0	BEFORE WE GET BACK TO THIS LIST ELEMENT AGAIN
	BCS	TIMEOUTLEH3	B/ TIMED OUT!
	STD	TIMEOUT:FUSE,X	PERHAPS TIME OUT DID NOT OCCUR; STORE FUSE BACK
	BNED	IORTI1	B/ TIMEOUT DID NOT OCCUR
TIMEOUTLEH3	; TIMEOUT OCCURRED
	CLR	TIMEOUT:FUSE,X	DEFUSE THIS LIST ELEMENT
	CLR	TIMEOUT:FUSE+1,X
	LDD	TIMEOUT:DCB,X	FETCH DCB POINTER FROM TIMEOUT BLOCK
	JMP	[TIMEOUT:ROUTINE,X]	AND GO TO THE DEVICE INTERRUPT ROUTINE
*	IT WILL DO THE IORTI FOR US
	PAGE	*** TASK SCHEDULER ***
*	ASSERT: INTERRUPTS ARE OFF AND SDOS:STACKSWITCHED=:FF
*
TASKRTI	LDX	CODE+SDOS:CURRENTASK	QUICK EXIT FROM INTERRUPT BACK TO TASK
	LDS	TCB:STACK,X	POINTS TO CONTEXT BLOCK; WE NEVER SAVED 0-7
FORCERTI	JMP	INTRTI	EXIT FROM INTERRUPT ROUTINE

FORCESCHEDULE	; FORCE SCHEDULER TO LOOK AROUND AFTER INTERRUPT
* NOTE: 6809 DPR REGISTER IS UNDEFINED HERE!
	CLR	SURPRISE	SYSTEM STATE HAS CHANGED SIGNIFICANTLY
IORTI	; SYSTEM STATE HAS NOT CHANGED SIGNIFICANTLY
	JSR	INTDISABLE	SO WE DON'T GET INTERRUPTED...
*			IF CODE+SDOS:STACKSWITCHED GOES TO -1!
IORTI1	; ENTRY POINT FOR TIMEOUT LIST ELEMENT HANDLER
	DEC	CODE+SDOS:STACKSWITCHED	SCHEDULER OR INTERRUPT ROUTINE ALREADY ACTIVE ?
	BPL	FORCERTI	YES, GO BACK TO IT...
	LDAA	SURPRISE	ANYTHING INTERESTING HAPPEN ?
	ORAA	DONTSTOPME	OR ARE WE IN TASK CRITICAL CODE ?
	BNE	TASKRTI	B/ NO, GO BACK TO TASK NOW
PUSH0TO7	; SAVE LOCATIONS 0 TO 7 IN TCB
	INC	CODE+SDOS:STACKSWITCHED	REMEMBER THAT "STACKS ARE SWITCHED"
*			ASSERT: SDOS:STACKSWITCHED=0
	JSR	INTENABLE	RE-ENABLE INTERRUPTS
	LDX	CODE+SDOS:CURRENTASK	BUT FIRST WE MUST SAVE THE REST OF THE TASK CONTEXT
	IF	M6809
	LDU	TCB:STACK,X	SET DPR USED BY SCHEDULER TO LAST TASK DPR
	LDA	REG:DP,U
	TFR	A,DP
	FIN
	LDD	$0		SAVE LOCATIONS 0-7
	STD	TCB:SCRATCHPAD+0,X
	LDD	$2
	STD	TCB:SCRATCHPAD+2,X
	LDD	$4
	STD	TCB:SCRATCHPAD+4,X
	LDD	$6
	STD	TCB:SCRATCHPAD+6,X
	PAGE
SCHEDLDS	; RESET TO BASE OF INTERRUPT STACK
	LDS	INTERRUPTSTACK	USE INTERRUPT STACK FOR SCHEDULER'S STACK
SCHEDTOP	; START SCANNING TCB LIST
	LDAA	#1	WE'RE STARTING OVER, NO BAD DECISIONS
	STAA	SURPRISE	"NO SURPRISES HAVE OCCURRED"
	LDX	CODE+SDOS:CONFIGURATION	GET POINTER TO LIST OF TASKS
	LDX	CNFG:TASKQUEUE,X
SCHEDLOOP	EQU	*
	STX	CODE+SDOS:CURRENTASK	SAVE POINTER TO THIS TCB IN CASE HE'S READY
	BSR	SCHEDTEST	GO TEST WAKE UP CONDITION
	BNE	JUSTREADY	B/ TASK JUST WENT READY!
	LDX	CODE+SDOS:CURRENTASK	GET TCB ADDRESS
	LDX	TCB:LNK,X	FOLLOW TO NEXT TCB
	BNE	SCHEDLOOP	LOOP IF NOT END OF LIST
	BRA	SCHEDTOP	ELSE START PROCESSING THE LIST AGAIN

SCHEDTEST	; GET PARAMETER AND GO TEST WAKE-UP CONDITION
	LDD	TCB:COND,X	PUSH WAKE-UP SUBROUTINE ADDRESS ONTO THE STACK
	PSHD
	LDX	TCB:PARAM,X	GET PARAMETER FOR WAKE-UP ROUTINE
	RTS		AND PASS CONTROL TO IT (RETURN ADDRESS PUSHED BY BSR)
**** NOTE: DPR NOT DEFINED ON ENTRY TO WAKE-UP CONDITION SUBROUTINE ****
	PAGE
JUSTREADY	; TASK JUST BECAME READY
	LDX	CODE+SDOS:CURRENTASK	GET ADDRESS OF TCB THAT JUST WENT READY
	LDAB	#EXECUTING/256	MARK TCB AS 'EXECUTING'
	STAB	TCB:COND,X	SO INVOKING WAKE-UP ROUTINE...
	LDAB	#EXECUTING&$FF	WILL SPEED UP FURTHER DISPATCHES
	STAB	TCB:COND+1,X
	LDX	TCB:STACK,X	GET TASK'S STACK POINTER
	STAA	REG:A,X	SET TASK'S A REGISTER TO WAKE UP CODE
EXECUTING	; TASK WAS IN EXECUTION WHEN STOPPED
	LDX	CODE+SDOS:CURRENTASK	SWITCH TO TASK'S STACK
	IF	M6809
	LDU	TCB:STACK,X	SELECT DPR FOR PAGE ZERO SCRATCH
	LDA	REG:DP,U
	TFR	A,DP
	FIN
	LDD	TCB:SCRATCHPAD+0,X	RESTORE LOCATIONS 0 TO 7
	STD	$0
	LDD	TCB:SCRATCHPAD+2,X
	STD	$2
	LDD	TCB:SCRATCHPAD+4,X
	STD	$4
	LDD	TCB:SCRATCHPAD+6,X
	STD	$6
	LDAA	SURPRISE	DID WE GET SURPRISED ?
	BEQ	SCHEDLDS	YES, GO SCHEDULE AGAIN!
	JSR	INTDISABLE	INTERRUPTS OFF SO WE CAN DIDDLE WITHOUT TROUBLE
	LDS	TCB:STACK,X	SWITCH TO USER'S STACK
	DEC	CODE+SDOS:STACKSWITCHED	FLAG "USING TASK'S STACK, NOT INTERRUPT STACK"
*			ASSERT: SDOS:STACKSWITCHED=:FF
	JMP	INTRTI	AND PICK UP EXECUTION OF USER TASK
	PAGE
WAITEVENTCOND	; WAIT FOR EVENT CONDITIONAL WAKE-UP ROUTINE
	LDAA	,X	FETCH THE EVENT (A BYTE) OF INTEREST
WAITEVENTCONDRTS
	RTS		RETURN CC <> 0 IF BYTE IS NON-ZERO
*
*	WAITEVENT$ -- WAIT FOR EVENT (X) TO GO NON-ZERO
*
WAITEVENT$	EQU	*
	LDAA	,X	HAS EVENT ALREADY OCCURRED ?
	BNE	WAITEVENTCONDRTS	B/ YES, AVOID INVOKING THE SCHEDULER!
	LDD	#WAITEVENTCOND	GET ADDRESS OF CONDITIONAL TEST ROUTINE
*	BSR	WAIT$
*	RTS
*
*	WAIT$ -- MAKE TASK WAIT FOR WAKEUP CONDITION
*	(A,B) POINTS TO WAKE UP CONDITION TEST SUBROUTINE...
*	WHICH IS CONTINUALLY CALLED BY THE SCHEDULER
*	(X) CONTAINS PARAMETER TO BE PASSED IN (X) TO WAKE-UP TEST
*	A NON-ZERO CONDITION CODE ON EXIT FROM WAKE-UP ROUTINE...
*	CAUSES THE TASK TO BE WOKEN; (A) FROM WAKE-UP ROUTINE...
*	WILL BE PASSED TO TASK IN (A) REGISTER
*	WAKE UP SUBROUTINE IS NOT CALLED ANYMORE.
*	ALL REGISTERS DESTROYED; SCRATCHPAD PRESERVED
*
WAIT$	; (X) POINT TO WAIT SUBROUTINE FOR TASK
	INC	DONTSTOPME	SO WE COME RIGHT BACK AFTER INTERRUPT
*			ASSERT: DONTSTOPME=1
	STX	SCHEDX	SAVE PARAMETER OF WAKE UP ROUTINE
	LDX	CODE+SDOS:CURRENTASK	REMEMBER ADDRESS OF WAKE-UP SUBROUTINE
	STD	TCB:COND,X
	LDD	SCHEDX	SAVE PARAMETER FOR WAKE-UP SUBROUTINE
	STD	TCB:PARAM,X	IN TCB
	IF	M6800!M6801
	PSHD		TURN RETURN ADDRESS INTO A CONTEXT BLOCK
	CLRA		MAKE A ZERO TO STORE
	PSHA		A ZERO FOR B REGISTER
	PSHA		A ZERO FOR THE A REGISTER
	PSHA		A ZERO CONDITION CODE BYTE
	ELSE	(M6809)
	CLI		MAKE SURE TASK INTERRUPTS ARE ON WHEN HE IS AWOKEN AGAIN
	PSHS	U,Y,X,DP,B,A,CC
	FIN
	JSR	INTDISABLE	SHUT DOWN THE WORLD...
*			SO WE CAN SWITCH STACKS SAFELY
	STS	TCB:STACK,X	SAVE TASK'S STACK POINTER
	LDS	INTERRUPTSTACK	AND SWITCH TO THE INTERRUPT STACK
	DEC	DONTSTOPME	SWITCH FROM TASK CRITICAL MODE...
*			TO "INTERRUPT" MODE (STACKSWITCHED WILL SAVE US!)
	JMP	PUSH0TO7	SAVE TEMPS AND THEN SCHEDULE SOME OTHER TASK
*	STARTIO$ -- (X) POINTS TO INTERRUPT ROUTINE ADDRESS
*	SIMULATES AN I/O INTERRUPT AND TRANSFERS CONTROL TO (X)
*	ALL REGISTERS DESTROYED EXCEPT (A,B) WHICH ARE PASSED TO INTERRUPT ROUTINE
*	USED TO START AN INTERRUPT ROUTINE UP; CALLABLE ONLY BY TASK LEVEL ROUTINES
*
STARTIO$	EQU	*
	IF	M6800!M6801
	JSR	INTDISABLE	DISABLE INTERRUPTS
	STX	SCHEDX	SAVE INTERRUPT ROUTINE ADDRESS
	DES		PUSH A GARBAGED X REGISTER
	DES
	PSHD		PUSH D REGISTER [NOTE: (D) IS PASSED TO ROUTINE INVOKED BY STARTIO!]
	DES		RESERVE SPACE FOR CC BITS ON STACK
	TSX		ZERO CC BITS (ENABLING INTERRUPTS ON RETURN)
	CLR	0,X
	INC	CODE+SDOS:STACKSWITCHED	FLAG 'IN AN INTERRUPT ROUTINE'
	LDX	CODE+SDOS:CURRENTASK	LEAVE TCB:COND = "EXECUTING"
	STS	TCB:STACK,X	AND SWITCH TO THE INTERRUPT STACK
	LDS	INTERRUPTSTACK	...
	JMP	[SCHEDX]	GO TO INTERRUPT ROUTINE ADDRESS
	ELSE	(M6809)
	JSR	INTDISABLE	DISABLE INTERRUPTS
	PSHS	U,Y,X,DP,B,A	PUSH REST OF CONTEXT BLOCK
	LDAA	#$80	= CC BYTE WITH "ENTIRE CONTEXT ON STACK" BIT SET
	PSHA
	LDA	1,S	RESTORE (D) TO ENTRY VALUE
	INC	CODE+SDOS:STACKSWITCHED	FLAG 'IN AN INTERRUPT ROUTINE'
	LDU	CODE+SDOS:CURRENTASK	LEAVE TCB:COND = "EXECUTING"
	STS	TCB:STACK,U	AND SWITCH TO THE INTERRUPT STACK
	LDS	INTERRUPTSTACK	...
	JMP	,X	GO TO INTERRUPT ROUTINE ADDRESS
	FIN

IOINTERRUPT	; IO INTERRUPT GOES HERE FIRST!
	INC	CODE+SDOS:STACKSWITCHED	BUMP NUMBER OF NESTED INTERRUPTS
	BNE	IOINTJ	B/ ALREADY SWITCHED TO INTERRUPT STACK
	LDX	CODE+SDOS:CURRENTASK	SWITCH TO SCHEDULER'S STACK
	STS	TCB:STACK,X	SAVE TASK'S STACK IN HIS TCB
	LDS	INTERRUPTSTACK	GET THE SCHEDULER'S STACK INSTEAD
IOINTJ ; JUMP TO INTERRUPT ROUTINE
	IF	M6809
	LDA	#$FF	SET DPR TO FIXED CONSTANT
	TFR	A,DP
	FIN
	JMP	JMPIOINT	GO DEAL WITH I/O INTERRUPT

PATCHSPACE	RPT	100	PATCH SPACE
	SWI
	PAGE
SDOS:END	EQU	*

ACTSIZE	EQU	*-CODE	ACTUAL RESIDENT SDOS SIZE
	IF	ACTSIZE>>SDOS:ESTSIZE
	?SDOS TOO BIG FOR MEMORY SIZE SPECIFIED
	FIN
	END
