        TITLE   'ccb - cache functions for rxdos'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  CCB Cache Manager for RxDOS                                  ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This material  was created as a published version  of a DOS  ;
        ;  equivalent product.   This program  logically  functions in  ;
        ;  the same way as  MSDOS functions and it  is  internal  data  ;
        ;  structure compliant with MSDOS 6.0                           ;
        ;                                                               ;
        ;  This product is distributed  AS IS and contains no warranty  ;
        ;  whatsoever,   including  warranty  of   merchantability  or  ;
        ;  fitness for a particular purpose.                            ;
        ;                                                               ;
        ;                                                               ;
        ;  (c) Copyright 1990, 1997. Api Software and Mike Podanoffsky  ;
        ;      All Rights Reserved Worldwide.                           ;
        ;                                                               ;
        ;  This product is protected under copyright laws and  may not  ;
        ;  be reproduced  in whole  or in part, in any form  or media,  ;
        ;  included but not limited to source listing, facsimile, data  ;
        ;  transmission, cd-rom, or  floppy disk without the expressed  ;
        ;  written consent of the author.                               ;
        ;                                                               ;
        ;  License  for  distribution  for commercial  use  or  resale  ;
        ;  required from:                                               ;
        ;                                                               ;
        ;  Api Software                                                 ;
        ;  12 South Walker Street                                       ;
        ;  Lowell,  MA   01851                                          ;
        ;                                                               ;
        ;  internet: mikep@world.std.com                                ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Compile with MASM 5.1                                        ;
        ;...............................................................;

        include rxdosmac.asm
        include rxdosdef.asm

RxDOS   SEGMENT PARA PUBLIC 'CODE'
        assume cs:RxDOS, ds:RxDOS, es:RxDOS, ss:RxDOS

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Buffer Management                                            ;
        ;...............................................................;

        public CCBChanged
        public invalidateBuffers
        public linkBegCCB
        public locateCCBPHeader
        public readBuffer
        public readSelBuffer
        public SelBuffer
        public unlinkCCB
        public updateAllChangedCCBBuffers
        public updateCCB
        public updateChangedCCB
        public updateDriveBuffers

        extrn DevRead                           : near
        extrn DevWrite                          : near
        extrn getAddrDPB                        : near
        extrn _RxDOS_BufferList                 : near
        extrn _RxDOS_pCDS                       : dword
        extrn _DebugInterruptTrap               : near

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Selected Buffer                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   cx:dx  sector to read                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  pointer to selected buffer                           ;
        ;   nz     buffer contains valid data                           ;
        ;          all registers preserved                              ;
        ;...............................................................;

SelBuffer:
        SaveRegisters ax, bx, cx, dx

        les di, dword ptr ss:[ _RxDOS_BufferList ]
        mov bx, -1

selBuffer_04:
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isVALID )
        jz selBuffer_08                                 ; unallocated buffer -->

        cmp byte ptr es:[ ccbDrive ][ di ], al          ; compare drives
        jnz selBuffer_08                                ; not this block, try next -->

        cmp cx, word ptr es:[ ccbLBN. _high][ di ]      ; compare logical block numbers
        jnz selBuffer_08                                ; not this block, try next -->
        cmp dx, word ptr es:[ ccbLBN. _low ][ di ]      ; compare logical block numbers
        jz selBuffer_36                                 ; buffer located -->

selBuffer_08:
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isONHOLD )
        jnz selBuffer_12                                ; if on hold, don't use -->
        mov bx, di                                      ; save last buffer address

selBuffer_12:
        mov di, word ptr es:[ ccbNext ][ di ]
        cmp di, -1
        jnz selBuffer_04
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if could not find a matching buffer, find an empty buffer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, bx
        cmp di, -1                                      ; buffer address available ?
        jnz selBuffer_30                                ; use the last referenced buffer -->

        les di, dword ptr ss:[ _RxDOS_BufferList ]      ; else must seatch buffers again

selBuffer_16:
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isVALID )
        jz selBuffer_32                                 ; unallocated buffer -->

        cmp word ptr es:[ ccbNext ][ di ], -1
        jz selBuffer_30                                 ; must write all, use least used -->
        mov di, word ptr es:[ ccbNext ][ di ]
        jmp selBuffer_16 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  prep returned buffer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
