266 lines · 14.9 KB · Assembly
RawDownload
; ------------------------------------------------------------; name      C; type      asm; cve       —; year      unknown; os        DOS; authors   unknown; source    krisyotam; archived  krisyotam (2026); notes     —; ------------------------------------------------------------INTERRUPTS      SEGMENT AT 0H    ;This is where the disk interrupt        ORG     13H*4            ;holds the address of its service routineDISK_INT        LABEL   DWORDINTERRUPTS      ENDS CODE_SEG        SEGMENT        ASSUME  CS:CODE_SEG        ORG     100H            ;ORG = 100H to make this into a .COM fileFIRST:  JMP     LOAD_CACHE      ;First time through jump to initialize routine         CPY_RGT DB      '(C)1985 S.Holzner'     ;A signature in bytes        TBL_LEN DW      64 ;<-- # OF SECTORS TO STORE IN CACHE, MIN=24, MAX=124.  ;THIS IS THE ONLY PLACE YOU MUST SET THIS NUMBER. EACH SECTOR = 512 BYTES.        TIME    DW      0       ;Time used to time-stamp each sector        OLD_CX  DW      0       ;Stores original value of CX (CX is used often)        LOW_TIM DW      0       ;Used in searching for least recently used sect.        INT13H  DD      0       ;Stores the original INT 13H address        RET_ADR LABEL   DWORD   ;Playing games with the stack here to preserve        RET_ADR_WORD    DW      2 DUP(0)            ;flags returned by Int 13H DISK_CACHE      PROC    FAR     ;The Disk interrupt will now come here.        ASSUME  CS:CODE_SEG        CMP     AX,201H         ;Is this a read (AH=2) of 1 sector (AL=1)?        JE      READ            ;Yes, jump to Read        CMP     AH,3            ;No. Perchance a write or format?        JB      OLD_INT         ;No, release control to old disk Int.        JMP     WRITE           ;Yes, jump to WriteOLD_INT:PUSHF                   ;Pushf for Int 13H's final Iret        CALL    INT13H          ;Call the Disk Int        JMP     PAST            ;And jump past all usual PopsREAD:   PUSH    BX              ;Push just about every register ever heard of        PUSH    CX        PUSH    DX        PUSH    DI        PUSH    SI        PUSH    DS        PUSH    ES        MOV     DI,BX       ;Int 13H gets data address as ES:BX, switch to ES:DI        ASSUME  DS:CODE_SEG     ;Make sure all labels found correctly        PUSH    CS              ;Move CS into DS by pushing CS, popping DS        POP     DS        MOV     OLD_CX,CX       ;Save original CX since we're about to use it        CMP     DH,0            ;DH holds requested head -- head 0?        JNE     NOT_FAT1        ;Nope, this can't be the first Fat sector        CMP     CX,6            ;If this is the directory, check if we have a        JE      FAT1            ; new disk.        CMP     CX,2            ;Track 0 (CH)? Sector 2 (CL)?        JNE     NOT_FAT1        ;If not, this sure isn't the FAT1FAT1:   CALL    FIND_MATCH  ;DOS reads in this sector first to check disk format        JCXZ    NONE            ;We'll use it for a check-sum. Do we have it        MOV     BX,DI           ; stored yet? CX=0-->no. If yes, restore BX        MOV     CX,OLD_CX       ; and CX from original values        PUSHF                   ;And now do the Pushf and call of Int13H to read        CALL    INT13H          ; FAT1        JC      ERR             ;If error, leave        MOV     CX,256          ;No error, FAT1 was read, check our valueREPE    CMPSW                   ; with CMPSW -- if no match, disk was changed        JCXZ    BYE             ;Everything checks out, Bingo, exit.        LEA     SI,TABLE        ;New Disk! Zero all the old disk's sectors        MOV     CX,TBL_LEN      ;Loop over all entries, DL holds drive #CLR:    CMP     DS:[SI+2],DL    ;Is this stored sector from the old disk?        JNE     NO_CLR          ;Nope, don't clear this entry        MOV     WORD PTR DS:[SI],0      ;Match, zero this entry, zero first wordNO_CLR: ADD     SI,518      ;Move on to next stored sector (512 bytes of stored        LOOP    CLR         ; sector and 3 words of identification & time-stamp)        JMP     BYE             ;Reset for new disk, let's leaveNONE:   CALL    STORE_SECTOR    ;Store FAT1 if there was no match to it        JC      ERR             ;Error -- exit ungraciously        JMP     BYE             ;No Error, Bye.NOT_FAT1:                       ;The requested sector was not FAT1. Let's        CALL    FIND_MATCH      ;get it. Or do we have it already?        JCXZ    NO_MATCH        ;No, jump to No_Match, store sector        MOV     CX,512          ;ES:DI and DS:SI already set up from Find_MatchREP     MOVSB                   ;Move 512 bytes to requested memory area        CMP     WORD PTR [BX+4],0FFFFH          ;Is this a a directory sector?        JE      BYE             ;Yes, don't reset time (already highest poss.)        INC     TIME            ;No, reset the time, this sector just accessed        MOV     AX,TIME         ;Move time into Time word of sector's 3 words        MOV     [BX+4],AX       ; of identification        JMP     BYE             ;And leave. If there's an article you'd like toNO_MATCH:                       ;see, by all means write in C/O PC Magazine.        CALL    STORE_SECTOR    ;Don't have this sector yet, get it.        JC      ERR             ;If read failed, exit with errorBYE:    CLC                     ;The exit point. Clear carry flag, set AX=1        MOV     AX,1            ; CY=0 --> no error, AH=0 --> error code = 0ERR:    POP     ES              ;If error, preserve flags and AX with error code        POP     DS              ;Pop all conceivable registers (except AX)        POP     SI        POP     DI        POP     DX        POP     CX              ;Now that the flags are set, we want to get the        POP     BX              ;old flags off the stack (put there by originalPAST:   POP     CS:RET_ADR_WORD ;Int call) To do that we save the return address        POP     CS:RET_ADR_WORD[2]      ;first and then pop the flags harmlessly        POP     CS:OLD_CX       ;into Old_CX, and then jump to RET_ADR.        JMP     CS:RET_ADR      ;Done with read. Now let's consider write.WRITE:  PUSH    BX              ;Push all registers, past and present        PUSH    CX        PUSH    DX        PUSH    DI        PUSH    SI        PUSH    DS        PUSH    ES        PUSH    AX        CMP     AX,301H         ;Is this a write of one sector?        JNE     NOSAVE          ;No, don't save it in the sector bank        PUSH    CS              ;Yep, set DS (for call to Int13H label) and        POP     DS              ; write this sector out        PUSHF        CALL    INT13H        JNC     SAVE       ;If there was an error we don't want to save sector        POP     CS:OLD_CX       ;Save AH error code, Pop old AX into Old_CX        JMP     ERR             ;And jump to an ignoble exitSAVE:   MOV     OLD_CX,CX       ;We're going to save this sector.        MOV     DI,BX           ;Set up DI for string move (to store written        CALL    FIND_MATCH      ; sector. Do we have it in memory? (set SI)        JCXZ    LEAVE           ;Nope, Leave (like above's Bye).        XCHG    DI,SI           ;Exchange destination and source        PUSH    ES              ;Set up DS:SI to point to where data written        POP     DS              ; from. We'll then use a string move        PUSH    CS              ;Set up ES so ES:DI points to sector bank        POP     ES              ; SI was set by Find_Match, Xchg'd into DI        MOV     CX,512          ;Get ready to move 512 bytesREP     MOVSB                   ;Here we goLEAVE:  POP     AX              ;Here is the leave        JMP     BYE             ;Which only pops AX and then jumps to ByeNOSAVE: PUSH    CS              ;More than 1 sector written, don't save but        POP     DS              ; do zero stored sectors that will be written        MOV     AH,0            ;Use AX as loop index (AL=# of sectors to write)TOP:    PUSH    CX              ;Save CX since destroyed by Find_Match        CALL    FIND_MATCH      ;Do we have this one?        JCXZ    NOPE            ;Nope if CX = 0        MOV     WORD PTR [BX],0 ;There is a match, zero this sectorNOPE:   POP     CX              ;Restore CX, the sector index        INC     CL              ;Move on to next one        DEC     AX              ;Decrement loop index        JNZ     TOP             ;And, unless that gives 0, go back againPOPS:   POP     AX              ;Pop 'em all, starting with AX        POP     ES        POP     DS        POP     SI        POP     DI        POP     DX        POP     CX        POP     BX        JMP     OLD_INT         ;And go back to OLD_INT for write.DISK_CACHE      ENDP FIND_MATCH      PROC    NEAR    ;This routine finds a sector in the sector bank        PUSH    AX              ;And returns SI set to sector's entry, BX set        LEA     SI,SECTORS      ; to the beginning of the 'table' -- the 3 words        LEA     BX,TABLE        ;that precede all sectors. If there was no match        MOV     AX,TBL_LEN      ; CX=0. When Int13H called, CH=trk #, CL=sec. #        XCHG    AX,CX           ; DH=head #, DL=Drive #. Get Tbl_Len into CXFIND:   CMP     DS:[BX],AX      ;Compare stored sector's original AX to current        JNE     NO              ;If not, not.        CMP     DS:[BX+2],DX    ;If so, check DX of stored sector with current        JE      GOT_IT          ;Yes, there is a match, leaveNO:     ADD     BX,518          ;Point to next Table entry        ADD     SI,518          ;And next sector too        LOOP    FIND            ;Keep looping until there is a matchGOT_IT: POP     AX              ;If there is no match, CX will be left 0        RET                     ;ReturnFIND_MATCH      ENDP STORE_SECTOR    PROC    NEAR    ;This routine, as it says, stores sectors        MOV     BX,DI           ;Original BX (ES:BX was original data address)        MOV     CX,OLD_CX       ; and CX restored (CX=trk#, Sector#)        PUSHF                   ;Pushf for Int 13H's Iret and call it        CALL    INT13H        JNC     ALL_OK          ;If there was an exit, exit ignominiously        JMP     FIN             ;If error, leave CY flag set, code in AH, exitALL_OK: PUSH    CX              ;No error, push used registers        PUSH    BX              ; and find space for sector in sector bank        PUSH    DX        LEA     DI,SECTORS      ;Point to sector bank        LEA     BX,TABLE        ; and Table        MOV     CX,TBL_LEN      ; and get ready to loop over all of them toCHK0:   CMP     WORD PTR DS:[BX],0      ;find if there is an unused sector        JE      FOUND           ;If the first word is 0, use this sector        ADD     DI,518          ;But this one isn't so update DI, SI and        ADD     BX,518          ; loop again        LOOP    CHK0        MOV     LOW_TIM,0FFFEH  ;All sectors were filled, find least recently        LEA     DI,SECTORS      ; used and write over that one        LEA     SI,TABLE        MOV     CX,TBL_LEN      ;Loop over all stored sectorsCHKTIM: MOV     DX,LOW_TIM      ;Compare stored sector to so-far low time        CMP     [SI+4],DX        JA      MORE_RECENT     ;If this one is more recent, don't use it        MOV     AX,DI           ;This one is older than previous oldest        MOV     BX,SI           ;Store sector bank address (DI) and table        MOV     DX,[SI+4]       ; entry (now in SI)        MOV     LOW_TIM,DX      ;And update the Low Time to this oneMORE_RECENT:        ADD     DI,518          ;Move on to next stored sector        ADD     SI,518          ;And next table entry        LOOP    CHKTIM          ;Loop again until all covered        MOV     DI,AX           ;Get Sector bank address of oldest into DIFOUND:  POP     DX              ;Restore used registers        POP     SI              ;Old BX (data read-to-address) --> SI        POP     CX        MOV     [BX],CX         ;Store the new CX as the sector's first word        MOV     [BX+2],DX       ;2nd word of Table is sector's DX        INC     TIME            ;Now find the new time        MOV     AX,TIME         ;Prepare to move it into 3rd word of Table        CMP     DH,0            ;Is this directory or FAT? (time-->FFFF)        JNE     SIDE1           ;If head is not 0, check other head        CMP     CX,9            ;Head zero, trk# 0, first sector? (directory)        JLE     DIR             ;Yes, this is a piece we always want stored        JMP     NOT_DIR         ;No, definitely not FAT or directorySIDE1:  CMP     DH,1            ;Head 1?        JNE     NOT_DIR         ;No, this is not File Alloc. Table or directory        CMP     CX,2            ;Part of the top of the directory?        JA      NOT_DIR         ;No, go to Not_Dir and set timeDIR:    MOV     AX,0FFFFH       ;Dir or FAT, set time high so always keptNOT_DIR:MOV     [BX+4],AX       ;Not FAT or dir, store the incremented time        PUSH    ES              ;And now get the data to fill the sector        POP     DS              ;SI, DI already set. Now set ES and DS for        PUSH    CS              ; string move.        POP     ES        MOV     CX,512          ;Move 512 bytesREP     MOVSB                   ;Right here        CLC                     ;Clear the carry flag (no error)FIN:    RET                     ;Error exit here (do not reset CY flag)STORE_SECTOR    ENDPTABLE:  DW      3 DUP(0)        ;Table and sector storage begins right hereSECTORS:                        ;First thing to write over is the following                                ; booster program.LOAD_CACHE        PROC    NEAR  ;This procedure intializes everything        LEA     BX,CLEAR        ASSUME  DS:INTERRUPTS   ;The data segment will be the Interrupt area        MOV     AX,INTERRUPTS        MOV     DS,AX        MOV     AX,word ptr DISK_INT    ;Get the old interrupt service routine        MOV     word ptr INT13H,AX      ; address and put it into our location         MOV     AX,word ptr DISK_INT[2]                                        ; INT13H so we can call it.        MOV     word ptr INT13H[2],AX        MOV     word ptr DISK_INT,OFFSET DISK_CACHE  ;Now load address of Cache        MOV     word ptr DISK_INT[2],CS   ;routine into the Disk interrupt        MOV     AX,TBL_LEN              ;The number of sectors to store in cache        MOV     CX,518                  ;Multiply by 518 (3 words of id and 512        MUL     CX                      ; bytes of sector data)        MOV     CX,AX                   ;Also, zero all the bytes so thatZERO:   MOV     BYTE PTR CS:[BX],0      ; Store_Sector will find 1st word a 0,        INC     BX                      ; indicating virgin territory.        LOOP    ZERO        MOV     DX,OFFSET TABLE         ;To attach in memory, add # bytes to        ADD     DX,AX                   ;store to Table's location and use        INT     27H                     ; Int 27HLOAD_CACHE        ENDPCLEAR:        CODE_SEG        ENDS        END     FIRST           ;END "FIRST" so 8088 will go to FIRST first.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t