home *** CD-ROM | disk | FTP | other *** search
/ Phoenix CD 2.0 / Phoenix_CD.cdr / 11a / cache3.zip / CACHE3.ASM < prev    next >
Assembly Source File  |  1986-11-15  |  26KB  |  563 lines

  1. INTERRUPTS      SEGMENT AT 0H    ;This is where the disk interrupt
  2.         ORG     13H*4            ;holds the address of its service routine
  3. DISK_INT        LABEL   DWORD  
  4. INTERRUPTS      ENDS
  5.  
  6. CODE_SEG        SEGMENT
  7.                 ASSUME  CS:CODE_SEG
  8.                 org     2h
  9. pspsize         dw      ?               ;size of physical machine
  10.                 org     80h
  11. parml           db      0  
  12.                 org     81h
  13. parm            db      127 dup (' ')
  14.  
  15.                 ORG     100H            ;ORG = 100H  this is a .COM file
  16. FIRST:          JMP     load_pgm        ;jump to initialize routine
  17.                                 
  18. CPY_RGT DB      '(C)85S.Holzner'        ;A signature in bytes
  19.  
  20. TBL_LEN         DW      40h     ;<-- # OF SECTORS IN CACHE, MIN=24, MAX=124.
  21.   
  22. entries         dw      0       ;used to count calls
  23. matches         dw      0       ;count hits
  24. highwater       dw      0       ;number of sectors in cache used
  25. TIME            DW      0       ;Time used to time-stamp each sector
  26. OLD_CX          DW      0       ;Stores original value of CX (CX is used often)
  27. LOW_TIM         DW      0       ;Used in searching for least recently used sect.
  28. INT13H          DD      0       ;Stores the original INT 13H address
  29. RET_ADR         LABEL   DWORD   ;Playing games with the stack here to preserve 
  30. RET_ADR_WORD    DW      2 DUP(0)            ;flags returned by Int 13H
  31. drive           db      0ffh     ;specific crive to be cached, if any 1,2,3,4...
  32.  
  33. DISK_CACHE      PROC    FAR     ;The Disk interrupt will now come here.
  34.         ASSUME  CS:CODE_SEG     
  35.  
  36.         cmp     drive,0ffh      ;specific disk to be cached?
  37.         je      diskany         ;if not, just go on
  38.         cmp     dl,drive        ;if so, is this the right one?
  39.         jne     old_int         ;if not, just return
  40. diskany:
  41.         inc     entries         ;count entries into program
  42.  
  43.         CMP     AX,0201H        ;Is this a read (AH=2) of 1 sector (AL=1)?
  44.         JE      READ            ;Yes, jump to Read
  45.         CMP     AH,3            ;No. Perchance a write or format?
  46.         JB      OLD_INT         ;No, release control to old disk Int.
  47.         cmp     al,0            ;Check sector count
  48.         je      old_int         ;Some HD functions don't use, so exit
  49.         JMP     WRITE           ;Yes, jump to Write
  50.  
  51. OLD_INT:PUSHF                   ;Pushf for Int 13H's final Iret
  52.         CALL    INT13H          ;Call the Disk Int
  53.         JMP     PAST            ;And jump past all usual Pops
  54.  
  55. READ:   PUSH    BX              ;Push just about every register ever heard of
  56.         PUSH    CX
  57.         PUSH    DX
  58.         PUSH    DI      
  59.         PUSH    SI        
  60.         PUSH    DS
  61.         PUSH    ES
  62.         MOV     DI,BX       ;Int 13H gets data address as ES:BX, switch to ES:DI
  63.         ASSUME  DS:CODE_SEG     ;Make sure all labels found correctly
  64.         PUSH    CS              ;Move CS into DS by pushing CS, popping DS
  65.         POP     DS
  66.         MOV     OLD_CX,CX       ;Save original CX since we're about to use it
  67.         sti                     ;Turn ints back on now
  68.         test    dl,80h          ;check hard disk request
  69.         jnz     not_fat1        ;if yes, bypass disk-swap tests
  70.         CMP     DH,0            ;DH holds requested head -- head 0?        
  71.         JNE     NOT_FAT1        ;Nope, this can't be the first Fat sector  
  72.         CMP     CX,6            ;If this is the directory, check if we have a
  73.         JE      FAT1            ; new disk.
  74.         CMP     CX,2            ;Track 0 (CH)? Sector 2 (CL)?
  75.         JNE     NOT_FAT1        ;If not, this sure isn't the FAT1        
  76. FAT1:   CALL    Find_sector     ;DOS reads in this sector first to 
  77.                                 ;    check disk format
  78.         JCXZ    NONE            ;We'll use it for a check-sum. Do we have it
  79.         MOV     BX,DI           ; stored yet? CX=0-->no. If yes, restore BX
  80.         MOV     CX,OLD_CX       ; and CX from original values
  81.         PUSHF                   ;And now do the Pushf and call of Int13H to read
  82.         CALL    INT13H          ; FAT1
  83.         JC      ERR             ;If error, leave
  84.  
  85.         MOV     CX,256          ;No error, FAT1 was read, check our value
  86.         REPE    CMPSW           ; with CMPSW -- if no match, disk was changed
  87.         JCXZ    BYE             ;Everything checks out, Bingo, exit.
  88.  
  89.         LEA     SI,TABLE        ;New Disk! Zero all the old disk's sectors
  90.         MOV     CX,TBL_LEN      ;Loop over all entries, DL holds drive #
  91. CLR:    CMP     DS:[SI+2],DL    ;Is this stored sector from the old disk?
  92.         JNE     NO_CLR          ;Nope, don't clear this entry
  93.         MOV     WORD PTR DS:[SI],0      ;Match, zero this entry, zero first word
  94. NO_CLR: ADD     SI,518      ;Move on to next stored sector (512 bytes of stored 
  95.         LOOP    CLR         ; sector and 3 words of identification & time-stamp)
  96.         JMP     BYE             ;Reset for new disk, let's leave
  97.  
  98. NONE:   CALL    STORE_SECTOR    ;Store FAT1 if there was no match to it
  99.         JC      ERR             ;Error -- exit ungraciously
  100.         JMP     BYE             ;No Error, Bye.
  101.  
  102. NOT_FAT1:                       ;The requested sector was not FAT1. Let's
  103.         CALL    Find_sector     ;get it. Or do we have it already?
  104.         JCXZ    NO_MATCH        ;No, jump to No_Match, store sector
  105.         inc     matches
  106.         MOV     CX,512          ;ES:DI and DS:SI already set up from Find_sector
  107. REP     MOVSB                   ;Move 512 bytes to requested memory area
  108.  
  109.         CMP     WORD PTR [BX+4],0FFFFH          ;Is this a a directory sector?
  110.         JE      BYE             ;Yes, don't reset time (already highest poss.)
  111.         INC     TIME            ;No, reset the time, this sector just accessed
  112.         MOV     AX,TIME         ;Move time into Time word of sector's 3 words
  113.         MOV     [BX+4],AX       ; of identification
  114.         JMP     BYE             ;And leave. If there's an article you'd like to
  115.  
  116. NO_MATCH:                       ;see, by all means write in C/O PC Magazine.
  117.         CALL    STORE_SECTOR    ;Don't have this sector yet, get it.
  118.         JC      ERR             ;If read failed, exit with error
  119. BYE:    CLC                     ;The exit point. Clear carry flag, set AX=1
  120.         MOV     AX,1            ; CY=0 --> no error, AH=0 --> error code = 0
  121. ERR:    POP     ES              ;If error, preserve flags and AX with error code
  122.         POP     DS              ;Pop all conceivable registers (except AX)
  123.         POP     SI
  124.         POP     DI
  125.         POP     DX
  126.         POP     CX              ;Now that the flags are set, we want to get the
  127.         POP     BX              ;old flags off the stack (put there by original
  128.  
  129. PAST:   POP     CS:RET_ADR_WORD ;Int call) To do that we save the return address
  130.         POP     CS:RET_ADR_WORD[2]      ;first and then pop the flags harmlessly
  131.         POP     CS:OLD_CX       ;into Old_CX, and then jump to RET_ADR.
  132.         JMP     CS:RET_ADR      ;Done with read. Now let's consider write.
  133.  
  134. WRITE:  PUSH    BX              ;Push all registers, past and present
  135.         PUSH    CX              
  136.         PUSH    DX
  137.         PUSH    DI
  138.         PUSH    SI
  139.         PUSH    DS
  140.         PUSH    ES
  141.         PUSH    AX
  142.  
  143.         sti
  144.         CMP     AX,0301H        ;Is this a write of one sector?
  145.         JNE     NOSAVE          ;No, don't save it in the sector bank
  146.         PUSH    CS              ;Yep, set DS (for call to Int13H label) and
  147.         POP     DS              ; write this sector out
  148.         PUSHF     
  149.         CALL    INT13H
  150.         JNC     SAVE            ;If there was an error  don't save sector
  151.         POP     CS:OLD_CX       ;Save AH error code, Pop old AX into Old_CX
  152.         JMP     ERR             ;And jump to an ignoble exit
  153.  
  154. SAVE:   MOV     OLD_CX,CX       ;We're going to save this sector.
  155.         MOV     DI,BX           ;Set up DI for string move (to store written
  156.         CALL    Find_sector     ; sector. Do we have it in memory? (set SI)
  157.         JCXZ    LEAVE           ;Nope, Leave (like above's Bye).
  158.         XCHG    DI,SI           ;Exchange destination and source
  159.         PUSH    ES              ;Set up DS:SI to point to where data written
  160.         POP     DS              ; from. We'll then use a string move
  161.         PUSH    CS              ;Set up ES so ES:DI points to sector bank
  162.         POP     ES              ; SI was set by Find_sector, Xchg'd into DI
  163.         MOV     CX,512/2        ;Get ready to move 512 bytes
  164.         REP     MOVSW           ;Here we go
  165.  
  166. LEAVE:  POP     AX              ;Here we leave
  167.         JMP     BYE             ;Which only pops AX and then jumps to Bye
  168.  
  169. NOSAVE: PUSH    CS              ;More than 1 sector written, don't save but
  170.         POP     DS              ; do zero stored sectors that will be written
  171.         MOV     AH,0            ;Use AX as loop index (AL=# of sectors to write)
  172. TOP:    PUSH    CX              ;Save CX since destroyed by Find_sector
  173.         CALL    Find_sector      ;Do we have this one?
  174.         JCXZ    NOPE            ;Nope if CX = 0
  175.         MOV     WORD PTR [BX],0 ;There is a match, zero this sector
  176. NOPE:   POP     CX              ;Restore CX, the sector index
  177.         INC     CL              ;Move on to next one
  178.         DEC     AX              ;Decrement loop index
  179.         JNZ     TOP             ;And, unless that gives 0, go back again
  180. POPS:   POP     AX              ;Pop 'em all, starting with AX
  181.         POP     ES
  182.         POP     DS
  183.         POP     SI
  184.         POP     DI
  185.         POP     DX
  186.         POP     CX
  187.         POP     BX
  188.         JMP     OLD_INT         ;And go back to OLD_INT for write.
  189.  
  190. DISK_CACHE      ENDP
  191.  
  192. Find_sector     PROC    NEAR    ;This routine finds a sector in the sector bank
  193.         PUSH    AX              ;And returns SI set to sector's entry, BX set
  194.                                 ;to the beginning of the 'table' entry.
  195.                                 ;If there was no match, CX = 0       
  196.  
  197.  
  198.         LEA     SI,SECTOR_data      
  199.         LEA     BX,TABLE        
  200.  
  201.         MOV     AX,TBL_LEN      ; When Int13H called, CH=trk #, CL=sec. #
  202.         XCHG    AX,CX           ; DH=head #, DL=Drive #. Get Tbl_Len into CX
  203.  
  204. FIND:   CMP     DS:[BX],AX      ;Compare stored sector's original AX to current 
  205.         JNE     NO              ;If not, not.
  206.         CMP     DS:[BX+2],DX    ;If so, check DX of stored sector with current
  207.         JE      GOT_IT          ;Yes, there is a match, leave
  208. NO:     ADD     BX,518          ;Point to next Table entry
  209.         ADD     SI,518          ;And next sector too
  210.         LOOP    FIND            ;Keep looping until there is a match
  211.  
  212. GOT_IT: POP     AX              ;If there is no match, CX will be left 0
  213.         RET                     ;Return
  214.  
  215. Find_sector      ENDP
  216.  
  217. STORE_SECTOR    PROC    NEAR    ;This routine, as it says, stores sectors
  218.         MOV     BX,DI           ;Original BX (ES:BX was original data address)
  219.         MOV     CX,OLD_CX       ; and CX restored (CX=trk#, Sector#)
  220.         PUSHF                   ;Pushf for Int 13H's Iret and call it
  221.         CALL    INT13H
  222.         JNC     ALL_OK          ;If there was an exit, exit ignominiously
  223.         JMP     FIN             ;If error, leave CY flag set, code in AH, exit
  224.  
  225. ALL_OK: PUSH    CX              ;No error, push used registers
  226.         PUSH    BX              ; and find space for sector in sector bank
  227.         PUSH    DX
  228.         lea     DI,SECTOR_data      ;Point to sector bank
  229.         LEA     BX,TABLE        ; and Table
  230.  
  231.         MOV     CX,TBL_LEN      ; and get ready to loop over all of them to
  232. CHK0:   CMP     WORD PTR DS:[BX],0      ;find if there is an unused sector
  233.         JE      FOUND           ;If the first word is 0, use this sector
  234.         ADD     DI,518          ;But this one isn't so update DI, SI and 
  235.         ADD     BX,518          ; loop again
  236.         LOOP    CHK0  
  237.  
  238.         MOV     LOW_TIM,0FFFEH  ;All sectors were filled, find least recently
  239.         LEA     DI,SECTOR_data      ; used and write over that one
  240.         LEA     SI,TABLE
  241.         MOV     CX,TBL_LEN      ;Loop over all stored sectors
  242. CHKTIM: MOV     DX,LOW_TIM      ;Compare stored sector to so-far low time
  243.         CMP     [SI+4],DX       
  244.         JA      MORE_RECENT     ;If this one is more recent, don't use it
  245.         MOV     AX,DI           ;This one is older than previous oldest
  246.         MOV     BX,SI           ;Store sector bank address (DI) and table
  247.         MOV     DX,[SI+4]       ; entry (now in SI)
  248.         MOV     LOW_TIM,DX      ;And update the Low Time to this one
  249. MORE_RECENT:                    
  250.         ADD     DI,518          ;Move on to next stored sector
  251.         ADD     SI,518          ;And next table entry
  252.         LOOP    CHKTIM          ;Loop again until all covered
  253.         MOV     DI,AX           ;Get Sector bank address of oldest into DI
  254. FOUND:  mov     highwater,cx    ;remember how much we searched
  255.         POP     DX              ;Restore used registers
  256.         POP     SI              ;Old BX (data read-to-address) --> SI
  257.         POP     CX
  258.  
  259.         MOV     [BX],CX         ;Store the new CX as the sector's first word
  260.         MOV     [BX+2],DX       ;2nd word of Table is sector's DX
  261.         INC     TIME            ;Now find the new time
  262.         MOV     AX,TIME         ;Prepare to move it into 3rd word of Table
  263.         CMP     DH,0            ;Is this directory or FAT? (time-->FFFF)
  264.         JNE     SIDE1           ;If head is not 0, check other head
  265.         CMP     CX,9            ;Head zero, trk# 0, first sector? (directory)
  266.         JLE     DIR             ;Yes, this is a piece we always want stored
  267.         JMP     NOT_DIR         ;No, definitely not FAT or directory
  268.  
  269. SIDE1:  CMP     DH,1            ;Head 1?
  270.         JNE     NOT_DIR         ;No, this is not FAT or directory
  271.         CMP     CX,2            ;Part of the top of the directory?
  272.         JA      NOT_DIR         ;No, go to Not_Dir and set time
  273. DIR:    MOV     AX,0FFFFH       ;Dir or FAT, set time high so always kept
  274.         dec     time            ;don't waste time on priority stuff
  275. NOT_DIR:MOV     [BX+4],AX       ;Not FAT or dir, store the incremented time
  276.  
  277.         PUSH    ES              ;And now get the data to fill the sector
  278.         POP     DS              ;SI, DI already set. Now set ES and DS for 
  279.         PUSH    CS              ; string move. 
  280.         POP     ES
  281.         MOV     CX,512          ;Move 512 bytes
  282. REP     MOVSB                   ;Right here
  283.         CLC                     ;Clear the carry flag (no error)
  284. FIN:    RET                     ;Error exit here (do not reset CY flag)
  285.  
  286. STORE_SECTOR    ENDP
  287.  
  288.         ;When Int13H called, CH=trk #         CL=sector #
  289.         ;                    DH=head # (0,1)  DL=Drive # (0,1,2,3)
  290.         ;                    AH=operation     AL=number of sectors
  291.         ;                    ES:BX=I/O buffer address
  292.       
  293.         ;  Operations are: 2=Read Sector(s)     3=Write Sectors
  294.         ;                  4=Verify Sectors     5=Format track     
  295.  
  296.         ;Format Buffer Contains 4 byte commands as reqd for 1 track
  297.         ;         Byte 1 = Track Number  Byte 2 = Head Number
  298.         ;         Byte 3 = Sector Number Byte 4 = Bytes/Sector Code
  299.  
  300.         ;Bytes/Sector code is: 00=128 01=256, 02=512 03=1024  
  301.  
  302.         ;Carry flag is 0 if operation successful, 1 for error
  303.  
  304.         ;AL = Number of bytes actually processed for Read/Write/Verify 
  305.  
  306.  
  307.         ;Table entry layout is:
  308.  
  309.           ;1st word of table entry: sector's CX (track number and sector number)
  310.           ;2nd word of Table entry: sector's DX (head number  and drive number)
  311.           ;3rd word is the incremented "time"
  312.           ;      unless sector is FAT or DIR, in which case it is FFFFh
  313.           ;Remaining 256 words are 512 bytes of the sector's contents. 
  314.  
  315.  
  316. zero      proc    near
  317.           pop     ax                   ;fix up the stack (offset)
  318. ;         pop     ax                   ;fix up the stack (CS)
  319.           pop     ax                   ;get back program length
  320.           mov     cx,ax                ;Also, zero all the bytes
  321.           shr     cx,1                 ;divide iterations by 2
  322.           push    cs                   
  323.           pop     es                   ;set up es
  324.           mov     si,offset table
  325.           mov     di,offset sector_data
  326.           repe    movsw                ;zero the world
  327.  
  328.           MOV     DX,OFFSET TABLE      ;To attach in memory, add # bytes to 
  329.           ADD     DX,AX                ; store to Table's location and use
  330.           INT     27H                  ; Int 27H exit and make resident
  331. zero      endp 
  332.  
  333. TABLE:    DW      3 DUP(0)        ;Table and sector storage begins right here
  334. Sector_data:  
  335.  
  336.                         ;First thing to write over is the following
  337.                         ; booster program.
  338.  
  339. cr            equ   13
  340. lf            equ   10
  341. parmptr       dw    81h           ;address of 'current' byte in parms
  342. radix         dw    10            ;radix for conversions
  343.  
  344. drivemsg      db    cr,lf,'Disk '
  345. drivewhere    db    ' ',' cache:' 
  346.               db    cr,lf,'$'
  347. driveerrmsg   db    cr,lf,'Disk err - use A-D',cr,lf,'$'
  348. sizeerrmsg    db    cr,lf,'Size err - use 24-124',cr,lf,'$' 
  349.  
  350. sizemsg       db    cr,lf
  351. sizedec       db    6 dup(' '),' bytes at '
  352. lochex        db    4 dup(' '),':0 '
  353. remdec        db    6 dup(' '),' free'
  354.               db    cr,lf,cr,lf,'$'
  355.  
  356. asciitable    db    '0123456789ABCDEF' ;table of characters 
  357.  
  358. asciibin      proc  near               ;si - source of characters (left end)
  359.                                        ;ax - contents in binary
  360.                                        ;cx - length in bytes
  361.               xor   ax,ax 
  362.               xor   bx,bx 
  363. asciiloop:
  364.               mov   bl,[si]          ;get character pointed to by SI
  365.               cmp   bl,' '           ;is character a blank?
  366.               je    asciiexit        ;if so, that is it.
  367.               cmp   bl,'0'
  368.               jl    asciierror
  369.               mul   radix            ;multiply ax by radix
  370.               cmp   bl,'9'
  371.               jg    asciihex         ;if character > 9 must be hex
  372.               and   bl,0fh           ;pick up numeric part
  373. asciiadd:                            ;add to accumulator
  374.               add   ax,bx
  375.               inc   si               ;scan right
  376.               loop  asciiloop
  377.               cmp   bl,bl            ;set zero flag
  378.               jmp   asciiret
  379.  
  380. asciihex:     cmp   radix,10         ;radix >10?
  381.               jle   asciierror       ;if not, error
  382.               or    bl,20h           ;make lower case 
  383.               cmp   bl,'a'
  384.               jl    asciierror
  385.               cmp   bl,'f'
  386.               jg    asciierror
  387.               sub   bl,87            ;turn 'A' into 10, etc
  388.               jmp   asciiadd
  389.  
  390. asciierror: 
  391.               xor   ax,ax                       ;return a zero on error
  392.  
  393. asciiexit:
  394.               cmp        bl," "                 ;set zero flag = no error
  395. asciiret:      
  396.               mov        parmptr,si             ;remember where we are at
  397.               mov        parml,cl               ;remember our length
  398.               ret
  399.  
  400. asciibin      endp
  401.  
  402. binascii      proc  near               ;di - target of characters (left just)
  403.                                        ;dx - most significant bits
  404.                                        ;ax - least significant bits
  405.                                        ;storage - radix
  406.               mov   si,offset asciitable  
  407.               xor   bh,bh              ;clear bh
  408.               xchg  bp,dx              ;ax,dx  contain data
  409. binloop:
  410.               xchg  ax,bp              ;get high word (dx)
  411.               xor   dx,dx              ;clear result
  412.               div   radix              ;divide ax by radix
  413.               xchg  bp,ax              ;get low word  (ax)
  414.               div   radix              ;divide 
  415.               mov   bl,dl              ;get into a register
  416.               mov   dl,cs:[bx+si]      ;get right ascii character
  417.               mov   [di],dl            ;and drop at target
  418.               dec   di                 ;set up for next character
  419.               cmp   ax,0               ;make sure something's left
  420.               jnz   binloop            ;if there is more loop
  421.               ret
  422.  
  423. binascii      endp 
  424.  
  425.  
  426. lineout       proc       near                   ;proc to type out a line
  427.               mov        byte ptr [di],cr       ;move in carriage return
  428.               inc        di
  429.               mov        byte ptr [di],lf       ;move in line feed
  430.               inc        di
  431.               mov        byte ptr [di],'$'      ;move in stopper
  432.               mov        ah,9h                  ;do screen i/o
  433.               int        21h
  434.               ret
  435.  
  436. lineout       endp 
  437.  
  438. scanparm      proc       near                   ;get start of next parm in SI
  439.               xor        cx,cx
  440.               mov        al,' '                 ;put blank in accumulator
  441.               mov        si,parmptr             ;get offset of parameter
  442.               mov        cl,parml               ;get length of remaining parm
  443.               cmp        cl,0                   ;is there any parm left?
  444.               je         scanret                ;if not, just exit
  445.  
  446. scanloop:     lodsb                             ;get byte pointed to by SI
  447.               cmp        al,' '                 ;is it blank?
  448.               jne        scangot
  449.               loop       scanloop               ;if so, loop on
  450.               jmp        scanset
  451. scangot:
  452.               dec        si                     ;back up over character
  453. scanset:
  454.               mov        parmptr,si             ;remember where we are at
  455.               mov        parml,cl               ;remember our length
  456. scanret:      
  457.               cmp        al,' '                 ;set zero flag
  458.               ret
  459. scanparm      endp
  460.  
  461.  
  462. Load_pgm  PROC    NEAR  ;This procedure intializes everything
  463.  
  464.           call       scanparm     ;call scan for start of parm
  465.           je         noparm       ;if none, skip checking parms
  466.           call       asciibin     ;turn chars into binary in ax
  467.  
  468.           mov        dx,offset sizeerrmsg
  469.           jnz        parmerr      ;if errors, skip out
  470.           mov        tbl_len,24   ;set minimum size
  471.           cmp        ax,24        ;smaller than minimum?
  472.           jl         parmerr      ;if so, complain
  473.           cmp        ax,124       ;larger than max?
  474.           jng        loadsize     ;if not, do what he asks
  475.           mov        tbl_len,124  ;if error then set max
  476.           jmp        parmerr      ;and complain
  477. loadsize:
  478.           mov        tbl_len,ax
  479.  
  480.           call       scanparm     ;call scan for start of parm
  481.           je         noparm       ;if none, skip checking parms
  482.  
  483.           and        al,255-20h   ;make upper case
  484.           mov        drivewhere,al;store in message
  485.           mov        dx,offset driveerrmsg
  486.           or         al,20h       ;make lower case
  487.           cmp        al,'a'       ;drive must be a to d
  488.           jl         parmerr
  489.           cmp        al,'d'
  490.           jg         parmerr      ;if not, will ignore
  491.           sub        al,'a'       ;make a = 0, b = 1, etc
  492.           mov        drive,al        
  493.  
  494.           mov         dx,offset drivemsg 
  495. parmerr:
  496.           mov        ah,9         ;dos function: type string 
  497.           int        21h 
  498.  
  499. noparm:
  500.           ASSUME  DS:INTERRUPTS   ;The data segment will be the Interrupt area
  501.           MOV     AX,INTERRUPTS
  502.           MOV     DS,AX
  503.  
  504.           MOV     AX,word ptr DISK_INT     ;Get the old interrupt service routine
  505.           MOV     word ptr INT13H,AX       ; address and put it into our location
  506.           MOV     AX,word ptr DISK_INT[2]  ; INT13H so we can call it.
  507.           MOV     word ptr INT13H[2],AX
  508.  
  509.           MOV     word ptr DISK_INT,OFFSET DISK_CACHE  ;Now load the address of Cache
  510.           MOV     word ptr DISK_INT[2],CS  ;routine into the Disk interrupt
  511.  
  512.           push    cs              ;back to normal addressibility
  513.           pop     ds                                            
  514.           assume  ds:code_seg     ;Make sure all labels found correctly
  515.  
  516.           MOV     AX,TBL_LEN      ;The number of sectors to store in cache
  517.           MOV     CX,518          ;Multiply by 518 (3 words of id and 512
  518.           MUL     CX              ; bytes of sector data) in AX
  519.  
  520.           push    ax              ;save ax 
  521.  
  522.           xor     dx,dx           ;clear hi bytes 
  523.           mov     di,offset sizedec+5  ;set target 
  524.           call    binascii
  525.  
  526.           push    cs              ;cs - where we are loaded
  527.           pop     ax              ;get it into ax for conversion
  528.  
  529.           mov     radix,16        ;housekeeping
  530.           xor     dx,dx           ;clear hi bytes 
  531.           mov     di,offset lochex+3  ;set target 
  532.           call    binascii
  533.  
  534.           push    cs              ;cs - where we are loaded
  535.           pop     bx              ;get it into bx for subtraction
  536.           mov     ax,pspsize      ;size of machine 5000h = 320k
  537.           sub     ax,bx           ;knock off where we are at
  538.           mov     dx,ax           ;get high bytes in accum ext
  539.           mov     cl,12
  540.           shr     dx,cl           ;orient correctly
  541.           mov     cl,4
  542.           shl     ax,cl           ;shift bytes in accum
  543.           pop     bx              ;get our length off stack
  544.           push    bx
  545.           sub     ax,bx           ;deduct length of this pgm
  546.           sbb     dx,0            ;if borrow, count it
  547.           mov     radix,10        ;now base 10
  548.           mov     di,offset remdec+5  ;set target 
  549.           call    binascii
  550.  
  551.           mov     dx,offset sizemsg 
  552.           mov     ah,9                 ;dos function: type string 
  553.           int     21h 
  554.  
  555.           call    zero                 ;finish up
  556.  
  557. Load_pgm  ENDP 
  558.  
  559. CODE_SEG  ENDS
  560.           END     FIRST           ;END 8088 will go to FIRST first.
  561.  
  562.