;
; TITLE		CP/M SPEED-UP BY DISK I/O BUFFERING
; FILENAME	FAST.ASM
; AUTHOR	Robert A. Van Valzah    12/25/78
; LAST REVISED	8-Oct-1981 David Bennett
; REASON	Upgrade to CP/M 2.0 compatibility
;
;
;  \\\\\\\\\\\\\\\\\                   /////////////////
;   >>>>>>>>>>>>>>>>>  E Q U A T E S  <<<<<<<<<<<<<<<<<
;  /////////////////                   \\\\\\\\\\\\\\\\\
;
BASE	EQU	100H	;ORG FOR THIS ASSEMBLY OF FAST
VERS	EQU	201	;FAST VERSION NUMBER
;
;	CP/M SYSTEM ENTRY POINTS AND RAM EQUATES
;
BOOT	EQU	0	;ADDRESS OF WARM BOOT VECTOR
CURDSK	EQU	4	;ADDRESS OF CURRENTLY LOGGED DRIVE
BDOS	EQU	5	;ADDRESS OF BDOS ENTRY VECTOR
DBUF	EQU	80H	;BUFFER USED FOR COMMAND LINE HOLDING
TPA	EQU	100H	;TRANSIENT PROGRAM AREA
;
;	GLOBAL EQUATES
;
SECLEN	EQU	80H	;LENGTH OF A SECTOR IN BYTES
DIRTRK	EQU	2	;TRACK WHICH HOLDS DIRECTORY
;
;	TOKEN VALUE EQUATES
;
SENTTOK	EQU	1	;TOKEN SHOWING DRIVE & TRACK INFO SENT
UPDTTOK	EQU	1	;TOKEN SHOWING SECTOR GETS WRITTEN
ONLNTOK	EQU	3	;TOKEN SHOWING THAT A DRIVE IS 'ON LINE'
;
;	ASCII CHARACTER EQUATES
;
CR	EQU	13	;CARRIAGE RETURN
LF	EQU	10	;LINE FEED
;
;	BDOS FUNCTION NUMBER EQUATES
;
READ	EQU	20	;READ A RECORD FROM A FILE
OPEN	EQU	15	;OPEN A FILE FOR READING
SETDMA	EQU	26	;CHANGE DMA ADDRESS
	PAGE
;
;
;   \\\\\\\\\\\\\\\\                 ////////////////
;    >>>>>>>>>>>>>>>>  M A C R O S  <<<<<<<<<<<<<<<<
;   ////////////////		     \\\\\\\\\\\\\\\\
;
$-MACRO
;
; THE CPM MACRO CALLS BDOS TO EXECUTE THE FUNCTION PASSED
; AS THE FIRST ARGUMENT.  THE SECOND ARGUMENT IS THE
; 'INFORMATION' TO PASSED TO CP/M (IF ANY).
;
CPM	MACRO	FUNC,INFO
	MVI	C,FUNC
	IF	NOT NUL INFO
	  LXI	D,INFO
	ENDIF
	CALL	BDOS
	ENDM
	PAGE
;
;  \\\\\\\\\\\\\\\\     F A S T     ////////////////
;   >>>>>>>>>>>>>>>>    I N I T    <<<<<<<<<<<<<<<<
;  ////////////////   M O D U L E   \\\\\\\\\\\\\\\\
;
;
; THIS MODULE MAY BE COMPLETELY OVERLAID ONCE FAST HAS BEEN
; SUCCESSFULLY INSTALLED IN THE SYSTEM.  IT BECOMES PART OF
; THE TPA.
;
;
; CONTROL IS TRANSFERED TO THIS ADDRESS (THE BASE OF FAST)
; UPON COMPLETION OF THE PACKUP MODULE.  HERE, WE INSTALL
; FAST AND LOAD THE TRANSIENT.
;
	ORG	BASE
	JMP	FASTNTRY ;JUMP AROUND FIRST PARAMETER BLOCK
;
; THIS IS THE FIRST (OF TWO) PARAMETER PASSING BLOCKS.
; INFORMATION IS PASSED BETWEEN THE FAST AND PACKUP MODULES.
; ANY CHANGES MADE TO THIS BLOCK MUST ALSO BE MADE TO THE
; CORRESPONDING BLOCK IN THE PACKUP MODULE.
;
;**************************************************************
;							      *
LEN:			;				      *
	DW	CODELEN	;RELOCATION LENGTH		      *
;							      *
FCB:			;FCB USED TO LOAD COM FILE	      *
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;	      *
	DB	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;	      *