selBuffer_30:
        call updateCCB                                  ; just in case update required

selBuffer_32:
        mov byte ptr es:[ ccbDrive     ][ di ], al      ; set drive
        mov word ptr es:[ ccbLBN. _high][ di ], cx      ; set logical block number
        mov word ptr es:[ ccbLBN. _low ][ di ], dx      ; set logical block number
        mov byte ptr es:[ ccbStatus    ][ di ], 00      ; clear status
        call setFlagIfDriveIsRemote                     ; sets remote flag

        push es
        push di
        call getAddrDPB                                 ; pointer to dpb
        mov dx, es                                      ; pointer was in [es:bx]

        pop di
        pop es
        mov word ptr es:[ ccbDPB. _segment ][ di ], dx
        mov word ptr es:[ ccbDPB. _pointer ][ di ], bx

selBuffer_36:
        cmp di, word ptr ss:[ _RxDOS_BufferList. _pointer ]
        jz selBuffer_38                                 ; if already at beginning -->

        call unlinkCCB                                  ; unlink CCB buffer
        call linkBegCCB                                 ; link to front of CCB chain

selBuffer_38:
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isVALID )
        RestoreRegisters dx, cx, bx, ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Is Drive Remote                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al      drive                                                ;
        ;  es:di   points to ccb buffer                                 ;
        ;...............................................................;

setFlagIfDriveIsRemote:
        push ax
        push di
        push es

        mov cl, sizeCDS
        mul cl                                          ; ax contains current drive
        les di, dword ptr ss:[ _RxDOS_pCDS ]            ; actual address of CDS
        add di, ax                                      ; current drive
        test word ptr es:[ _cdsFlags ][ di ], _CDS_NETWORKEDDRIVE        
        pop es
        pop di
        jz IfDriveIsRemote_12                           ; if not remote -->

        or byte ptr es:[ ccbStatus ][ di ], ( ccb_isREMOTE )

IfDriveIsRemote_12:
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Remove CCB Buffer from Linked List                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es:di   points to ccb buffer                                 ;
        ;...............................................................;

unlinkCCB:
        pushf                                           ; save current interrupt status
        push ax
        push di
        cli                                             ; disable interrupts

        mov ax, word ptr es:[ ccbPrev ][ di ]
        mov di, word ptr es:[ ccbNext ][ di ]
        cmp ax, -1                                      ; no previous ?
        jnz unlinkCCB_06                                ; no -->
        mov word ptr ss:[ _RxDOS_BufferList. _pointer ], di  ; update beg of list if no previous

unlinkCCB_06:
        cmp di, -1                                      ; no next pointer ?
        jz unlinkCCB_08                                 ; skip removing its Prev pointer ->
        mov word ptr es:[ ccbPrev ][ di ], ax           ; next's prev fixed.

unlinkCCB_08:
        cmp ax, -1                                      ; no previous ?
        jz unlinkCCB_12                                 ; no -->
        xchg di, ax
        mov word ptr es:[ ccbNext ][ di ], ax           ; prev's next fixed.

unlinkCCB_12:
        pop di
        mov word ptr es:[ ccbPrev ][ di ], -1           ; unlink current pointers
        mov word ptr es:[ ccbNext ][ di ], -1
        pop ax
        popf                                            ; restore interrupt status
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Link CCB Buffer to Beginning of List                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es:di   points to ccb buffer                                 ;
        ;...............................................................;

linkBegCCB:
        pushf                                           ; save current interrupt status
        push ax
        cli                                             ; disable interrupts

        mov ax, di
        les di, dword ptr ss:[ _RxDOS_BufferList ]
        cmp ax, di                                      ; relinking ccb buffer to beg of list ?
        jz linkBegCCB_08                                ; yes, don't do it -->

        mov word ptr es:[ ccbPrev ][ di ], ax           ; previous to orig prev

        xchg di, ax
        mov word ptr es:[ ccbNext ][ di ], ax           ; this next to old next
        mov word ptr es:[ ccbPrev ][ di ], -1           ; current no more previous
        
        mov word ptr ss:[ _RxDOS_BufferList. _pointer ], di

