               UIDE Data Caching For User Drivers
             ======================================

UIDE can be called by user drivers to cache their read/write data.

A user driver must have a "callback" subroutine which does its I-O.
Every I-O request to a user driver will cause a call to UIDE, which
immediately calls-back the user routine for output and for uncached
input.    Data already in cache is simply moved to the user's input
buffer, without a call-back.

A user "callback" subroutine is entered on UIDE's stack.    All 32-
bit CPU registers, plus the DS: and ES: segment registers, are free
for use.   If the FS: or GS: registers are required, the "callback"
logic must save and restore them.    The "callback" subroutine must
handle I-O for the request submitted to UIDE for caching.     After
I-O ends, it clears the carry bit for "no errors" or sets carry and
sets an error code in the AL-register.    Then, the "callback" sub-
routine exits back to UIDE with a "retf" command.

Upon return from a caching call to UIDE, the user driver must check
for a device I-O error and also an "XMS error" (carry set, AH=0FFh)
declared by UIDE.   This denotes a problem moving cached data to or
from XMS memory, thus a memory DIAGNOSTIC may be necessary!

During their initialization, user drivers must examine memory, from
about 00600h to F8000h, to find UIDE and save its "segment" address
(e.g. in variable UIDESeg, as shown in the sample logic below), for
use in caching calls.   User drivers must search for a segment with
the following data at the indicated addresses --

  Bytes 00004h-00005h:   0C800h (fixed driver "attributes").

  Bytes 0009Ch-0009Fh:   Upper-case letters  UIDE  (driver I.D.).
  Byte  000A0h:          09Ch (fixed "pushf"   command).
  Byte  000A1h:          055h (fixed "push bp" command).

User drivers must AVOID a segment with bytes 00000h-00003h equal to
0FFFFFFFFh (uninitialized header "link" word), which may be the DOS
loader's "leftover" image of UIDE!

If a user driver finds UIDE, but its UIDE_IOF flag byte has no free
cache-unit numbers (bits 2-7 of UIDE_IOF are all ones), user-driver
caching is not available, and UIDE caching calls may NOT be issued!
This is true if all cache-unit numbers have been reserved for other
drivers or if the "stand alone" UIDE (no cache!) is present.   When
UIDE is not found, or when its caching is unavailable, the variable
UIDESeg  (UIDE's segment) can remain "zero" and be tested on an I-O
request, as in the sample logic below.

User drive "CacheUnit" number must range from 030h to 05Fh.    UIDE
permanently reserves units 000h to 02Fh for its disks/diskettes and
CD/DVD drives.   Cache-unit numbers are reserved/released in groups
of 8 by setting/clearing an appropriate bit in UIDE_IOF as follows:

UIDE_IOF bit 2:  Reserves cache-units 030h to 037h.
         bit 3:  Reserves cache-units 038h to 03Fh.
         bit 4:  Reserves cache-units 040h to 047h.
         bit 5:  Reserves cache-units 048h to 04Fh.
         bit 6:  Reserves cache-units 050h to 057h.
         bit 7:  Reserves cache-units 058h to 05Fh.

User drivers must set or clear these bits themselves, when a driver
is being "initialized" or "unloaded".    Other drivers must NOT use
cache-unit numbers which are already reserved!   Drivers must avoid
altering UIDE_IOF bits 0 or 1, as they are UIDE's "busy" and "flush
cache" flags!

After doing the above initialization procedures, a user driver then
calls UIDE for data caching with the following logic --

UIDE_IOF equ  byte  ptr 013h    ;UIDE flags & cache-unit "bitmap".
UIDE_CBA equ  dword ptr 018h    ;UIDE user "callback" address, seg:
                                ;  offset (not 32-bit!).   Must NOT
                                ;  be set when UIDE is "busy"!
UIDE_TYP equ  byte  ptr 01Fh    ;UIDE device-type code, always 07Eh
                                ;  for any user devices.   Must NOT
                                ;  be set when UIDE is "busy"!
UIDE_ENT equ  000B8h            ;Fixed user-caching "entry" offset.

         ..
         ..
         ..
        mov   cx,UIDESeg        ;UIDE absent or caching unavailable?
        jcxz  NoUIDE            ;If either, go do normal device I-O.
        mov   es,cx             ;Set saved UIDE driver segment.
        mov   eax,BufferAddr    ;Set EAX = 32-bit buffer address.
                                ;("VDS lock" address, NOT seg:offs!).
        mov   cl,Sectors        ;Set CL = number of 512-byte sectors.
        mov   ch,RdWrCmd        ;Set CH = 00h if read, 02h if write.
        mov   di,LBAHighBits    ;Set DI =  upper 16 LBA addr. bits.
        mov   dx,LBAMidBits     ;Set DX = middle 16 LBA addr. bits.
        mov   si,LBALowBits     ;Set SI =  lower 16 LBA addr. bits.
                                ;(Unused hi-order bits must be 0!).
        movzx bp,CacheUnit      ;Set BP = 8-bit cache unit number.
        pushf                   ;Stack current CPU flags.
        cli                     ;Disable CPU interrupts.
        bts   es:UIDE_IOF,0     ;Is UIDE currently busy?
        jc    BsyErr            ;Yes?  Handle as an error!
        push  cs                ;Set "callback" subroutine seg:offs
        push  offset OurCBRtn   ;  in UIDE "callback" address.
        pop   es:UIDE_CBA
        mov   es:UIDE_TYP,07Eh  ;Set "user device" in UIDE byte 01Fh.
        push  cs                ;Stack UIDE "Int 13h" exit address.
        push  offset Return
        pushf                   ;Stack "dummy" flags and BP-reg.,
        push  bp                ;  loaded when UIDE does its exit.
        xor   bx,bx             ;Ensure UIDE "base register" is zero.
        push  es                ;Do 32-bit "jump" (not call) to UIDE.
        push  UIDE_ENT
        retf
Return: jc    CachErr           ;If carry is set, go handle error!
        ..                      ;No UIDE errors if we arrive here.
        ..
        ..
BsyErr: popf                    ;If busy, reload interrupt state.
        ..                      ;Handle UIDE-busy error as desired.
        ..
        ..
NoUIDE: ..                      ;No UIDE caching -- do normal I-O.
        ..
        ..

If a media-change or serious error for a user drive requires a UIDE
cache "flush", the following logic can be used --

        ..
        ..
        cmp   UIDESeg,0         ;UIDE absent or caching unavailable?
        je    NoFlsh            ;If either, no cache "flush" needed.
        mov   es,UIDESeg        ;Set saved UIDE driver segment.
        or    es:UIDE_IOF,002h  ;Post UIDE "flush cache" flag.
                                ;("Flush" occurs before next I-O).
NoFlsh: ..
        ..