;							      *
;**************************************************************
;
; END OF FIRST PARAMETER BLOCK
;
FASTNTRY:
	LXI	SP,STACK ;USE LOCAL STACK SPACE
	CALL	LINKUP	;INSTALL FAST BY LINKING TO CP/M
	LDA	CURDSK	;INIT DDB POINTERS TO CURRENT DRIVE
	MOV	C,A
	CALL	FSETDSK
	CALL	LOADTRAN ;LOAD THE TRANSIENT
	CALL	MSGP	;PRINT FAST SIGNON MESSAGE
	DB	'Alfred Hospital FAST '
		;DEFINE VERSION NUMBER IN ASCII
	DB	'0'+vers/100
	db	'.'
	DB	'0'+(VERS/10) mod 10
	DB	'0'+VERS MOD 10
	DB	CR, LF+80H
	JMP	BEGINE	;BEGIN EXECUTION OF TRANSIENT
	PAGE
;
; LINKUP INSTALLS FAST IN A CP/M SYSTEM IN TWO STEPS.
; INTERNALLY REFERENCED ROUTINE.
; FIRST, THE EXISTING BIOS JUMP TABLE IS SAVED AND IT IS
; REPLACED WITH A JUMP TABLE INTO FAST, AND
; SECOND, THE BDOS VECTOR IS MODIFIED TO JUMP TO THE BASE OF
; FAST, WHICH, IN TURN, JUMPS TO THE REAL BDOS.  THUS,
; TRANSIENTS THINK THE TPA ENDS BELOW FAST INSTEAD OF BELOW
; BDOS.
;
LINKUP:
	; STEP 1
	LDA	BOOT+2 ;GET BIOS BASE ADDRESS INTO DE
	MOV	D,A
	MVI	E,0
	PUSH	D		;SAVE FOR SECOND PART OF TRANSFER
	LXI	H,SAVEJMP
	MVI	C,jtablen	;length of jump table
	CALL	XF1		;MOVE FROM BIOS INTO SAVEJMP
				;REPLACE OLD TABLE WITH LINK TO FAST
	LXI	D,FASTLINK
	POP	H
	MVI	C,jtablen
	CALL	XF1		;MOVE FROM FAST LINK INTO BIOS
	; STEP 2
	LHLD	BDOS+1		;GET CURRENT BDOS BASE ADDRESS
	SHLD	NEWBVECT+1 	;COPY IT INTO THE NEW BDOS VECTOR
	LXI	H,NEWBVECT 	;MODIFY BDOS VECTOR TO JMP TO
	SHLD	BDOS+1 		;NEW BDOS VECTOR
	RET
;
; LOADTRAN LOAD THE TRANSIENT NAMED IN THE ARGUMENT TO FAST
; INTO THE TPA WITH CHECKING FOR OVERLAY (LOAD ERROR - 
; TRANSIENT TOO BIG OR TPA TOO SMALL).
; RETURNS ONLY IF LOAD WENT OK.  BAILS OUT WITH ERROR MESSAGE
; VIA UNLINK OTHERWISE.
; INTERNALLY REFERENCED ROUTINE.
;
LOADTRAN:
	LXI	D,TPA	;SET DMA ADDRESS INTO TPA FOR OPEN
	PUSH	D	;SAVE DMA ADDRESS FOR INCREMENTING
	CPM	SETDMA
	CPM	OPEN,FCB ;OPEN THE COM FILE
	INR	A	;WAS OPEN SUCCESSFULL?
	JZ	NOCMERR	;NO - ISSUE NO COM FILE ERROR
READSEC:
	POP	D	;GET DMA ADDRESS FROM STACK
	PUSH	D	;AND SAVE BACK FOR LATER
	MVI	A,HIGH (BASE-100H) ;MAX ALLOWABLE DMA ADDRESS
	CMP	D	;IS COM FILE TOO BIG?
	JC	LOADERR	;YES - ISSUE LOAD ERROR
	CPM	SETDMA	;PASS DMA ADDRESS TO CP/M
	POP	D	;INCREMENT DMA ADDRESS BY ONE SECTOR
	LXI	H,SECLEN
	DAD	D
	PUSH	H	;PUT NEW DMA ADDRESS BACK
	CPM	READ,FCB ;READ A SECTOR FROM COM FILE INTO TPA
	ORA	A	;WAS READ OK?
	JZ	READSEC	;YES - KEEP READING
			;NOPE - ALL DONE LOADING
	CPM	SETDMA,DBUF ;SET DMA ADDRESS LIKE CCP WOULD
	POP	B	;CLEAN GARBAGE DMA ADDRESS OFF STACK
	RET
;
LOADERR:
	CALL	MSGP	;PRINT LOAD ERROR MESSAGE
	DB	'OUT OF MEMOR', 'Y'+80H
	JMP	UNLINK	;RETURN TO CP/M