linkBegCCB_08:
        pop ax
        popf
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Mark CCB buffer changed.                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es:di  pointer to selected buffer                            ;
        ;...............................................................;

CCBChanged:
        or byte ptr es:[ ccbStatus ][ di ], ( ccb_isDIRTY + ccb_isVALID )
        and byte ptr es:[ ccbStatus ][ di ], NOT ( ccb_isONHOLD )
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Locate from reference actual start of CCB buffer             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   si     pointer into a CCB buffer                            ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  pointer to CCB Header                                ;
        ;   cx     offset from start of data                            ;
        ;...............................................................;

locateCCBPHeader:
        les di, dword ptr ss:[ _RxDOS_BufferList ]

locateCCBPHeader_04:
        mov cx, si
        sub cx, di                                      ; how far from start of buffer
        jl locateCCBPHeader_08                          ; go to next -->
        cmp cx, sizeCCB                                 ; within a CCB buffer ?
        jg locateCCBPHeader_08                          ; go to next -->

        or cx, cx                                       ; no carry (leave offset alone )
        ret

locateCCBPHeader_08:
        mov di, word ptr es:[ ccbNext ][ di ]
        cmp di, -1
        jnz locateCCBPHeader_04

        stc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Buffer                                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx:dx  sector to read                                       ;
        ;   ax     drive                                                ;
        ;   bx     attributes to claim buffer                           ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  pointer to selected buffer                           ;
        ;...............................................................;

readBuffer:
        call SelBuffer                                  ; find a buffer
        jnz readBuffer_08                               ; if buffer is valid -->

        call readSelBuffer                              ; else read buffer

readBuffer_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Selected Buffer                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  pointer to selected buffer                           ;
        ;   cx:dx  sector to read                                       ;
        ;   ax     drive                                                ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  pointer to selected buffer                           ;
        ;   cx:dx  sector to read                                       ;
        ;...............................................................;

readSelBuffer:
        SaveAllRegisters

        push di
        push es
        mov byte ptr es:[ ccbDrive     ][ di ], al      ; set drive
        mov word ptr es:[ ccbLBN. _high][ di ], cx      ; set logical block number
        mov word ptr es:[ ccbLBN. _low ][ di ], dx      ; set logical block number
        mov byte ptr es:[ ccbStatus    ][ di ], 00      ; kill entry's value, validity

        mov bx, 0001
        lea di, offset ccbData [ di ]                   ; buffer address
        call DevRead                                    ; read buffer

        pop es
        pop di
        jc readSelBuffer_08                             ; if error -->
        mov byte ptr es:[ ccbStatus ][ di ], (ccb_isVALID)

readSelBuffer_08:
        RestoreAllRegisters
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Update CCB buffer, always.                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es:di  pointer to selected buffer                            ;
        ;  All Registers Preserved                                      ;
        ;...............................................................;

updateCCB:
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isVALID )
        jz updateCCB_08                                 ; data not valid -->
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isDIRTY )
        jnz updateChangedCCB                            ; data valid and changed -->

updateCCB_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Update Changed CCB buffer.                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es:di  pointer to selected buffer                            ;
        ;  All Registers Preserved                                      ;
        ;                                                               ;
        ;  CY returned in fail in write                                 ;
        ;...............................................................;

updateChangedCCB:
        Entry
        ddef _buffer, es, di

        SaveAllRegisters

        mov bx, 0001
        mov dx, word ptr es:[ ccbLBN. _low  ][ di ]     ; get sector
        mov cx, word ptr es:[ ccbLBN. _high ][ di ]
        mov al, byte ptr es:[ ccbDrive ][ di ]          ; get drive number
        cbw
        lea di, offset ccbData [ di ]                   ; buffer address
        call DevWrite                                   ; write t\disk 
        jc updateCCB_12                                 ; if error/ fail -->

        les di, dword ptr [ _buffer ][ bp ]
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isFAT )
        jz updateCCB_12                                 ; not a FAT buffer -->
        call updateAlternateFATTables                   ; update FAT tables
      ; jc updateCCB_12                                 ; if error/ fail -->

