
      UIDE And UHDD Caching For User Drivers (Software Information)
      -------------------------------------------------------------

   UIDE or UHDD can be called by user device drivers to cache read and
   write data.   In the following document, except where noted, "UIDE"
   refers to the UHDD driver as well.

   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 but
   the SS:SP-registers are available and may be used as needed.   UIDE
   does not "touch" the FS: and GS: registers, so user drivers needing
   them must save and reload them.   The "callback" subroutine must do
   all I-O for the request originally "submitted" to UIDE for caching.
   After I-O ends, the "callback" subroutine clears the carry flag for
   "no error" or sets carry and sets an error code in the AL-register.
   The "callback" subroutine then exits to UIDE with a "retf" command.

   Upon final 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
   reg. = 0FFh) declared by UIDE.   This denotes trouble moving cached
   data or cache-table values to or from XMS memory, thus a DIAGNOSTIC
   may be necessary for XMS memory!

   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 UDvrSeg 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:  Driver "attributes":  0C800h for UIDE or
                                                  08000h for UHDD.

      Bytes 0000Ah-00011h:  UHDD driver name, "UHDD$" and 3 zeros for
                                 caching, "UHDD-SA$" without caching.

      Bytes 000A4h-000A7h:  UIDE I.D. bytes, ASCII letters "UIDE".

      Bytes 000A8h-000A9h:  Cache validity code, 0559Ch for UIDE/UHDD.
                                 (fixed "pushf"/"push bp" commands).

   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 "UHDD-SA$" name is found
   in bytes 000Ah-0011h, the "stand alone" UHDD is loaded, and caching
   calls must NOT be issued to it!

   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, also if the "stand alone" UIDE (no cache!) is in use.   If
   UIDE is not found, or its caching is not available, the user-driver
   variable  UDvrSeg  can remain "zero" and be tested on I-O requests,
   as in the sample logic below.

   User drive "CacheUnit" numbers must range from 030h to 05Fh.   UIDE
   permanently reserves units 000h to 027h for its disks/diskettes and
   units 028h to 02Fh for its 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 that are already reserved!    Drivers must avoid
   altering UIDE_IOF bits 0 or 1, as they are UIDE's "busy" and "flush
   common cache" flags!

   User drivers can have their data cached into UIDE's "Common" cache,
   "CD/DVD" cache, "User-1" and "User-2" caches (unavailable when UHDD
   with only 2 caches is present), or they can set their own parameter
   table and use their own cache.   Each UIDE cache parameter table is
   28 bytes and may be assembled as follows:

     STLmt  dw  0       ;Binary-search table limit offset, initially
                        ;  zero and maintained/updated only by UIDE.
     LUTop  dw  0FFFFh  ;Least-recently-used "top" index, initially
                        ;  -1 and maintained/updated only by UIDE.
     LUEnd  dw  0FFFFh  ;Least-recently-used "end" index, initially
                        ;  -1 and maintained/updated only by UIDE.
     Blocks dw  0       ;Number of cache blocks, 1 to 65,535.
     SecPB  db  0       ;Number of 512-byte sectors per cache block.
     SecSC  db  0       ;Sector-shift count.   4 for 8K-byte cache
                        ;  blocks, incremented by 1 for each doubling
                        ;  of the block size.  Maximum value of 7 for
                        ;  the maximum 64K-byte cache blocks.
     MidPt  dw  0       ;Binary-search "midpoint" index.   Must be a
                        ;  power of 2 (512, 1024, etc.), and must be
                        ;  less-than or exactly equal-to the number
                        ;  of cache blocks.
     XBase  dd  0       ;32-bit XMS "base" address, indicating the
                        ;  beginning of all cached data.   XMS memory
                        ;  must be allocated by the user:  (512 bytes
                        ;  per sector) * (sectors per block) * (cache
                        ;  block count), plus 16 bytes per block for
                        ;  the tables/buffer described below.
     DTAddr dd  0       ;32-bit cache data-table address, 12 bytes
                        ;  per cache block, normally set directly
                        ;  past the cached data.   No init needed.
     STAddr dd  0       ;32-bit binary-search table address, 2 bytes
                        ;  per cache block, usually set directly past
                        ;  the cache data-table.   MUST be set during
                        ;  driver-init to "ascending" or "descending"
                        ;  numbers, from zero to the maximum cache
                        ;  block -1.
     STBuff dd  0       ;32-bit binary-search buffer address, 2 bytes
                        ;  per cache block, usually set directly past
                        ;  the binary-search table.   No init needed.

   All but the first 3 parameters are constants.   Every UIDE cache is
   completely controlled by its binary-search table ("STLmt") plus its
   least-recently-used list ("LUTop"/"LUEnd").    A cache is "flushed"
   on media-changes or a bad I-O error merely by resetting these three
   variables to their initial values of zero, 0FFFFh, and 0FFFFh!   No
   reloading of cache-block numbers at "STAddr" is needed!

   One of the 4 UIDE parameter-table addresses or a user table address
   must be set into UIDE's "working" parameter-table address, prior to
   each caching request.   Note that this is a segment/offset address,
   not a 32-bit number!   The parameter table address is normally that
   of UIDE's "User-1" cache, but user drivers can "select" any desired
   cache, including their own if active.    UIDE fetches all the cache
   parameters before I-O and saves the ending "STLmt/LUTop/LUEnd" data
   in the parameter table after I-O has ended.   UIDE will "flush" the
   selected cache on any request passed to the BIOS (disk/diskette I-O
   excepted), on any diskette or CD/DVD media-change, or on XMS errors
   involving its cache tables.   Other error handling is at the user's
   discretion.

   User drivers must access certain variables within UIDE/UHDD for any
   caching request.   Note the DIFFERENCES in the driver addresses for
   a user "callback" pointer, device-type code, and working parameter-
   table pointer!   UIDE and UHDD use different designs, since UIDE is
   a "block device" driver, and UHDD is a "character" driver that also
   provides a small /B "stand alone" option.   Other user variables in
   the list below have the SAME addresses in UIDE or UHDD, to simplify
   doing user-driver caching.     Variables used during a user caching
   request are as follows --

   UIDE_IOF equ  byte  ptr 013h    ;I-O flags & cache-unit "bitmap".

   UHDD_HDL equ  word ptr  008h    ;UHDD XMS "handle" for user drivers
                                   ;  sharing UHDD's XMS buffer (UDVD2
                                   ;  etc.).   NOT available for UIDE!
   UHDD_CBA equ  dword ptr 014h    ;UHDD user "callback" address, seg:
                                   ;  offset (not 32-bit!).   Must NOT
                                   ;  be set when UHDD is "busy"!
   UHDD_TYP equ  byte  ptr 01Bh    ;UHDD device-type code, always 07Eh
                                   ;  for a user device.   Must NOT be
                                   ;  set when UHDD is "busy"!
   UHDD_TBL equ  dword ptr 058h    ;UHDD "working" parameter address,
                                   ;  set for any request.   Must NOT
                                   ;  be set when UHDD is "busy"!

   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 a user device.   Must NOT be
                                   ;  set when UIDE is "busy"!
   UIDE_TBL equ  dword ptr 02Ch    ;UIDE "working" parameter address,
                                   ;  set for any request.   Must NOT
                                   ;  be set when UIDE is "busy"!

   UIDE_CCT equ  word ptr  09Ch    ;"Common" parameter-table offset.
   UIDE_U1T equ  word ptr  09Eh    ;"User-1" parameter-table offset.
   UIDE_U2T equ  word ptr  0A0h    ;"User-2" parameter-table offset.
   UIDE_CDT equ  word ptr  0A2h    ;"CD/DVD" parameter-table offset.
   UIDE_ENT equ  000C0h            ;User-driver "entry" offset.
   UIDE_SEG equ  word ptr  0E0h    ;Parameter-table segment for all
                                   ;  driver cache parameter tables.

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

            ..
            ..
            ..
           mov   cx,UDvrSeg        ;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/UHDD "callback" address.
           pop   es:UIDE_CBA
           mov   es:UIDE_TYP,07Eh  ;Set "user device" type in UIDE/UHDD.
           push  es:UIDE_SEG       ;Set "User-1" cache parameter-table
           push  es:UIDE_U1T       ;  address, usually.   Any of the 4
           pop   es:UIDE_TBL       ;  UIDE caches, or a user-specified
                                   ;  cache, can be used as necessary.
           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.
           ..
           ..

   All UIDE caches can be "flushed" if needed, as described above.   An
   example of the required logic is as follows:

           ..
           ..
           cmp   UDvrSeg,0         ;UIDE absent or caching unavailable?
           je    NoFlsh            ;If either, no cache "flush" needed.
           push  di                ;Save DI- and ES-registers.
           push  es
           mov   es,UDvrSeg        ;Get our main UIDE driver segment.
           mov   di,es:UIDE_U1T    ;Set "User-1" table pointer/segment.
           mov   es,es:UIDE_SEG
           and   word ptr es:[di],0     ;Reset search-limit index.
           or    dword ptr es:[di+2],-1 ;Reset LRU "top"/"end" indexes.
           pop   es                     ;Reload ES- and DI-registers.
           pop   di
   NoFlsh: ..
           ..

   Note that the above "flush" routine should be used only when UIDE is
   not "busy", i.e. UIDE_IOF bit 0 is NOT set!   This allows drivers to
   share any of UIDE's caches properly.   A flush issued by one driver,
   while UIDE is doing I-O for another driver using the SAME cache, may
   cause a CRASH!!