;
NOCMERR:
	CALL	MSGP	;PRINT 'NO COM FILE'
	DB	'NO COM FIL', 'E'+80H
	JMP	UNLINK	;RETURN TO CP/M
;
; END OF INIT MODULE
	PAGE
;
;  \\\\\\\\\\\\\\\\                  ////////////////
;   >>>>>>>>>>>>>>>>    F A S T     <<<<<<<<<<<<<<<<
;  ////////////////   M O D U L E    \\\\\\\\\\\\\\\\
;
;
; THIS MODULE CONTAINS THE ACTUAL DISK I/O BUFFERING CODE.
;
; THIS ADDRESS BECOMES THE NEW TOP OF THE TPA.
;
	ORG	(($-1) OR 255) + 1 ;ORG TO PAGE BOUNDRY
	DS	6	;SO BDOS VECTOR ADDRESS = XXX6
NEWBVECT:
	JMP	$-$	;ADDRESS WILL BE MODIFIED BY LINKUP
;
; DDB ADDRESS BUFFERS.  EACH BUFFER IS FOUR W-O-R-D-S LONG,
; ONE WORD FOR THE ADDRESS OF THE BUFFER FOR EACH OF FOUR
; POSSIBLE DRIVES.  IF THERE IS NO DDB FOR A GIVEN DRIVE,
; THE DDB ADDRESS IS ZERO.
;
RDBUF	DW	0, 0, 0, 0
WRBUF	DW	0, 0, 0, 0
DIRBUF	DW	0, 0, 0, 0
;
; 8-10-81 upgrade to CP/M compatibility
; when off-line disc is first used, BIOS routine SETDSK is called
; CP/M 2.x BIOS returns a vital parameter in HL, which must be
; passed back to BDOS.  It is stored here for that purpose.
;
online	dw	0, 0, 0, 0	; stored HL return from SETDSK
;
; USER PATCHABLE MEMORY HIT (ERROR) ROUTINE
; INTERNALLY REFERENCED ROUTINE.
;
MEMHIT:
	PUSH D ! PUSH B ;SAVE REGISTERS
	CALL	MSGP
	DB	'MEMORY HI', 'T'+80H
	POP B ! POP D
	RET
;
FASTLINK:		;JMP TABLE TO REPLACE EXISTING BIOS'S
	JMP	UNLINK	;FOR EITHER BOOT, REMOVE FAST AND BOOT
	JMP	UNLINK
	JMP	CCONST	;JUST PASS ALL CHARACTER I/O THRU
	JMP	FCONIN	;EXCEPT CONSOLE IN
	JMP	CCONOUT
	JMP	CLIST
	JMP	CPUNCH
	JMP	CREADER
	JMP	FHOME	;USE FAST HOME INSTEAD OF BIOS
	JMP	FSETDSK	;USE FAST SET DISK
	JMP	FSETTRK	;USE FAST SET TRACK
	JMP	FSETSEC	;USE FAST SET SECTOR
	JMP	FSETDMA	;USE FAST SET DMA
	JMP	FREAD	;USE FAST READ SECTOR
	JMP	FWRITE	;USE FAST WRITE SECTOR
jtablen	equ	$-fastlink
;
;
BEGINE:			;ENTRY POINT FROM INIT TO EXEC. TRANS.
	CALL	TPA	;EXECUTE TRANS.
			;IF HE RETURNS, FALL THRU TO UNLINK
;
; UNLINK IS THE COMPLIMENTARY ROUTINE TO LINKUP.  IT RESTORES
; THE BIOS JUMP TABLE AND THE BDOS VECTOR TO THEIR STATES
; BEFORE FAST WAS INSTALLED (BY LINKUP).
; INTERNALLY AND EXTERNALLY REFERENCED ROUTINE.
;
UNLINK:
	LXI	SP,STACK ;SETUP LOCAL STACK
			;RETURN BIOS VECTORS UNHARMED
	LXI	D,SAVEJMP
	LDA	BOOT+2 ;GET BIOS BASE INTO DE
	MOV	H,A
	MVI	L,0
	MVI	C,jtablen	;length of jump table
	CALL	XF1		;MOVE FROM SAVEJMP TO BIOS BASE
	LHLD	NEWBVECT+1 	;GET REAL BDOS VECTOR ADDRESS
	SHLD	BDOS+1 		;STORE IT INTO REAL BDOS VECTOR
	CALL	CLOSE		;EMPTY ANY DDB'S WHICH MAY HAVE DATA
	CALL	MSGP		;PRINT FAST SIGN OFF MESSAGE
	DB	CR, LF
	DB	'FAST terminate', 'd'+80H
	JMP	BOOT		;AND REBOOT