updateCCB_12:
        pushf
        les di, dword ptr [ _buffer ][ bp ]
        and byte ptr es:[ ccbStatus ][ di ], NOT ( ccb_isDIRTY + ccb_isONHOLD )
        popf                                            ; keep flag, but kill error buffer

        RestoreAllRegisters
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Update Alternate FAT Table                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Given a FAT sector address, copies the sector to the alter-  ;
        ;  nate FAT Table.                                              ;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  pointer to a FAT CCB Buffer                          ;
        ;...............................................................;

updateAlternateFATTables:

        Entry
        ddef  _ccb, es, di
        def   _dpbNumberFat
        def   _dpbFATSectors

        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isFAT )
        jz updateAlternateFATTables_22                  ; not a fat designated sector -->

        les bx, dword ptr es:[ ccbDPB  ][ di ]          ; get assigned DPB
        mov ax, word ptr es:[ _dpbSectorsPerFat ][ bx ]
        storarg _dpbFATSectors, ax

        mov ah, 0
        mov al, byte ptr es:[ _dpbNumCopiesFAT  ][ bx ]
        storarg _dpbNumberFat, ax

        getdarg es, di, _ccb
        mov dx, word ptr es:[ ccbLBN. _low  ][ di ]     ; get original sector
        mov cx, word ptr es:[ ccbLBN. _high ][ di ]
        mov al, byte ptr es:[ ccbDrive ][ di ]          ; get drive number
        cbw

        lea di, offset ccbData [ di ]                   ; buffer address

updateAlternateFATTables_08:
        dec word ptr [ _dpbNumberFat ][ bp ]
        jle updateAlternateFATTables_22                 ; if not more than 1 -->

        mov bx, 0001
        add dx, word ptr [ _dpbFATSectors ][ bp ]       ; adjust for fat size
        adc cx, 0000
        SaveRegisters es, di, dx, cx, ax
        call DevWrite
        RestoreRegisters ax, cx, dx, di, es
        jnc updateAlternateFATTables_08

updateAlternateFATTables_22:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Update All Drive Buffers                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;...............................................................;

updateDriveBuffers:
        les di, dword ptr ss:[ _RxDOS_BufferList ]

updDriveBuffer_04:
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isVALID )
        jz updDriveBuffer_16                            ; not a valid block -->
        cmp byte ptr es:[ ccbDrive ][ di ], al          ; compare drives
        jnz updDriveBuffer_16                           ; not this block, try next -->
        call updateCCB                                  ; update block if changed

updDriveBuffer_16:
        mov di, word ptr es:[ ccbNext ][ di ]
        cmp di, -1
        jnz updDriveBuffer_04

        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Update All Changed Buffers                                   ;
        ;...............................................................;

updateAllChangedCCBBuffers:
        pushf
        SaveAllRegisters
        les di, dword ptr ss:[ _RxDOS_BufferList ]

updateAllChanged_04:
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isVALID )
        jz updateAllChanged_16                          ; not a valid buffer -->
        call updateCCB                                  ; updated buffer if req'd

updateAllChanged_16:
        mov di, word ptr es:[ ccbNext ][ di ]
        cmp di, -1
        jnz updateAllChanged_04
        
        RestoreAllRegisters
        popf
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Invalidate Buffers for Specific Drive                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;...............................................................;

invalidateBuffers:
        les di, dword ptr ss:[ _RxDOS_BufferList ]

invBuffer_04:
        test byte ptr es:[ ccbStatus ][ di ], ( ccb_isVALID )
        jz invBuffer_16                                 ; not a valid buffer -->
        cmp al, byte ptr es:[ ccbDrive ][ di ]          ; same drive ?
        jnz invBuffer_16                                ; if not --> 

        and byte ptr es:[ ccbStatus ][ di ], NOT ( ccb_isVALID )

invBuffer_16:
        mov di, word ptr es:[ ccbNext ][ di ]
        cmp di, -1
        jnz invBuffer_04

        ret
                
RxDOS   ENDS
        END
