;
; 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 INFO
RMATION 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