;
; FAST CONSOLE IN
; EXTERNALLY REFERENCED ROUTINE
; WRITES ALL SECTORS WAITING TO BE WRITTEN SO THAT USER
; CAN'T GET HIMSELF INTO TROUBLE BY REMOVING THE DISK
; EXTERNALLY REFERENCED ROUTINE.
;
FCONIN:
	CALL	CLOSE	;WRITE ALL DDB'S
	JMP	CCONIN	;NOW DO THE CONSOLE INPUT
;
; FAST HOME ROUTINE
; INTERNALLY AND EXTERNALLY REFERENCED
;
FHOME:
	MVI	C,0	;JUST PASS 0 TO FAST SET TRACK
			;FALL THRU
;
; FAST SET TRACK
; EXTERNALLY REFERENCED ROUTINE
; REQUESTED TRACK IS JUST STORED AWAY UNTIL
; A READ OR WRITE REQUEST COMES IN.
;
FSETTRK:
	MOV	A,C	;GET REQUEST TO REG A
	STA	REQTRK	;STORE IT
	RET
;
; FAST SET SECTOR
; EXTERNALLY REFERENCED ROUTINE
; THE REQUESTED SECTOR IS JUST
; STORED AWAY UNTIL A READ OR WRITE COMES IN.
;
FSETSEC:
	MOV	A,C	;GET REQUESTED SECTOR TO REG A
	STA	REQSEC	;STORE IT
	RET
;
; FAST SET DMA ADDRESS
; EXTERNALLY REFERENCED ROUTINE.
;
FSETDMA:
	MOV	H,B	;REQUESTED DMA ADR INTO HL
	MOV	L,C
	SHLD	REQDMA	;SAVE IT
	RET
;
; FAST SET DISK DRIVE
; EXTERNALLY REFERENCED ROUTINE
;
FSETDSK:
	MOV	E,C	;SAVE DRIVE FOR ON LINE CODE
	MOV	A,C	;GET REQUESTED DRIVE
	STA	REQDSK	;SAVE FOR PASS THRU
	ADD	A
	MOV	C,A	;FORM word INDEX IN REG BC
	MVI	B,0
	LXI	H,DIRBUF ;GET NEW DIR DDB POINTER
	CALL	INDXIND
	SHLD	DIRADR	;AND SAVE IT
	LXI	H,RDBUF	;GET NEW READ TRACK DDB POINTER
	CALL	INDXIND
	SHLD	RDADR	;AND SAVE IT
	LXI	H,WRBUF	;GET NEW WRITE TRACK DDB POINTER
	CALL	INDXIND
	SHLD	WRADR
	LXI	H,ONLINE ;BASE OF 'ON LINE' VECTOR
	call	indxind
	MOV	A,h	;IS THIS DRIVE ON LINE?
	ORA	l
	RNZ		;->yes,  return to BDOS with HL set
; get this drive on-line
	push	b	;preserve word index into table
	mov	c,e
	CALL	CSETDSK	;first, really select it
	pop	b
	mov	a,h	;check the return in HL
	ora	l
	rz		;->zero is error, return to BDOS with HL zero
	push	h	;ok, save (HL)
	lxi	h,online;..in online table
	dad	b	;..word-indexed by drive number
	pop	b
	mov	m,c
	inx	h
	mov	m,b
	push	b	;we still need this for later
	lda	reqdsk	;get disc again, home it
	mov	c,a
	CALL	CHOME
	LHLD	DIRADR	;AND READ THE DIRECTORY IF IT IS BUFFERED
	call	RDDDB
	pop	h
	ret		;return to BDOS with HL set
;
; FAST READ SECTOR
; EXTERNALLY REFERENCED ROUTINE.
;
FREAD:
	LHLD	RDADR	;GET ADDRESS OF READ TRACK DDB
	CALL	GETSEC	;TRY TO FIND SECTOR IN READ DDB
	RC		;FOUND IT
	LHLD	DIRADR	;TRY TO FIND SECTOR IN DIRECTORY DDB
	CALL	GETSEC
	RC		;FOUND IT
	LHLD	WRADR	;TRY TO FIND SECTOR IN WRITE DDB
	CALL	GETWSEC
	RC		;FOUND IT
	LHLD	RDADR	;SEE IF THERE IS A READ TRACK BUFFER
	MOV	A,H
	ORA	L
	JZ	RDPASS	;NO - THEN JUST PASS THE READ THRU
	LDA	REQTRK	;YES - SEE IF READING FROM NON-DIRECTORY
	CPI	DIRTRK	;SECTORS OF DIRECTORY TRACK
	JZ	RDPASS	;YES - JUST LET THAT PASS THRU
	PUSH	PSW	;NO - WRITE ANY DATA IN READ BUFFER
	PUSH	H
	CALL	WRDDB
	POP	H
	POP	PSW
	MOV	M,A	;FILL NEW TRACK INTO READ DDB
	PUSH	H	;SAVE READ DDB ADDRESS
	CALL	RDDDB	;AND FILL NEW DDB WITH DATA
	POP	H	;GET READ DDB ADDRESS BACK
	JMP	GETSEC	;TRANSFER SECTOR FROM DDB TO CALLER
			;AND RETURN FROM GETSEC
;
; FAST SECTOR WRITE
; EXTERNALLY REFERENCED ROUTINE.
;
FWRITE:
	LHLD	WRADR	;TRY TO PUT SECTOR INTO WRITE BUFFER
	CALL	PUTSEC
	RC		;IT WENT - ALL DONE
	LHLD	DIRADR	;TRY TO PUT IT INTO DIRECTORY BUFFER
	CALL	PUTSEC
	RC		;IT WENT - ALL DONE
	LHLD	RDADR	;TRY TO PUT INTO READ BUFFER
	CALL	PUTSEC
	RC		;IT WENT - ALL DONE
	LHLD	WRADR	;IS THERE A WRITE TRACK BUFFER?
	MOV	A,H
	ORA	L
	JZ	WRPASS	;NO - JUST PASS THIS WRITE THRU
	LDA	REQTRK	;YES - SEE IF THIS IS A NON-DIRECTORY
	CPI	DIRTRK	;SECTOR ON THE DIRECTORY TRACK?
	JZ	WRPASS	;YES - JUST PASS THAT THRU
	PUSH	PSW	;NO - DUMP EXISTING WRITE BUFFER TO DISK
	PUSH	H
	CALL	WRDDB
	POP	H
	POP	PSW
	MOV	M,A	;AND BUFFER UP REQUESTED TRACK
	CALL	PUTSEC	;MOVE SECTOR INTO WRITE BUFFER
	LDA	WRTERRF	;RETURN WRITE ERROR FLAG
	RET
;
; CLOSE ALL OPEN DDB'S.  WRITE ANY DATA LEFT IN THEM OUT TO
; DISK
; INTERNALLY REFERENCED ROUTINE.
;
CLOSE:
	MVI	C,4*3	;DDB COUNT
	LXI	H,RDBUF	;START OF DDB ADDRESS TABLES
CLOS:
	MOV	E,M	;GET DDB ADDRESS TO REG DE
	INX	H
	MOV	D,M
	INX	H
	PUSH	H	;SAVE TABLE ADDRESS
	PUSH	B
	XCHG		;DDB ADDRESS TO REG HL
	CALL	WRDDB	;CLOSE ONE DDB
	LDA	WRTERRF	;ANY WRITE ERRORS IN CLOSING?
	ORA	A
	JZ	CLOSOK	;NO - KEEP CLOSING
	CALL	MSGP
	DB	'DISK WRITE ERROR', CR, LF+80H
CLOSOK:
	POP	B	;GET COUNT
	POP	H	;GET TABLE ADDRESS
	DCR	C	;DONE ALL DDBS YET?
	JNZ	CLOS	;NO - KEEP CLOSING
	RET		;YES - ALL DONE
;
; INDEXED INDIRECT ADDRESSING.  RETURNS THE WORD AT THE
; ADDRESS BASE + INDEX
; INTERNALLY REFERENCED ROUTINE.
;
INDXIND:
	DAD	B	;ADD IN INDEX
	MOV	A,M	;GET LOW ORDER OF RESULT
	INX	H
	MOV	H,M	;GET HIGH ORDER OF RESULT
	MOV	L,A
	RET
;
; GET A SECTOR FROM A WRITE DDB INTO REQDMA.  MATCHES ONLY
; IF UPDATE FLAG IS SET.  RETURNS FLAG IF FOUND.
; INTERNALLY REFERENCED ROUTINE.
;
GETWSEC:
	CALL	SRCHDDB	;FIND A SECTOR AND TRACK MATCH?
	RNC		;NO - RETURN WITHOUT FLAG
	INX	H	;YES - SEE IF SECTOR HAS BEEN
	MOV	A,M	;WRITTEN (UPDATE FLAG=1)?
	ORA	A
	JNZ	GS1	;YES - TRANSFER FROM WRITE DDB
	CALL	RDPASS	;NO - BYPASS BUFFERING
	STC		;SET FOUND IT FLAG
	RET
;
; GET A SECTOR FROM A DDB INTO REQDMA.  FLAG SET IF FOUND.
; INTERNALLY REFERENCED ROUTINE.
;
GETSEC:
	CALL	SRCHDDB	;DO WE HAVE THE REQUESTED SECTOR?
	RNC		;NO - RETURN WITHOUT FLAG
	INX	H	;YES - POINT TO SECTOR DATA
GS1:			;ENTRY FROM GETWSEC (ABOVE)
	INX	H
	XCHG		;DATA ADDRESS TO REG DE
	LHLD	REQDMA	;DESTINATION TO REG HL
	CALL	XFER	;PERFORM THE TRANSFER
	LDAX	D	;GET GOOD/BAD FLAG
	STC		;SET 'FOUND IT' FLAG
	RET
;
; SEARCH A DDB FOR THE REQUESTED TRACK AND SECTOR
; INTERNALLY REFERENCED ROUTINE.
;
SRCHDDB:
	MOV	A,H	;SEE IF DDB IS PRESENT AT ALL
	ORA	L
	RZ		;NO - WE DON'T HAVE IT
	LDA	REQTRK	;YES - SEE IF REQTRK MATCHES DDB TRACK
	XRA	M
	RNZ		;NO MATCH - WE DON'T HAVE IT
	INX	H	;TRACKS MATCH - CHECK SECTORS
	INX	H
	LDA	REQSEC
	MOV	C,A
	LXI	D,SECLEN+3 ;BYTES BETWEEN SECTOR NUMBERS
SRCH1:
	MOV	A,M	;GET A SECTOR NUMBER FROM DDB
	ORA	A	;END OF DDB?
	RZ		;YES - RETURN WITHOUT FLAG
	CMP	C	;NO - SEE IF REQSEC MATCHES DDB SECTOR
	STC		;PREPARE TO RETURN FLAG IF SO
	RZ		;THEY MATCH - RETURN FLAG
	DAD	D	;NO MATCH - ON TO NEXT SECTOR
	JMP	SRCH1
;
RDPASS:
	CALL	PASSTHRU
	JMP	CREAD
;
; MOVE A SECTOR FROM MEMORY INTO A DDB
; INTERNALLY REFERENCED ROUTINE.
;
PUTSEC:
	CALL	SRCHDDB	;DOES IT GO IN THIS DDB?
	RNC		;NO - RETURN WITHOUT FLAG
	INX	H	;POINT TO DATA FIELD OF DDB
	MVI	M,UPDTTOK ;SET UPDATE FLAG
	INX	H
	XCHG		;SAVE DDB ADDRESS IN REG DE
	LHLD	REQDMA	;GET SOURCE ADDRESS INTO REG HL
	XCHG		;NOW REG HL=DDB ADDRESS (DEST) AND . .
	CALL	XFER	;REG DE=MEMORY (SOURCE)
	XRA	A	;RETURN WITHOUT WRITE ERROR
	MOV	M,A	;SET GOOD/BAD FLAG TO GOOD
	STC		;SET SECTOR PUT FLAG
	RET
WRPASS:
	CALL	PASSTHRU
	JMP	CWRITE
;
; PASS ALL DISK I/O PARAMETERS THRU TO BIOS
; INTERNALLY REFERENCED ROUTINE
;
PASSTHRU:
	LDA	REQDSK	;PASS REQUESTED DISK THRU
	MOV	C,A
	CALL	CSETDSK
	LDA	REQTRK	;PASS REQUESTED TRACK THRU
	MOV	C,A
	CALL	CSETTRK
	LDA	REQSEC	;PASS REQUESTED SECTOR
	MOV	C,A
	CALL	CSETSEC
	LHLD	REQDMA	;PASS REQUESTED DMA ADDRESS
	MOV	B,H
	MOV	C,L
	JMP	CSETDMA	;RETURN FROM CSETDMA
;
; READ A DDB FROM DISK.  ALL SECTORS ARE FILED IN, AND THEIR
; UPDATE FLAGS ARE RESET.
; INTERNALLY REFERENCED ROUTINE.
;
RDDDB:
	MOV	A,H	;DO NOTHING FI NO DDB
	ORA	L
	RZ
	PUSH	H	;SAVE DDB ADDRESS
	CALL	SENDDT	;SEND DRIVE AND TRACK INFO TO BIOS
	POP	H	;GET DDB ADDRESS
	INX	H	;POINT TO FIRST SECTOR BYTE
	INX	H
RDDSEC:
	MOV	A,M	;GET SECTOR NUMBER
	ORA	A	;END OF DDB?
	RZ		;YES - ALL DONE
	MOV	C,A	;PASS SECTOR TO BIOS
	PUSH	H	;SAVE WHILE CALLING
	CALL	CSETSEC
	POP	H	;GET DDB ADDRESS BACK
	INX	H	;POINT TO UPDATE FLAG
	MVI	M,0	;RESET UPDATE FLAG
	INX	H	;POINT TO DATA FIELD
	PUSH	H	;SAVE DATA POINTER
	MOV	B,H	;DATA ADDRESS TO REG BC
	MOV	C,L
	CALL	CSETDMA	;PASS DATA ADDRESS TO BIOS
	CALL	CREAD	;PERFORM THE READ
	POP	H	;GET DATA ADDRESS BACK
	LXI	B,SECLEN ;BYTES TO GOOD/BAD FLAG
	DAD	B
	MOV	M,A	;STORE GOOD/BAD FLAG FROM BIOS IN DDB
	INX	H
	JMP	RDDSEC	;ON TO NEXT SECTOR
;
; WRITE A DDB BACK TO DISK.  ONLY SECTORS WITH THE UPDATE FLAG
; SET GET WRITTEN.
; THE DRIVE AND TRACK INFORMATION FROM THE DDB IS SAVED BY
; THIS ROUTINE AND PASSED TO THE BIOS BY RCLDT ONLY IF
; A SECTOR IS TO BE WRITTEN.  THIS IS DONE TO BYPASS BIOS
; DELAYS WHEN SWITCHING DRIVES AND TO PREVENT NEEDLESS
; SEEKING TO TRACKS WHICH MIGHT NOT RECIEVE ANY DATA.
; INTERNALLY REFERENCED ROUTINE.
;
WRDDB:
	XRA	A	;CLEAR WRITE ERROR FLAG
	STA	WRTERRF
	MOV	A,H	;SEE IF A DDB IS PRESENT
	ORA	L
	RZ		;NO - ALL DONE WRITING
	MOV	A,M	;GET TRACK NUMBER
	INR	A	;TRACK = 0FFH (MEANING EMPTY)?
	RZ		;YES - NO WRITING TO DO
	SHLD	TRKPTR	;NO - SAVE DISK & TRACK POINTER FOR
	XRA	A	;RECALL LATER, AND RESET FLAG 
	STA	SENTFLG	;SHOWING THAT THEY HAVEN'T BEEN SENT
	INX	H	;POINT TO FIRST SECTOR NUMBER
	INX	H
TSTUPDT:
	MOV	A,M	;GET SECTOR TO REG A
	ORA	A	;END OF DDB?
	RZ		;YES - ALL DONE
	MOV	C,A	;SAVE SECTOR IN REG C
	INX	H	;POINT TO UPDATE FLAG
	MOV	A,M	;GET UPDATE FLAG
	ORA	A	;DOES THIS SECTOR NEED UPDATING ON DISK?
	CNZ	WRSEC	;YES - WRITE IT BACK
	LXI	D,SECLEN+2 ;BYTES TO NEXT SECTOR NUMBER
	DAD	D
	JMP	TSTUPDT	;CHECK THE NEXT UPDATE FLAG
;
; WRITE A SECTOR FROM A DDB BACK OUT TO DISK.
; INTERNALLY REFERENCED ROUTINE.
;
WRSEC:
	MVI	M,0	;RESET UPDATE FLAG
	PUSH	H	;SAVE UPDATE FLAG POINTER
	CALL	CSETSEC	;PASS SECTOR NUBER TO BIOS
	CALL	RCLDT	;PASS DRIVE AND TRACK TO BIOS
	POP	H	;GET UPDATE FLAG POINTER
	PUSH	H	;AND SAVE AGAIN
	INX	H	;POINT TO DATA
	MOV	B,H	;DATA ADDRESS TO REG BC
	MOV	C,L
	CALL	CSETDMA	;PASS DMA ADDRESS TO BIOS
	CALL	CWRITE	;WRITE THE DATA OUT
	LXI	H,WRTERRF ;OR THIS WRITE ERROR FLAG IN WITH
	ORA	M	;THE REST FROM THIS WRDDB
	MOV	M,A
	POP	H	;GET CALLERS REG HL BACK
	RET
;
; RECALL DISK AND TRACK INFO FROM A DDB, PASS IT TO THE BIOS,
; AND SET FLAG SHOWING THAT IS HAS BEEN SENT.
; INTERNALLY REFERENCED ROUTINE.
;
RCLDT:
	LXI	H,SENTFLG ;HAS THIS INFO ALREADY BEEN SENT?
	MOV	A,M
	ORA	A
	RNZ		;YES - SKIP SENDING AGAIN
	MVI	M,SENTTOK ;NO - SET FLAG SHOWING IT HAS BEEN SENT
	LHLD	TRKPTR	;GET POINTER TO DRIVE & TRACK INFO
			;AND FALL THRU TO SEND IT TO BIOS
;
; SEND DRIVE AND TRACK INFO FROM A DDB TO THE BIOS.
; INTERNALLY REFERENCED ROUTINE.
;
SENDDT:
	PUSH	H	;SAVE TRACK POINTER
	INX	H	;POINT TO DRIVE
	MOV	C,M	;GET DRIVE TO REG C
	CALL	CSETDSK	;PASS DISK
	POP	H	;GET TRACK POINTER
	MOV	C,M	;GET TRACK TO REG C
	JMP	CSETTRK	;PASS TRACK AND RETURN FROM THERE
;
; MOVE A SECTOR FROM ONE PLACE IN MEMORY TO ANOTHER.  GIVE
; ERROR MESSAGE IF DATA CAN'T BE WRITTEN INTO DESTINATION.
; INTERNALLY REFERENCED ROUTINE.
;
XFER:
	MVI	C,SECLEN ;COUNT NUMBER OF BYTES TO XFER IN C
XF1:
	LDAX	D	;GET BYTE FROM SOURCE
	MOV	M,A	;STORE INTO DESTINATION
	XRA	M	;DID IT GO?
	CNZ	MEMHIT	;NO - GIVE WARNING MESSAGE
	INX	H	;INCREMENT DEST POINTER
	INX	D	;INCREMENT SOURCE POINTER
	DCR	C	;DONE WITH MOVE YET?
	JNZ	XF1	;NO - KEEP MOVING
	RET		;YES - ALL DONE
;
; LOCAL MESSAGE PRINTER.  WE CAN'T USE BDOS BECAUSE BDOS IS NOT
; RE-ENTRANT (WE MIGHT HAVE GOTTEN HERE VIA BDOS).  FOLLOW
; THE CALL TO MSGP WITH THE TEXT OF THE MESSAGE.  THE LAST
; CHARACTER OF THE MESSAGE MUST HAVE BIT SEVEN ON (I.E. +80H)
; INTERNALLY REFERENCED ROUTINE.
;
MSGP:
	XTHL		;GET MESSAGE ADDRESS TO REG HL
MSG1:
	MOV	C,M	;GET MESSAGE CHARACTER
	PUSH	H	;SAVE MESSAGE ADDRESS DURING PRINTING
	CALL	CCONOUT	;SEND IT
	POP	H	;RESTORE MESSAGE ADDRESS
	MOV	A,M	;GET CHR JUST SENT
	INX	H	;POINT TO NEXT CHARACTER
	ORA	A	;WAS THAT THE LAST CHR TO PRINT?
	JP	MSG1	;NO - KEEP PRINTING
	XTHL		;YES - PUT MODIFIED RETURN ADDREESS
	RET		;BACK ON STACK AND RETURN PAST MESSAGE
	PAGE
;
;  \\\\\\\\\\\\\\\\                ////////////////
;   >>>>>>>>>>>>>>>>    R A M     <<<<<<<<<<<<<<<<
;  ////////////////   A R E A S    \\\\\\\\\\\\\\\\
;
;
; SAVE SPACE FOR A COPY OF THE BIOS JUMP TABLE BEFORE THE
; INSTALLATION OF FAST
;
SAVEJMP:
CBOOT:	 JMP	$-$
CWBOOT:	 JMP	$-$
CCONST:	 JMP	$-$
CCONIN:	 JMP	$-$
CCONOUT: JMP	$-$
CLIST:	 JMP	$-$
CPUNCH:	 JMP	$-$
CREADER: JMP	$-$
CHOME:	 JMP	$-$
CSETDSK: JMP	$-$
CSETTRK: JMP	$-$
CSETSEC: JMP	$-$
CSETDMA: JMP	$-$
CREAD:	 JMP	$-$
CWRITE:	 JMP	$-$
;
DIRADR	DW	0	;ADDRESS OF CURRENT DIRECTORY DDB
RDADR	DW	0	;ADDRESS OF CURRENT READ TRACK DDB
WRADR	DW	0	;ADDRESS OF CURRENT WRITE TRACK DDB
;
TRKPTR	DW	0	;POINTER TO TRACK & DRIVE INFO IN DDB
SENTFLG	DB	0	;ZERO IF TRACK & DRIVE HAVEN'T BEEN SENT
WRTERRF	DB	0	;WRITE DDB ERROR FLAG, 0 IF NO ERROR
;
REQDSK	DB	0FFH
REQTRK	DB	0FFH
REQSEC	DB	0FFH
REQDMA	DW	0FFFFH
;
	DS	10H	;AT LEAST AS MUCH STACK AS CCP
;
	ORG	(($-1) OR 255) + 1 ;ORG TO NEXT PAGE BOUNDRY
STACK:
;
CODELEN	EQU	$-BASE
;
	ORG	CODELEN	;INFORM OPERATOR OF CODE LENGTH
	END	FASTNTRY

