What is DoubleSpace and How Does It Work?

Archived content. No warranty is made as to technical accuracy. Content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

This chapter provides technical information and troubleshooting tips for DoubleSpace.

DoubleSpace is a technology that compresses data stored by the FAT file system in real time. Real time means that data is compressed and decompressed as it is written and read.

Real-time compression provides a transparent way to store data more compactly. Unlike file-compression utilities such as PKZIP, real-time compression compresses files when you store them and decompresses them when you need them. With a file-compression utility, you must run the utility once to compress a file, and again to decompress the file when you need to gain access to the file's contents.

On This Page

How Does DoubleSpace Compress Data?
How Does DoubleSpace Store Compressed Data on a Drive?
What is DBLSPACE.BIN?
What Happens During DoubleSpace Setup?
What Happens When MS-DOS 6 Starts With DoubleSpace?
MS-DOS 6 DoubleSpace Files and Their Functions
Removing DoubleSpace from Your Computer
Troubleshooting DoubleSpace
DoubleSpace System API Specification
Microsoft Real-Time Compression Interface (MRCI) Specification

How Does DoubleSpace Compress Data?

DoubleSpace compresses data by analyzing a block of data for repeated sequences, and then encoding these repeated sequences as a match in a very compact form. Consider the following sentence:

The rain in Spain falls mainly on the plain.

To compress this sentence, DoubleSpace first identifies the repeated sequences, and then writes them as <offset, length>. Offset is the number of bytes to the left of where the match starts, and length is the number of bytes matched. For example, this sentence would be compressed as follows:

The rain <3,3>Sp<9,4>falls m<11,3>ly on <34,37>pl<15,3>.

Once it identifies the matches, DoubleSpace encodes this information compactly. The key is to analyze the frequency of occurrence of the offset and length values, and then to choose very short encoding for the most common values.

How Does DoubleSpace Store Compressed Data on a Drive?

Compressed files are stored in a compressed volume file, or CVF. The CVF resides on an uncompressed drive, or host drive, and typically takes up almost all of the space on it. After you compress the existing files on a drive, the CVF contains the files from that drive. Usually, the host drive contains very little free space after compression, since the compressed volume file uses so much space. Because it is compressed, a CVF can store more data than the space it uses; for example, a typical CVF might use 10 MB of space on the host drive but contain 20 MB of compressed data.

A CVF is just a Hidden, Read-Only file until you mount it. Mounting a CVF establishes a connection between it and a drive letter, so that you can use that CVF as a drive.

DoubleSpace manages the files and free space on the CVF. A small amount of space is left over on the host drive for data that cannot or should not be compressed.

What is DBLSPACE.BIN?

DBLSPACE.BIN is a subsystem of the MS-DOS 6 operating system. It provides access to the data on compressed drives. DBLSPACE.BIN has two components:

  • A disk space manager

  • A MRCI server (a compress/decompress engine)

The Disk Space Manager

DBLSPACE.BIN is a compression subsystem that appears to the FAT file system like a block device driver; to the FAT file system, the CVF appears like any other drive. FAT stores information about files and directories, while DoubleSpace stores information only about the sector space it manages within the CVF. DoubleSpace can be thought of as a disk space manager.

The smallest FAT allocation unit is the cluster, which on a typical drive is 8K. On such a drive, a 1-byte file takes up 8K on the disk, and a 40K+1-byte file takes up 48K on the disk. This end-of-file waste, or cluster overhang, can be significant if the drive contains many small files.

The smallest DoubleSpace allocation unit is the sector, which is 512 bytes on most systems. This finer granularity is possible because DoubleSpace maintains its own disk allocation data structures within the CVF that map FAT clusters to DoubleSpace sectors. As a consequence, DoubleSpace minimizes cluster-overhang waste.

The Compress/Decompress Engine

The other component of DBLSPACE.BIN compresses and decompresses files as they are written to and read from the disk. There are tradeoffs in choosing a compression algorithm: speed of compression, speed of decompression, achieved compression ratio, and memory consumption. No algorithm provides the best of all. Speed is clearly important for real-time compression, and it is understood throughout the software industry which algorithms are optimal for use with real-time compression. Therefore, the compress/decompress engine in DBLSPACE.BIN, like that of add-in compression products, is based on Lempel-Ziv algorithms. Since Lempel-Ziv algorithms are extremely well-understood, it is not surprising that all real-time compression products, including DoubleSpace, achieve roughly the same compression ratios.

The efficiency of the compress/decompress code affects the speed of compression. DBLSPACE.BIN contains code that has been hand-tuned for optimum efficiency with the 8086/8088, 80286, and 80386 processors.

What Happens During DoubleSpace Setup?

When you run MS-DOS Setup, it installs MS-DOS 6 on your computer but does not compress your files. To actually compress your files, you must run DoubleSpace Setup by typing dblspace at the command prompt.

When DoubleSpace Setup starts, you can choose either Express or Custom Setup. Express Setup, which is the default, compresses the existing files on drive C. In Custom Setup, you can either compress the existing files on a selected drive (including drives other than C) or create a new, empty compressed drive and leave your existing files uncompressed.

When compressing the existing files on a drive, DoubleSpace first creates an empty CVF. DoubleSpace then repeats the following steps until all the files on that drive are compressed:

  1. DoubleSpace reads an uncompressed file from the host drive.

  2. It compresses the file.

  3. It writes the file to the CVF.

  4. It deletes the uncompressed file from the host drive.

  5. DoubleSpace repeats steps 1 through 4 until there is no more space on the CVF. When there is no more space on the CVF, DoubleSpace increases the size of the CVF and resumes the process at step 1.

Compressing existing files (also called compressing in place) takes roughly one minute per megabyte of data on the drive.

The compression process is both safe and restartable. DoubleSpace ensures the safety of the data by flushing disk buffers regularly. Because of this, no data ever exists only in memory; any data currently in memory is also on the disk. If DoubleSpace is interrupted while compressing data (for example, if the computer is accidentally restarted), DoubleSpace recovers automatically. DoubleSpace keeps track of its progress by adding information to the AUTOEXEC.BAT file; if interrupted, DoubleSpace uses this information to resume compressing exactly where it left off.

When compressing existing files, DoubleSpace compresses and moves most files from that drive to the CVF. When DoubleSpace finishes compressing the drive, nothing is left on the uncompressed (host) drive except a few system files, which are either Hidden/System or Hidden/System/Read-Only. These system files are:

Filename

Function

IO.SYS

An MS-DOS system file. The computer will start only if this file is stored on the uncompressed drive.

MSDOS.SYS

An MS-DOS system file. The computer will start only if this file is stored on the uncompressed drive.

DBLSPACE.BIN

The MS-DOS system file that provides access to compressed drives. The computer will start only if this file is stored on the uncompressed drive.

DBLSPACE.000

The CVF that holds the compressed data. When mounted, the CVF appears to be a normal drive.

DBLSPACE.INI

File that contains configuration information for DoubleSpace.

386SPART.PAR

The Windows permanent swap file, which must be stored on the uncompressed drive.

What Happens When MS-DOS 6 Starts With DoubleSpace?

When you start a computer on which DoubleSpace has been installed, the following occurs:

  1. IO.SYS loads, and then looks on the startup drive for the DBLSPACE.BIN file. If DBLSPACE.BIN is there, IO.SYS loads it at the top of conventional memory. If DBLSPACE.BIN is not there, IO.SYS detects that the computer is not running DoubleSpace, and continues the startup process as it does without compression.

  2. DBLSPACE.BIN reads settings from the DBLSPACE.INI file. These settings specify which CVFs DoubleSpace should mount as compressed drives and which drive letter to assign to each drive.

    If DBLSPACE.BIN cannot find the DBLSPACE.INI file on the startup drive or on drive C, DLBSPACE.BIN unloads itself from memory, and MS-DOS continues as it would on an uncompressed system.

  3. DBLSPACE.BIN mounts the CVFs specified by the DBLSPACE.INI file and assigns them the specified drive letters. (A CVF named DBLSPACE.000 indicates that the existing files on that drive were compressed. When DoubleSpace mounts a CVF with the filename DBLSPACE.000, it swaps the CVF's drive letter with the letter of its host drive.

  4. MS-DOS processes the CONFIG.SYS file exactly as it would on an uncompressed system.

  5. If MS-DOS encounters the command devicehigh=c:\dos\dblspace.sys /move in the CONFIG.SYS file and upper memory is available, then MS-DOS moves DBLSPACE.BIN from the top of conventional memory to upper memory. (DBLSPACE.BIN cannot initially load in upper memory because IO.SYS loads it before processing the CONFIG.SYS file. At this point in the startup process, no upper-memory manager is present, so upper memory blocks do not yet exist. Therefore IO.SYS must load DBLSPACE.BIN into conventional memory, which is less than optimal on 386 and 486 systems. But DBLSPACE.BIN loads at the top of conventional memory, not the bottom, so it can later relocate to upper memory without leaving a hole in lower conventional memory and fragmenting it.)

    If the CONFIG.SYS file does not contain a command for DBLSPACE.SYS, MS-DOS automatically relocates DBLSPACE.BIN from the top of conventional memory to the bottom of conventional memory when it has finished processing the CONFIG.SYS file (loading DBLSPACE.SYS by using the device command has the same effect).

    Note: The sys and format /s commands now copy DBLSPACE.BIN in addition to IO.SYS and MSDOS.SYS, so an MS-DOS 6 system floppy disk can be used to start a compressed system.

MS-DOS 6 DoubleSpace Files and Their Functions

The following table lists the DoubleSpace files and their functions.

Filename

Function

DBLSPACE.BIN

The portion of MS-DOS that provides access to the data on compressed drives. DBLSPACE.BIN is loaded at system startup, before MS-DOS processes the CONFIG.SYS file.

DBLSPACE.EXE

Program you run to create or modify DoubleSpace drives. The first time you run this program, it runs as DoubleSpace Setup. Thereafter, it runs as the DoubleSpace maintenance program.

DBLSPACE.HLP

Help file for the DBLSPACE program..

DBLSPACE.INF

Information file used by DoubleSpace Setup.

DBLSPACE.INI

DoubleSpace information file.

DBLSPACE.WIN

Used to keep track of Windows during DoubleSpace installation.

DBLSPACE.SYS

Device driver that, when loaded, determines the final location of DBLSPACE.BIN in memory. Can be used to move DBLSPACE.BIN to the top of conventional memory or to upper memory.

DBLSPACE.00x

Compressed volume file, which contains an entire compressed drive.

DBLWIN.HLP

Help file for the Windows DoubleSpace Info dialog.

For definitions of terms used in this section, run DoubleSpace, and then choose Index from the Help menu.

DBLSPACE.BIN

DBLSPACE.BIN is the component of the MS-DOS operating system that mounts the compressed volume file so you can access the files on your compressed drive. MS-DOS loads DBLSPACE.BIN before processing the commands in your CONFIG.SYS file. (The file must be located in the root directory of your startup drive); no device command is needed to load DBLSPACE.BIN. (For information about loading DBLSPACE.BIN into upper memory, see the section below on DBLSPACE.SYS.)

DBLSPACE.EXE

This program allows you to create, mount, or modify DoubleSpace drives. If you have not yet installed DoubleSpace, typing dblspace at the command prompt starts the DoubleSpace Setup program. If you have already installed DoubleSpace, then typing dblspace runs the DoubleSpace maintenance program, which provides menu commands you can use to modify or create DoubleSpace drives. You can also perform many of these functions from the command prompt, by adding switches and other parameters to the dblspace command. For more information, type help dblspace at the command prompt.

DBLSPACE.HLP

This file contains the help for the DoubleSpace Setup and maintenance programs. To use DoubleSpace help, press F1 from any DoubleSpace screen or dialog box, or choose Contents or Index from the Help menu in the DoubleSpace maintenance program.

DBLSPACE.INF

DoubleSpace Setup uses the DBLSPACE.INF file when you are creating a new DoubleSpace drive.

Commands under the [SpecialFiles] section indicate actions to be taken for specific files; the possible actions are copy=, ignore=, and move=. Without a path, any file with that name will match; wild cards (* and ?) are acceptable.

Programs under the [CopyFiles] section are optional utilities that will be left on the original startup drive if there is enough space.

Programs under the [dangerous] section will be disabled during DoubleSpace Setup.

Programs under the [prior] section will have the command devicehigh=c:\dos\dblspace.sys /move placed above them in the CONFIG.SYS file.

DBLSPACE.INI

The DBLSPACE.INI file is a text file with the System, Read-Only, and Hidden attributes. DoubleSpace stores this file in the root directory of your startup drive. The DBLSPACE.INI file contains variables that DoubleSpace uses when your computer starts. Although it is possible to change these variables yourself, you should do so only if you understand what they do and what the results might be. Before changing the DBLSPACE.INI file, you should make a backup copy of the file.

The DBLSPACE.INI file can contain one or more of the following variables:

MaxRemovableDrives=n

Specifies how many additional drives DoubleSpace should allocate memory for when your computer starts. DoubleSpace allocates 96 bytes of memory for each additional drive. The MaxRemovableDrives variable determines how many additional compressed drives you can create or mount without restarting your computer.

FirstDrive=X

Set by DoubleSpace each time it modifies the DBLSPACE.INI file. Do not change the FirstDrive variable yourself.

LastDrive=Y

Specifies the highest drive letter available for use by DoubleSpace. (If another program uses one of the drive letters specified for DoubleSpace, the highest drive letter available to DoubleSpace will be higher than the LastDrive variable.)

MaxFileFragments=n

Set by DoubleSpace to specify the degree of fragmentation to allow in all mounted compressed volume files. Do not change the MaxFileFragments variable yourself.

ActivateDrive=X,Yn

Specifies a compressed volume file that DoubleSpace should mount automatically when your computer starts. The DBLSPACE.INI file can contain as many ActivateDrive variables as there are CVFs.

The ActivateDrive variable requires the X, Y, and n parameters. The way DoubleSpace uses these parameters depends on whether the specified CVF was created by compressing existing files or by using free space.

If the specified CVF was created by compressing existing files

If the filename of the CVF is DBLSPACE.000, then that CVF was created by compressing existing files. In that case, DoubleSpace uses the parameters for the ActivateDrive variable as follows:

X

Specifies the drive letter to be assigned to the uncompressed (host) drive.

Y

Specifies the drive letter to be assigned to the compressed drive.

n

Specifies the filename extension of the compressed volume file. If the CVF was created by compressing existing files, n is set to 0.

For example, the following is a sample ActivateDrive variable for a CVF that was created by compressing existing files:

ActivateDrive=K,C0

This sample ActivateDrive variable specifies that the CVF's filename is DBLSPACE.000. When mounted, the compressed drive will be assigned drive letter C; the uncompressed drive (which contains the CVF) will be assigned drive letter K.

If the specified CVF was created by using free space on a drive

If the filename of the CVF is not DBLSPACE.000 (for example, DBLSPACE.002), then that CVF was created by using free space on an existing drive. In that case, DoubleSpace uses the parameters for the ActivateDrive variable as follows:

X

Specifies the drive letter to be assigned to the compressed drive.

Y

Specifies the drive letter to be assigned to the uncompressed (host) drive.

n

Specifies the filename extension of the compressed volume file. If the CVF filename is DBLSPACE.001, set n to 1; if the CVF filename is DBLSPACE.002, set n to 2; and so on

For example, the following is a sample ActivateDrive variable for a CVF that was created by using free space on an existing drive:

ActivateDrive=J,D1

This sample ActivateDrive variable specifies that the CVF's filename is DBLSPACE.001. When mounted, the compressed drive will be assigned drive letter J; the uncompressed drive (which contains the CVF) will be assigned drive letter D.

DBLSPACE.WIN

The DBLSPACE.WIN file is a temporary file that DBLSPACE.BIN generates to keep track of Windows information when compressing a drive. DoubleSpace deletes the file automatically after it finishes compressing the drive.

DBLSPACE.SYS

The DBLSPACE.SYS device driver does not provide access to compressed drives; it simply moves DBLSPACE.BIN to its final location in memory. For more information, type help dblspace.sys at the command prompt.

DBLSPACE.00x

A file with a name in the form DBLSPACE.00x is a compressed volume file (CVF) and contains all the files on a compressed drive. A compressed volume file is stored in the root directory of the uncompressed (host) drive.

When you compress the files on an existing drive, DoubleSpace names the associated compressed volume file DBLSPACE.000. When you create a new compressed drive, DoubleSpace names the associated CVF by using a number such as 001 (for example, DBLSPACE.001).

Most CVFs can store more data than the space they use; for example, a typical CVF might use 10 MB of space on its host drive but contain 20 MB of compressed data. When a CVF is mounted, or made active, it is assigned a drive letter and appears as a disk drive. The CVF's contents are then accessible and appear as normal files.

DBLWIN.HLP

This file contains help for the Windows DoubleSpace Info dialog. (To display this dialog, choose the DoubleSpace Info command from the Tools menu in File Manager.)

Removing DoubleSpace from Your Computer

There is no method for automatically removing DoubleSpace. There are two ways to remove it manually:

  • Back up the files on all your compressed drives, remove DoubleSpace, and then restore the backed-up files onto your uncompressed drive. (Note that all the files currently on your compressed drive(s) might not fit on your hard disk after you remove DoubleSpace.)

  • Move as many files as possible from your compressed drive to your uncompressed drive, reduce the size of your compressed drive to free space on the uncompressed drive, and keep moving files and shrinking the compressed drive until no more files remain on the compressed drive. Then, remove DoubleSpace.

    Important: The drive letter of your uncompressed drive may change after you remove DoubleSpace. If it does, any files or programs configured for use on the uncompressed drive (for example, your Windows permanent swap file) will need to be reconfigured.

The procedures in this section explain each method of removing DoubleSpace.

To remove DoubleSpace by using the back-up-and-restore method

  1. Delete any unnecessary files from your compressed drives.

  2. Back up the files on all compressed drives. If your backup program is located on a compressed drive, make sure you copy the backup program files to an uncompressed drive or to a floppy disk.

    If you are using Microsoft Backup for MS-DOS, you need to copy the following program files:

    MSBACKUP.EXE

    MSBACKUP.OVL
    MSBACKUP.INI
    MSBACKDB.OVL
    MSBACKDR.OVL
    MSBACKFB.OVL
    MSBACKFR.OVL
    MSBCONFG.OVL
    DEFAULT.SET
    MSBACKUP.LOG
    MSBACKUP.RST

  3. To determine which drive is your uncompressed drive, type dblspace /list at the command prompt. The uncompressed drive is listed under the CVF Filename column. For example, if H:\DBLSPACE.000 is the CVF Filename associated with drive C, drive H is the uncompressed drive.

  4. If you are removing DoubleSpace from your startup drive, copy the COMMAND.COM file from your compressed drive to the root directory of your uncompressed drive.

  5. Make your uncompressed drive the current drive. For example, if drive H is your uncompressed drive, type H: at the command prompt. To change to the root directory, type cd\ at the command prompt. If you want to delete all of your DoubleSpace drives, type the following at the command prompt:

    deltree dblspace.*

  6. To delete just one of your DoubleSpace drives, use the deltree command to delete the CVF for the drive. (The dblspace /list command also shows the CVF names for your drives.)

    For example, if the CVF is DBLSPACE.000, type the following at the command prompt:

    deltree dblspace.000

  7. Restart your computer.

  8. Restore your backed-up files. If your Backup program files are on a floppy disk, copy them to the hard disk first. Then run the Backup program from your hard disk.

    Note: You might need to retrieve your catalog file from your backup floppy disks. To do so, choose the Catalog button in the Restore dialog box.

To remove DoubleSpace by using the move-and-resize method

  1. Delete any unnecessary files from your compressed drives.

    To determine which drive is your uncompressed (host) drive, type dblspace /list at the command prompt. The uncompressed drive is listed under the CVF Filename column. For example, if H:\DBLSPACE.000 is the CVF Filename associated with drive C, drive H is the uncompressed drive.

  2. Delete any unnecessary files from the uncompressed drive, including your Windows permanent swap file (if any).

  3. Change to your compressed drive, and then type dblspace /size at the command prompt. DoubleSpace will reduce the drive's size as much as possible, which will free some space on the uncompressed drive. (If you have more than one compressed drive, carry out this step for each one.)

    If DoubleSpace cannot reduce a compressed drive's size because the drive is too fragmented, run Microsoft Defragmenter by typing defrag at the command prompt. When Defragmenter completes, type dblspace /size at the command prompt.

  4. Use the move command to move files from the compressed drive to the uncompressed drive until only .5 MB of free space remains on the uncompressed drive.

  5. Repeat steps 3 and 4 until your compressed drives do not contain any files you want to keep.

  6. If you are removing DoubleSpace from your startup drive, copy the COMMAND.COM file from your compressed drive to the root directory of your uncompressed drive.

  7. Make your uncompressed drive the current drive. For example, if drive H is your uncompressed drive, type H: at the command prompt. To change to the root directory, type cd\ at the command prompt. If you want to delete all of your DoubleSpace drives, type the following at the command prompt:

    deltree dblspace.*

    If you want to delete just one of your DoubleSpace drives, use the deltree command to delete the CVF for the drive. (The dblspace /list command also shows the CVF names for your drives.) For example, if the CVF is DBLSPACE.000, type the following at the command prompt:

    deltree dblspace.000

  8. Remove all references to dblspace from your CONFIG.SYS and AUTOEXEC.BAT files.

  9. Restart your computer.

Troubleshooting DoubleSpace

The following section provides tips on troubleshooting DoubleSpace. For additional tips, see the DoubleSpace section of the README.TXT file.

If You Receive a Corrupt Swap File Warning After Removing a Compressed Drive

If you remove a compressed drive, you might receive the message "The swap file is corrupt" when you start Windows.

To solve this problem

  1. Open Control Panel, and then double-click the 386 Enhanced icon.

  2. Choose the Virtual Memory button. Windows displays a dialog box stating that a corrupt swap file was found and asks if you want to set the file's length to zero.

  3. Choose the Yes button. Windows displays another Virtual Memory dialog box.

  4. Choose the Change button. Windows displays swap-file settings.

  5. In the Drive list box, select a drive that is not compressed. In the Type list box, select "Permanent."

    If your uncompressed drive does not have enough free space to create a permanent swap file, create a temporary swap file on either your compressed or uncompressed drives. (For information about freeing space on your uncompressed drive, see the DoubleSpace section of the README.TXT file.)

    When you have finished specifying swap-file settings, choose OK twice, and follow the instructions on your screen.

If Your Computer Restarts When You Are Specifying DoubleSpace Commands

If your computer restarts unexpectedly after you type dblspace /ratio or other DoubleSpace commands, edit your CONFIG.SYS file and move the command line for DBLSPACE.SYS before any commands that start network drivers.

DoubleSpace Setup places the command line for DBLSPACE.SYS before any commands for network drivers, so this problem should not occur unless you have moved the command line yourself.

If You See the Message "DoubleSpace cannot defragment because of an unknown error"

If you are running a screen-saver program when you run DoubleSpace Setup, you may receive the following error message:

DoubleSpace cannot defragment drive C because an unknown error occurred.

To work around this problem

  1. Edit your CONFIG.SYS or AUTOEXEC.BAT file and disable or remove the command that starts the screen-saver program.

  2. Restart your computer.

  3. Run DoubleSpace Setup by typing dblspace at the command prompt.

  4. After DoubleSpace Setup completes, edit your CONFIG.SYS or AUTOEXEC.BAT file and reenable the command that starts the screen-saver program.

If You See the Message "There are no more drive letters..."

If you have not installed DoubleSpace on your hard disk and you try to mount a compressed floppy disk, you receive the following error message:

There are no more drive letters reserved for DoubleSpace to use. To add more drive 
letters, choose the Options command from the Tools menu.

You must load DBLSPACE.BIN and create a DBLSPACE.INI file to mount compressed floppy disks if you have not installed DoubleSpace on your hard disk.

To mount a compressed floppy disk without running DoubleSpace Setup

  1. Using a text editor such as MS-DOS Editor, create a file that contains only the following commands:

    MaxRemovableDrives=2

    LastDrive=F

    The lastdrive command must specify one letter higher than your last logical drive letter. For example, if your last drive is E, the command should appear as lastdrive=f.

  2. Save the file in the root directory of your startup disk; give it the filename DBLSPACE.INI.

  3. Copy the DBLSPACE.BIN file from your MS-DOS directory to the root directory of your startup drive.

  4. Restart your computer.

  5. Mount the compressed floppy disk by using the dblspace /mount command. For example, if the compressed floppy disk is in drive A, type the following at the command prompt:

    dblspace /mount a:

DoubleSpace System API Specification

MS-DOS 6 DoubleSpace integrated disk compression includes the DoubleSpace System Application Programming Interfaces (API), which are supported by DBLSPACE.BIN. You might want to make use of the DoubleSpace System API if you are a developer of disk-utility software. With the DoubleSpace System API, software such as defragmentation programs, disk-repair programs, disk-caching programs, and backup programs will be able to take advantage of DoubleSpace integrated disk compression.

To receive more information about the DoubleSpace System API, contact Microsoft Developer Relations at 1-800-227-4679.

Microsoft Real-Time Compression Interface (MRCI) Specification

MRCI is the Microsoft Real-Time Compression Interface, a software interface definition that allows a MRCI client to request compression services from a MRCI server that supports the Microsoft Real-Time Compression Format (MRCF). A MRCI server may implement support for MRCF compression and decompression either fully in software or with partial or complete hardware assistance. Today, MRCI servers exist only as software (for example, the Microsoft DoubleSpace compressed file system includes a software MRCI server), but in the future hardware-based MRCI servers will be common.

MRCI has two purposes:

  • To define standard, system-level, compress/decompress services that can be used by ISVs, independent of the implementation of those services.

  • To define a standard way for IHVs and PC manufacturers to implement dedicated compression hardware.

Note that MRCI defines a standard for lossless compression, which is different from the compression used for pictorial and video images and defined in standards such as JPEG and MPEG. Lossless compression is useful for many operating system and application functions, especially where the type of data is unknown and so cannot benefit from data type-specific algorithms. The benefits of lossless compression include:

  • Increasing the effective storage capacity of media

  • Maximizing the effective data bandwidth between two computers

  • Maximizing the effective data bandwidth between a computer and a storage device

MRCI is currently used by the Microsoft DoubleSpace compressed file system, the Microsoft Flash File System, and the Microsoft Backup program in MS-DOS 6.

Since MRCI defines an interface standard that allows for hardware implementations, it is important to note the advantages that compression/decompression hardware has over compression/decompression software:

  • Performance: Tightly integrated compression/decompression hardware (on the local bus, for example) will improve performance, but until there are actual implementations it is difficult to pinpoint the magnitude of improvement.

  • Multitasking performance: When used by a multithreaded operating system such as Windows NT, compression/decompression hardware acts as a highly specialized second CPU; this frees up the main CPU to execute other threads not requesting compression services.

  • Better compression: By using more intensive matching algorithms, compression/decompression hardware can improve compression ratios by 10 percent to 15 percent over software without paying any performance penalty.

Standards Defined by MRCI

MRCI defines the following standards:

  • Query API: MRCI defines a rendezvous API that lets an application check for the presence of MRCI-compliant compression services (possibly implemented in hardware).

  • Compress and decompress APIs: If a MRCI server is present, an application can disable its own internal software compression/decompression routines. Using the address returned from the query, the application can then transfer data to and from the MRCI server that it needs compressed or decompressed.

  • Compression format: A standard compression format means that given a stream of uncompressed bytes as input, all MRCI-compliant hardware will output exactly the same stream of compressed bytes, and vice versa for decompression. MRCI enables the exchange of compressed data between systems by defining a format which is a variant of Lempel-Ziv encoding.

How to Implement MRCI

ISVs that want take advantage of MRCI will need the following:

  • MRCI specification: This section of the Resource Kit, available to the general public.

  • MRCI software libraries for MS-DOS and Windows: The libraries (MRCFDOS.LIB and MRCFWIN.LIB) provide very fast real-time compression/decompression services in software and adhere to the MRCI compression format. These are the same libraries used in the MS-DOS 6 DoubleSpace and Backup programs. The libraries check for the presence of a MRCI server and use it if present. If not present, the libraries use their own internal software routines. So, by simply linking in a library, you get fast, real-time, MRCI-compliant compression/decompression services regardless of the hardware environment. There is no need to learn about the details of MRCF, since these details are hidden by the libraries.

To use the MRCI software libraries, you must license them from Microsoft. There is no licensing fee. For licensing information, contact Microsoft Developer Relations at 1-800-227-4679.

IHVs that want to develop MRCI-compliant hardware will need the following:

  • MRCI specification: This section of the Resource Kit, available to the general public.

  • MRCI software libraries for MS-DOS and Windows: See above. These libraries are necessary to test hardware for MRCI compatibility.

  • MRCI algorithms: C language reference code that implements the MRCI compression and decompression algorithms.

  • MRCI compression format: A description of the MRCI compression format.

To use the MRCI software libraries, algorithms, and compression format, you must license them from Microsoft. There is no licensing fee. For licensing information, contact Microsoft Developer Relations at 1-800-227-4679.

If you have a CompuServe account, you can obtain additional MRCI source examples and any updates to the MRCI information included in this guide.

Operating System Support

MRCI currently supports the MS-DOS and Microsoft Windows operating systems. MRCI for Windows NT will be defined at a later date, as an extension to the Hardware Abstraction Layer (HAL).

Overview of MRCI Services

The MRCI services are briefly described in the following table.

Service

Description

MRCQuery

Return MRCI server information

MRCCompress

Compress a data buffer using StandardCompression

MRCDecompress

Decompress a data buffer

MRCMaxCompress

Compress a data buffer using MaxCompression

MRCIncrementalDecompress

Decompress a portion of a data buffer (optimization for DoubleSpace)

MRCQuery enables a client or server to establish communication with an existing MRCI server. A software interrupt is used to make this call, the interrupt number being chosen to permit the server to be supplied in any manner (as code in ROM, or as a DOS device driver or TSR, or as a Windows VxD). A new MRCI server can install itself in the system, partially or fully replacing a previously installed MRCI server. Only the last MRCI server installed is accessible via MRCQuery.

The compression and decompression services, MRCCompress, MRCMaxCompress, MRCDecompress, and MRCIncrementalDecompress, permit a client to call a server to compress and decompress data in the MRC Format. These services are invoked via a direct call to the server (the address having been obtained by MRCQuery), for optimum performance.

Note: A client must enter the Windows Disk Critical Section before calling the direct call entry point of a MRCI server. This protects the MRCI server from being reentered. Failure to do so will cause data corruption and data loss in a multitasking environment. For more information, see the sample code in the "MRCI Definitions" section later in this chapter.

MRCI Interrupts

MRCI defines a new interrupt 2Fh call (AX=4A12h) to allow a MRCI client to detect a MRCI server, and a new interrupt 1Ah call (AX=B001h) to allow the first MRCI server or client to detect a ROM BIOS (presumably hardware-based) MRCI server. MRCI clients and servers first check for the presence of an existing MRCI server by issuing the INT 2Fh call. If that fails, they issue the INT 1Ah call. If that fails, then there is no MRCI server present.

These two rendezvous interrupts are provided to ensure compatibility with existing MS-DOS software. If a software MRCI server intends to install itself over an existing INT 1Ah MRCI server, or a MRCI client is going to remain resident (either as an MS-DOS device driver or terminate-and-stay-resident (TSR) program), then it must hook INT 2Fh and create a RAM copy of the MRCINFO structure (see below). The procedure allows subsequent programs to supersede the MRCI server without cutting the initial resident program out of the loop.

MRCI Definitions

The following ASM headers document MRCI. These are specified in ASM because that is the highest-performance interface. These may be called from C with suitable C "wrapper" functions.

;***    MRCI.INC - Microsoft Real-Time Compression Interface definitions
;
;       MRCI version 1.00.07  12-Mar-1993
intMRCI         equ       2Fh       ; MRCI interrupt number
mrciDETECT      equ    04A12h       ; intMRCI AX for detecting MRCI server
intMRCIROM      equ       1Ah       ; ROM MRCI interrupt number
mrciDETECTROM   equ    0B001h       ; intMRCIROM AX for detecting MRCI server
;***    mcXXXX - flag values passed to MRCI operations
;
;       MRCCompress and MRCDecompress take a flag to indicate whether the
;       client is a *system* component (and hence may call with InDOS
;       set), or an *application*.
;
;       mcSYSTEM clients must ensure the following is true before calling
;       MRCI:
;           1)  The Windows Disk Critical Section is owned
;           2)  The InDOS flag is *set*
;
;       mcAPPLICATION clients must ensure the following is true before calling
;       MRCI:
;           1)  The Windows Disk Critical Section is owned
;           2)  The InDOS flag is *clear*
;       FAILING TO FOLLOW THE ABOVE RULES WILL LIKELY RESULT IN A SYSTEM
;       HANG AND LOSS OF USER DATA.
;
mcAPPLICATION   equ     0   ; Client is an application
mcSYSTEM        equ     1   ; Client is a file system driver
;***    micapXXXXX - bit flags for MRCINFO.mi_flCapabilities
;
;       These define both the capabilities of the Server, and also double
;       as *operation* codes passed to the mi_pfnOperate entry point in
;       the server.
;                          111111
;                          5432109876543210
;                          ----------------
micapNONE           equ    0000000000000000b ; No capabilities
micapSTANDARD       equ    0000000000000001b ; Standard compress
micapDECOMPRESS     equ    0000000000000010b ; Standard compress
micapRESERVED_1     equ    0000000000000100b ; RESERVED for future use
micapMAX            equ    0000000000001000b ; MaxCompress
micapRESERVED_2     equ    0000000000010000b ; RESERVED for future use
micapINCDECOMP      equ    0000000000100000b ; Incremental Decompress
   ;
   ; Remaining bits (6..14) are RESERVED and must be 0
   ;
micapREADONLY       equ    1000000000000000b ; MRCINFO structure is read-only
micapDEINSTALL      equ    1111111111111111b ; Server deinstall service
;***    MRCINFO - MRC Information data structure
;
;       A pointer to a MRCINFO structure is returned from MRCQuery, and sent
;       on MRCNotifyLoad.  This structure contains information on the MRCI
;       server and its capabilities.
MRCINFO struc
mi_lVendor          dd  ?       ; A 4-byte vendor ID.
                                ; Microsoft's vendor ID is "MSFT".
mi_wVendorVersion   dw  ?       ; Version number of the MRC server.
                                ; High byte is major number, low byte is minor.
                                ; EXAMPLES: v3.20 = 0314h, v10.01 = 0A01h
mi_wMRCIVersion     dw  ?       ; Version number of the MRCI supported by
                                ; this server.
mi_pfnOperate       dd  ?       ; Far pointer of the server compression entry
                                ; point.
                                ; NOTE: Caller must ensure that the Windows
                                ;       critical section is held *before*
                                ;       calling this entry point!
mi_flCapability     dw  ?       ; Bit field of server capabilities
                                ; See micapXXX for bit definitions
mi_flHWAssist       dw  ?       ; Bit field of hardware assisted
                                ; server capabilities.  One-to-one
                                ; correspondance with mi_flCapability
                                ; bits.  A bit set in this field
                                ; indicates the corresponding
                                ; capability is hardware assisted.
mi_cbMax            dw  ?       ; Maximum number of bytes that the compression
                                ; services provider can compress or decompress.
                                ; Requests to compress or decompress buffers in
                                ; excess of this length will fail.
                                ; All MRCI servers are to support at least
                                ; 8192 byte (8Kb) blocks.
MRCINFO ends
;***    MRCREQUEST - MRC compress/decompress Request packet
;
;       This structure is used to pass parameters to the server for
;       compress/decompress operations.
;
;       General Notes
;       -------------
;       (1) <mr_pbSrc,mr_cbSrc> and <mr_pbDst,mr_cbDst> MUST NOT
;           OVERLAP!
;
;       (2) The safest practice is for mr_cbSrc and mr_cbDst to be
;           identical.
;       Details on Structure Members
;       ----------------------------
;       mr_pbSrc
;           This points to the *source* buffer.
;
;           On a *compress* operation, the contents of this buffer are
;           *uncompressed* data.
;
;           On a *decompress* operation, the contents of this buffer are
;           *compressed* data.
;
;           On a *incremental decompress* operation, this field points to
;           the next section of compressed data to be uncompressed.  The
;           server updates the offset portion of this address after each
;           incremental decompress call and the application should not modify
;           this address between incremental decompress calls on the same block
;           of compressed data.
;
;       mr_cbSrc
;           This is the size of the *source* buffer.
;
;           On a *compress* operation, this is the amount of data to
;           be compressed.
;
;           For a *decompress* operation, this value is ignored.  The amount
;           of data to be decompressed is specified by the mr_cbDst parameter,
;           described below.
;
;       mr_RESERVED
;           RESERVED for future use.  Should be 0.
;
;       mr_pbDst
;           This points to the *destination* buffer.
;
;           On a *compress* operation, this buffer receives the *compressed*
;           result of the operation.
;
;           On a *decompress* operation, this buffer receives the
;           *uncompressed* result of the operation.
;
;           On a *incremental decompress* operation, this field points to
;           the next location in the destination buffer where uncompressed
;           data is to be stored.  The server updates the offset portion of
;           this address after each incremental decompress call, and the
;           application should not modify this address between incremental
;           decompress calls on the same block of compressed data.
;
;       mr_cbDst
;           On INPUT, for a *compress* operation, this is the size of the
;           *destination* buffer. If the compressed data would overflow
;           this buffer length, then the operation fails and the server
;           returns the error MRCI_ERROR_BUFFER_OVERFLOW.
;
;           On INPUT, for a *decompress* operation, this must be EXACTLY the
;           number of bytes that will be decompressed, as the MRCI server
;           uses this information to determine when to stop decompressing.
;
;           On INPUT, for an *incremental decompress* operation, this is
;           the number of bytes that should uncompressed at this time.  This
;           will typically be less than the original uncompressed size of the
;           compressed block.  A single compressed block can be uncompressed in
;           steps by making multiple incremental decompress calls for smaller
;           sized blocks.  However, to incrementally decompress an entire
;           compressed block requires that the sum of the individual mr_cbDst
;           counts be EXACTLY the number of bytes that were originally
;           compressed so the MRCI server can determine when to stop
;           decompressing.
;
;
;           On OUTPUT, the Server updates this field with the actual size
;           of the resulting compressed/uncompressed data.
;
;       mr_cbChunk
;           This is information that the Server compress routines can use to
;           "early out" of the compression as early as possible.
;
;           Valid values are 1 (client is interested in savings as small as
;           1 byte) to 32767.  DblSpace passes 512, and Flash File System
;           passes 1.
;
;           This field is most easily explained by giving an example:
;
;           Example:
;               DblSpace does space allocation in chunks of 512 bytes (the
;               common sector size on a disk).
;
;               The compression server can use this information for two
;               optimizations:
;
;               (1) If the Server cannot compress the uncompressed data
;                   enough to save at least 512 bytes, then the data is
;                   *incompressible* as far as DblSpace is concerned, even
;                   if it could be compressed to save fewer than 512 bytes.
;
;               (2) While compressing, if the Server gets to a point where
;                   the remaining uncompressed data is of such a length that
;                   it can be encoded simply (without table lookups, etc.)
;                   and not cross a 512 byte boundary, then the Server can
;                   do the simple encoding.
;
;           It is likely that these optimizations will be hard to perform
;           quickly in software, but it is possible that hardware can do
;           these optimizations without any performance loss.
;
;       mr_dwIncDecomp
;           NOTE: This is used for Incremental Decompression only.
;
;           For the first *incremental decompression* call on a compressed
;           block, this value must be set to zero.  Upon return, the field
;           will contain state information for use on the next incremental
;           decompress call.  This value must not be modified between
;           subsequent incremental decompress calls on the same compressed
;           block.
MRCREQUEST  struc
mr_pbSrc        dd  ?   ; Pointer to source buffer
mr_cbSrc        dw  ?   ; Size of source buffer, in bytes
mr_RESERVED     dw  ?   ; RESERVED for future use.
mr_pbDst        dd  ?   ; Pointer to destination buffer
mr_cbDst        dw  ?   ; Size of destination buffer, in bytes
mr_cbChunk      dw  ?   ; Client compressed data storage chunk size (see above!)
mr_dwIncDecomp  dd  ?   ; Incremental Decompression state
MRCREQUEST  ends
;***    MRCI_ERROR_XXX definitions
;
;       Error codes returned from MRCIOperate
;
MRCI_ERROR_NONE             equ     0   ; No error
MRCI_ERROR_NOT_SUPPORTED    equ     1   ; Unsupported operation requested
MRCI_ERROR_BUSY             equ     2   ; Server is busy
MRCI_ERROR_BUFFER_OVERFLOW  equ     3   ; Destination buffer too small
MRCI_ERROR_NOT_COMPRESSIBLE equ     4   ; Data could not be compressed
MRCI_ERROR_BAD_MRC_FORMAT   equ     5   ; Compressed data format is bad
;***    DefineMRCQuery - Macro to generate MRCQuery function
;
;       Put this macro somewhere in your code segment.  It will define the
;       MRCQuery routine, which you can then call far.  See the MRCQuery
;       header below for documentation on its behavior.
DefineMRCQuery  macro
;***    sigOLD_CX, sigOLD_DX, sigNEW_CX, sigNEW_DX - MRCI Server detection
;
;       These values are used to verify that the response from issuing
;       intMRCI is coming from a MRCI server, and not some other piece
;       of code.
;
;       The *old* values are passed on the mrciQUERY call, and the server
;       must change CX/DX to the *new* values, so that the caller can
;       trust that the MRCI server was responding, and not some other
;       interrupt hook.
;
;       The Server uses this code sequence to transform CX/DX:
;
;           ;------------- entry: cx='ab'  dx='cd'
;
;           xchg    ch,cl       ; cx='ba'  dx='cd'
;           xchg    dh,dl       ; cx='ba'  dx='dc'
;           xchg    dx,cx       ; cx='dc'  dx='ba'
;
sigOLD_CX   equ 'MR'
sigOLD_DX   equ 'CI'
sigNEW_CX   equ 'IC'
sigNEW_DX   equ 'RM'
;***    MRCQuery - Detect presence of MRCI server, return MRCINFO
;
;       Detect presence of MRCI server safely, and if present return
;       pointer to the server's MRCINFO structure.  NOTE that we check
;       first for a RAM-based server, and then for a ROM-based server.
;
;       Entry
;           none
;
;       Exit-Success
;           ax = 0, MRCI server is present.
;           es:di -> MRCINFO structure
;
;
;       Exit-Failure
;           ax = 1, NO MRCI server is present.
;
;       Uses
;           ax,di,es,flags
MRCQuery  proc near
;*  Save caller's registers
        SaveReg <ax,bx,cx,dx,si,bp,ds>
;*  Check intMRCI vector before we issue the interrupt.
        xor     ax,ax                   ; Segment of interrupt vector table
        mov     ds,ax
        lds     si,ds:[intMRCI*4]       ; ds:si -> MRCI server
;*  Test if vector is plausible
        mov     ax,ds
        or      ax,ax                   ; Vector hooked?
        jz      mdr                     ;   NO, go make another check
;*  Call the server
        mov     ax,mrciDETECT           ; Function
        mov     cx,sigOLD_CX            ; Signatures for validation
        mov     dx,sigOLD_DX
        int     intMRCI                 ; Call server
        cmp     cx,sigNEW_CX            ; Signature match?
        jne     mdr                     ;   NO, go make another check
        cmp     dx,sigNEW_DX            ; Signature match?
        je      mdp                     ;   YES, have server
;*  Server not present, check for ROM based server
mdr:
        xor     ax,ax
        mov     ds,ax
        lds     si,ds:[intMRCIROM*4]    ; ds:si -> ROM MRCI server
;*  Test if vector is plausible
        mov     ax,ds
        or      ax,ax                   ; Vector hooked?
        jz      mde                     ;   NO, fail
;*  Call the server
        mov     ax,mrciDETECTROM        ; Function
        mov     cx,sigOLD_CX            ; Signatures for validation
        mov     dx,sigOLD_DX
        int     intMRCIROM              ; Call ROM server
        cmp     cx,sigNEW_CX            ; Signature match?
        jne     mde                     ;   NO, go make another check
        cmp     dx,sigNEW_DX            ; Signature match?
        jne     mde                     ;   No, fail
;*  Server is present
mdp:
        xor     ax,ax                   ; Indicate success
        jmp     short mdx               ; Go exit
;*  Set error
mde:    mov     ax,1                    ; Indicate failure
;*  Restore caller's registers and exit
mdx:    RestoreReg <ds,bp,si,dx,cx,bx,ax>
        ret
MRCQuery  endp
endm ;; DefineMRCQuery
;***    DefineMRCCompress - Macro to generate MRCCompress function
;
;       Put this macro somewhere in your code segment.  It will define the
;       MRCCompress routine, which you can then call far.  See the MRCCompress
;       header below for documentation on its behavior.
DefineMRCCompress   macro
;***    MRCCompress - Compress an uncompressed data buffer
;
;       Entry
;           ax = operation to perform:
;                   micapSTANDARD
;                   micapMAX
;           cx = type of client:
;                   mcAPPLICATION - application
;                   mcSYSTEM      - file system client
;
;           ds:si -> MRCREQUEST structure
;               mr_pbSrc     - Pointer to uncompressed data buffer
;               mr_cbSrc     - Length of uncompressed data
;               mr_pbDst     - Pointer to compressed data buffer
;               mr_cbDst     - Length of compressed data buffer
;               mr_cbChunk   - Granularity of compressed data storage
;
;           es:bx -> MRCINFO structure returned by MRCQuery
;
;       Exit-Success
;           ax = 0, compress operation completed
;           ds:[si].mr_cbDst has length of compressed data in mr_pbDst.
;
;       Exit-Failure
;           Contents of mr_pbDst buffer and value of mr_cbDst are
;           undefined.
;           ax = non-zero error code:
;           MRCI_ERROR_NOT_SUPPORTED
;               Server does not support this operation.
;
;           MRCI_ERROR_BUSY
;               Server is busy with another operation.  Try again later.
;               NOTE: The most common case where this could occur is if an
;                     application calls the server while a disk cache
;                     (like SmartDrive) is writing its lazy-write queue at
;                     interrupt time to a compressed drive.  The application
;                     should try the operation again
;
;           MRCI_ERROR_BUFFER_OVERFLOW
;               The destination buffer size (mr_cbDst) was not large enough
;               to hold the compressed data.
;
;           MRCI_ERROR_NOT_COMPRESSIBLE
;               The data was not compressible, i.e., the size of the compressed
;               data would have been greater than (mr_cbDst - mr_cbChunk).
;
;       Uses
;           ax,flags
MRCCompress proc near
;*  Save caller's registers
        SaveReg <bx,cx,dx,bp,si,di,ds>
;*  Enter a Windows disk critical section.  This must be done to prevent
;   the MCRI server from being reentered if multiple VMs are making MRCI
;   calls under 386 Enhanced mode Windows.  The MRCI server sets the
;   InDOS flag, but that is not enough, as InDOS is a per-VM variable.
        push    ax                      ; NOTE: Use this exact sequence of
        mov     ax,8001h                ;       instructions because Windows
        int     2ah                     ;       expects it and will patch
        pop     ax                      ;       other code here
        call    dword ptr es:[bx].mi_pfnOperate ; Call Server
;*  Release the disk critical section.  The MRCI server is now available.
        push    ax                      ; NOTE: Use this exact sequence of
        mov     ax,8101h                ;       instructions because Windows
        int     2ah                     ;       expects it and will patch
        pop     ax                      ;       other code here
;*  Restore caller's registers and exit
        RestoreReg <ds,di,si,bp,dx,cx,bx>
        ret
MRCCompress endp
endm ;; DefineMRCCompress
;***    DefineMRCDecompress - Macro to generate MRCDeCompress function
;
;       Put this macro somewhere in your code segment.  It will define the
;       MRCDecompress routine, which you can then call far.
;       See the MRCDecompress header below for documentation on its behavior.
DefineMRCDecompress   macro
;***    MRCDecompress - Decompress an compressed data buffer
;
;       Entry
;           cx = type of client:
;                   mcAPPLICATION - application
;                   mcSYSTEM      - file system client
;
;           ds:si -> MRCREQUEST structure
;               mr_pbSrc   - Pointer to compressed data buffer
;               mr_cbSrc   - Length of compressed data
;               mr_pbDst   - Pointer to uncompressed data buffer
;               mr_cbDst   - Length of uncompressed data buffer
;
;           es:bx -> MRCINFO structure returned by MRCQuery
;
;       Exit-Success
;           ax = 0, decompress operation completed
;           ds:[si].mr_cbDst has length of uncompressed data in mr_pbDst.
;
;       Exit-Failure
;           Contents of mr_pbDst buffer and value of mr_cbDst are
;           undefined.
;           ax = non-zero error code:
;           MRCI_ERROR_NOT_SUPPORTED
;               Server does not support this operation.
;
;           MRCI_ERROR_BUSY
;               Server is busy with another operation.  Try again later.
;               NOTE: The most common case where this could occur is if an
;                     application calls the server while a disk cache
;                     (like SmartDrive) is reading ahead at interrupt time to
;                     a compressed drive.  The application should try the
;                     operation again.
;
;           MRCI_ERROR_BUFFER_OVERFLOW
;               The destination buffer size (mr_cbDst) was not large enough
;               to hold the uncompressed data.
;
;           MRCI_ERROR_BAD_MRC_FORMAT
;               The compressed data format was invalid (generally only
;               detectable as an overrun of the source buffer length, because
;               the MRC Format has no redundancy).
;               NOTE: Most software implementations will not generate this
;                     error, since it is to expensive (in time) to check for
;                     buffer overrun.  Hardware implementations may be able
;                     to check for this without performance penalty, however.
;
;       Uses
;           ax,flags
MRCDecompress proc near
;*  Save caller's registers
        SaveReg <bx,cx,dx,bp,si,di,ds>
;*  Enter a Windows disk critical section.  This must be done to prevent
;   the MCRI server from being reentered if multiple VMs are making MRCI
;   calls under 386 Enhanced mode Windows.  The MRCI server sets the
;   InDOS flag, but that is not enough, as InDOS is a per-VM variable.
        push    ax                      ; NOTE: Use this exact sequence of
        mov     ax,8001h                ;       instructions because Windows
        int     2ah                     ;       expects it and will patch
        pop     ax                      ;       other code here
        mov     ax,micapDECOMPRESS
        call    dword ptr es:[bx].mi_pfnOperate ; Call Server
;*  Release the disk critical section.  The MRCI server is now available.
        push    ax                      ; NOTE: Use this exact sequence of
        mov     ax,8101h                ;       instructions because Windows
        int     2ah                     ;       expects it and will patch
        pop     ax                      ;       other code here
;*  Restore caller's registers and exit
        RestoreReg <ds,di,si,bp,dx,cx,bx>
        ret
MRCDecompress endp
endm ;; DefineMRCDecompress
 MRCI System Client Example
;***    MRCSYSCL.ASM - MRCI System Client EXAMPLE
;
;       MRCI version 1.00.07  12-Mar-1993
;
;       NOTE: This example is implemented assuming the code segment is
;             writeable, to simplify the exposition.
;
;       This is an example "system client" of the Microsoft Real-time
;       Compression API.  A "system client" is a device driver or TSR
;       that loads and stays resident for the lifetime of the system.
;       DoubleSpace, Flash File System 2, and network drivers are examples
;       of system clients.
;
;       By contrast, an "application client" is a program like MSBackup,
;       which loads, uses MRCI, and then terminates.
;
;       This example demonstrates the proper technique for:
;
;       1) Calling the MRCI server to get its capabilities
;       2) Calling the MRCI server to perform Standard Compress
;       3) Calling the MRCI server to perform Decompress
include utility.inc
include mrci.inc
;***********************
;* Private Definitions *
;***********************
cbBUFFER    equ     8192                ; Our compression block size
;****************
;* CODE segment *
;****************
code    segment
    assume  cs:code
;*************
;* DATA AREA *
;*************
pmi     dd  0                           ; Pointer to MRCINFO structure
buffer1 db  cbBUFFER dup (?)            ; One data buffer
buffer2 db  cbBUFFER dup (?)            ; Another data buffer
mr      MRCREQUEST    <>                ; MRCI request block
;*********************
;* USE MRCI Services *
;*********************
main    proc
        call    InitMRC                 ; Get MRCI server information
        or      ax,ax                   ; MRCI server present
        jnz     max                     ;   NO, exit
;*  Compress some data
;
;       Assume buffer1 has uncompressed data
;
        mov     ax,cs                   ; Get CS in AX, for buffer pointers
        mov     ds,ax
        mov     si,OFFSET mr            ; ds:si -> our MRCREQUEST packet
        ;* Set source pointer, length
        mov     ds:[si].mr_pbSrc.offst,OFFSET buffer1
        mov     ds:[si].mr_pbSrc.segmt,ax
        mov     ds:[si].mr_cbSrc,cbBUFFER
        ;* Set destination pointer, length
        mov     ds:[si].mr_pbDst.offst,OFFSET buffer2
        mov     ds:[si].mr_pbDst.segmt,ax
        mov     ds:[si].mr_cbDst,cbBUFFER
        ;* Set storage chunk granularity
        mov     ds:[si].mr_cbChunk,1    ; We store at byte granularity
        ;* Get MRCINFO pointer for call
        les     bx,cs:pmi
        ;* Set operation and client type
        mov     ax,micapSTANDARD
        mov     cx,mcSYSTEM
        call    MRCCompress             ; Compress!
        or      ax,ax                   ; Did compress work?
        jnz     max                     ;   NO, go exit
        ;*  buffer2 now has compressed data, and
        ;   ds:[si].mr_cbDst has the size of the compressed data.
;*  Decompress some data
;
;       Assume buffer1 has compressed data
;
        mov     ax,cs                   ; Get CS in AX, for buffer pointers
        mov     ds,ax
        mov     si,OFFSET mr            ; ds:si -> our MRCREQUEST packet
        ;* Set source pointer, length
        mov     ds:[si].mr_pbSrc.offst,OFFSET buffer1
        mov     ds:[si].mr_pbSrc.segmt,ax
        mov     ds:[si].mr_cbSrc,cbBUFFER
        ;* Set destination pointer, length
        mov     ds:[si].mr_pbDst.offst,OFFSET buffer2
        mov     ds:[si].mr_pbDst.segmt,ax
        mov     ds:[si].mr_cbDst,cbBUFFER
        ;* Get MRCINFO pointer
        les     bx,cs:pmi
        ;* Set client type
        mov     cx,mcSYSTEM
        call    MRCDecompress           ; Compress!
        or      ax,ax                   ; Did compress work?
        jnz     max                     ;   NO, go exit
        ;*  buffer2 now has decompressed data, and
        ;   ds:[si].mr_cbDst has the size of the decompressed data.
;*  Do something else interesting
        ;* Nothing else to do, this is just an example.
max:    ret
main    endp
;** BEGIN: Include code for MRCCompress/MRCDecompress here!
;
        DefineMRCCompress
        DefineMRCDecompress
;
;** END:   Include code for MRCCompress/MRCDecompress here!
;******************
;* INIT-TIME CODE *
;******************
;** BEGIN: Include code for MRCQuery here!
;
        DefineMRCQuery
;
;** END:   Include code for MRCQuery here!
;**     InitMRC - Get address of MRCI Server
;
;
;       Entry
;           none.
;
;       Exit-Success
;           ax = 0
;           pmi = pointer to MRCI server MRCINFO structure
;
;       Exit-Failure
;           ax != 0, no MRCI server present
;
;       Uses
;           ax,bx,es,flags
InitMRC proc    near
    assume  ds:nothing,es:nothing,ss:nothing
        call    MRCQuery                ; es:bx -> MRCINFO, if server present
        or      ax,ax                   ; Is server present?
        jnz     imx                     ;   NO, go fail (ax != 0)
;*  Server present, save MRCINFO pointer
        mov     cs:pmi.offst,bx ; Store offset
        mov     ax,es
        mov     cs:pmi.segmt,ax ; Store segment
        xor     ax,ax                   ; Indicate success
imx:    ret
InitMRC endp
code    ends
        end
 MRCI Server Example
;***    MRCSRV.ASM - MRCI Server EXAMPLE
;
;       MRCI version 1.00.07  12-Mar-1993
;
;       NOTE: This example is implemented assuming the code segment is
;             writeable, for simplicity.
;
;       EXPORTED FUNCTIONS:
;           Server    - intMRCI interrupt handler
;           Operate   - Compress/Decompress entry point
;           Strategy  - DOS device driver strategy entry point
;           Interrupt - DOS device driver interrupt entry point
;
;
;       INTERNAL FUNCTIONS:
;           InitMRCServer       - Install Server
;           ShouldWeInstall     - Test if we should replace existing server
;           GetInDOSFlagPointer - Get address of InDOS flag
;           DoCompress          - Unimplemented compress routine
;           DoDecompress        - Unimplemented decompress routine
include utility.inc
include mrci.inc
;***********************
;* Private Definitions *
;***********************
MYVERSION   equ 0100h                   ; Version of this server
MRCIVERSION equ 0100h                   ; Version of MRCI supported by this server
MYVENDOR    equ 5446534Dh               ; "MSFT" (reversed for byte ordering)
MYVENDORhi  equ 5446h                   ; High word of MYVENDOR
MYVENDORlo  equ     534Dh               ; Low word of MYVENDOR
cbMAX       equ 8192                    ; Maximum compression block size
;* This server's capabilities
micapMine   equ micapSTANDARD or micapDECOMPRESS

;******************

;* PRIVATE MACROS *
;******************

;*** EnterCriticalSection - Grab server critical section
;
; Entry
; cx - Flag indicating whether the caller may already be inside DOS.
; mcAPPLICATION (0) - InDOS must be 0.
; mcSYSTEM (1) - InDOS may have any value.
;
; Exit-Success
; Zero Flag Set, critical section entered
; InDos is guaranteed to be non-zero
; fBusy set to non-zero
;
; Exit-Failure
; Zero Flag clear, critical section NOT entered
;
; Uses
; dx,si,ds,flags
EnterCriticalSection macro reg
cli ; Grab our lock atomically
cmp cs:fBusy,0 ; Are we already working on something?
jnz ecsx ; YES, return failure
;* Make InDOS non-zero
;
; NOTE: We check for failure here. We should not have to, since if we
; are busy, fBusy would be set, and we would have detected it above.
; The other case is a non-file system client calling, but InDOS is
; set. This case should not happen, because if InDOS is set, no
; non-file system client should gain control of the CPU and try to
; call us. So, if such a non-file system client tries this, we
; fail the call.
;
lds si,cs:fpInDOS ; ds:si -> InDOS flag
mov dl,ds:[si] ; dl = InDOS flag
or dl,dl ; In DOS?
jz ecs10 ; NO, okay to take critical section

;* InDOS is non-zero, see if caller allows this
; Zero Flag is CLEAR (=> not zero) from falling through the "jz" above
;
; NOTE: We allow a system client to call even if InDOS is not set, since
; it is possible that a system client may need to operate at
; interrupt time for "background" operations.
;
; *** THIS IS DISCOURAGED BEHAVIOR ***
;
jcxz ecsx ; NO, caller is not file system
.errnz mcAPPLICATION
;* InDOS was zero, or caller was a file system
ecs10: mov cs:oldInDOS,dl ; Save old InDOS flag, for later
mov byte ptr ds:[si],1 ; Set InDOS flag
mov cs:fBusy,1 ; We are now busy!
xor dl,dl ; Set zero flag to indicate success
ecsx: ; done with macro, zero flag indicates success/failure
sti ; Done being atomic
endm ;; EnterCriticalSection

;*** LeaveCriticalSection - Leave server critical section
;
; Entry
; cx - Flag indicating whether the caller may already be inside DOS.
; mcAPPLICATION (0) - Application client
; mcSYSTEM (1) - File System client
;
; Exit
; InDos restored to value saved by EnterCriticalSection
; fBusy set to zero
;
; Uses
; ax,si,ds,flags
LeaveCriticalSection macro reg
cli ; Release our lock atomically
;; Should assert that fBusy is set
;; cmp cs:fBusy,0 ; Are we already working on something?
;; jnz lcs20 ; YES, but not any longer
;;lcs10:
;;
;; Handle case of server attempting to leave critical section
;; without being inside critical section!
;;lcs20:
;* Restore InDOS
lds si,cs:fpInDOS ; ds:si -> InDOS flag
;; Should assert that InDOS is non-zero
;; cmp ds:[si],0 ; InDOS set?
;; jz lcs10 ; NO, but should have been!
mov al,cs:oldInDOS ; al = old InDOS flag
mov ds:[si],al ; Restore old InDOS flag
;* Clear our busy flag
mov cs:fBusy,0 ; No longer busy

        sti                             ; Done being atomic
endm ;; LeaveCriticalSection
;****************
;* CODE segment *
;****************
code    segment public byte
    assume  cs:code,ds:nothing,es:nothing,ss:nothing
;;      org     0       ; Driver starts at 0
;*************
;* DATA area *
;*************
;**     Character Device Header
;
DHlink  dd      0FFFFFFFFh              ; Next device => none
DHattr  dw      8000h                   ; Simple character device
DHstrat dw      Strategy                ; Device strategy entry point
DHinter dw      Interrupt               ; Device interrupt entry point
DHname  db      'MRCISRV$'              ; Device name
;**     mrcinfo - MRCINFO to return to clients
;
;       NOTE: This structure must be writeable, so that a new Server
;             can install and hook this structure with its values.
;
;       vendor,server version,MRCI version,entry point,capabilities,cbMAX
;
mi      MRCINFO <MYVENDOR,MYVERSION,MRCIVERSION,Operate,micapMine,cbMAX>
;**     fpOldintMRCI - previous contents of intMRCI interrupt vector
;
fpOldintMRCI   dd  ?
;**     pmi - pointer to active MRCINFO structure
;
;       If we replace an old server, then that server's MRCINFO structure
;       is the one that we use.  So, we need a level of indirection to it.
;       The more common case is that we are the only server, so this
;       variable will just point to our mi.
;
pmi     dd  ?
;**     rpDOS - pointer to DOS request packet
;
rpDos   dd      ?
;**     fpInDOS, oldInDOS - pointer to DOS "InDOS" flag, old InDOS flag value
;
;       This is REQUIRED for a software-only MRCI server, to signal that
;       the server is busy, and so prevent reentrancy.  Whenever the server
;       is about to become non-reentrant (starting a compress/decompress
;       operation, for example), it must ensure that InDOS is set.
;
;   ATTENTION--BEGIN
;
;       Furthermore, ALL callers of MRCI *must* ensure that the Windows
;       Disk Critical Section is held *prior* to calling MRCI.  This prevents
;       programs in different Windows virtual machines from reentering MRCI
;       and encountering the MRCI_ERROR_BUSY error.
;
;       Block device drivers, and system extensions like DBLSPACE.BIN, do not
;       need to grab this critical section because the MS-DOS kernel has
;       already done so before giving them control.
;
;       But TSRs and application programs *must* grab this critical section
;       prior to calling MRCI.
;
;   ATTENTION--END
;
;       The following worst-case scenario demonstrates why this is needed,
;       and why returning *Busy* (see fBusy below) is of no help:
;
;       1)  MSBackup calls MRCI server to do a decompress operation
;       2)  While MRCI server is busy, user brings up a TSR (e.g., SideKick)
;       3)  SideKick does a DOS file open and read on a compressed drive
;       4)  DOS FAT file system calls DoubleSpace
;       5)  DoubleSpace calls MRCI server, which returns busy
;
;       At this point, the system is deadlocked!  DoubleSpace has no choice
;       but to try the request again, or return some sort of disk error!
;
;       By setting the DOS InDOS flag, however, the MRCI server signals to
;       TSRs (and Windows) that it is entering a critical section, and so
;       a well-written TSR will not try to make DOS calls or MRCI calls.
fpInDOS     dd      ?   ; Pointer to DOS InDOS flag
oldInDOS    db      ?   ; Old InDOS flag value
;**     fBusy - TRUE if we are compressing/decompressing
;
;       Setting the DOS InDOS flag will protect us in most cases, but
;       having this flag lets us prevent being reentered by an interrupt-
;       time caller.
;
fBusy   db      ?   ; 0 => not busy, !0 => busy
;********
;* CODE *
;********
;***    Strategy - Device driver strategy entry point
;
;       Entry
;           es:bx - pointer to DOS request packet
;
;       Exit
;           es:bx saved in data:rpDos
;
;       Uses
;           None
;
Strategy    proc    far
    assume  cs:code,ds:nothing,es:nothing
        mov     cs:rpDos.offst,bx       ; Store request packet pointer
        mov     cs:rpDos.segmt,es
        ret
Strategy    endp
;***    Interrupt - Device driver interrupt entry point
;
;       Entry
;           rpDOS - pointer to DOS request packet saved by Strategy function
;
;       Exit
;           ???
;
;       Uses
;           None
;
Interrupt   proc    far
        SaveReg <ax,bx,cx,dx,si,di,ds,es>
        push    cs                      ; Make access to driver segment faster
        pop     ds
    assume  ds:code
        les     bx,rpDos                ; es:bx -> DOS request packet
        mov     al,es:[bx].rpFunction   ; al = function
        cmp     al,DEVINIT              ; Init call?
        mov     ax,STERR+03h            ; Assume ERROR+"Unknown command"
        jne     itrx                    ;   NO, fail call
;*  Init device
        call    GetInDOSFlagPointer     ; Needed for critical section calls
        SaveReg <bx,es>
        call    InitMRCServer
        RestoreReg <es,bx>
        mov     cx,OFFSET cs:EndOfResidentCode ; Assume we are installed
        or      ax,ax                   ; Are we installed?
        jz      itr10                   ;   YES, set break address, note that
                                        ;   ax=status=fine!
;*  Did not install MRCI server
        xor     cx,cx                   ;   NO, make break address small
        mov     ax,STERR                ; What error code should we use?
;*  Set break address
;
;   cx = break address
;   ax = status code
itr10:  mov     es:[bx+14].offst,cx
        mov     es:[bx+14].segmt,cs
;*  es:bx -> DOS request packet
;   ax = status code
itrx:   or      ax,STDONE               ; Set DONE bit in status
        mov es:[bx].rpStatus,ax         ; Store status
        RestoreReg <es,ds,di,si,dx,cx,bx,ax>
        ret
Interrupt   endp
;***    Server - MRCI server interrupt entry point
;
;       Entry
;           mov ax,MRCI function  ; Set function
;           int intMRCI           ; Call us here
;
;       Exit
;           See MRCI spec.  IRET to return.
;
;       Uses
;           AX, DI, ES, flags
Server  proc
    assume  ds:nothing,es:nothing,ss:nothing
;*  Verify that caller is asking for a MRCI server
        cmp     cx,sigOLD_CX            ; Signature match?
        je      srv10                   ;   YES, keep testing
        jmp     dword ptr cs:[fpOldintMRCI] ; Chain to previous hooker
srv10:  cmp     dx,sigOLD_DX            ; Signature match?
        je      srv20                   ;   YES, caller wants us
        jmp     dword ptr cs:[fpOldintMRCI] ; Chain to previous hooker
;*  Dispatch MRCI function
srv20:  cmp     ax,mrciDETECT           ; Detect call?
        je      dms10                   ;   NO, continue
;*  mrciQUERY call
        push    cs
        pop     es
        mov     di,OFFSET mi            ; es:di -> our MRCINFO structure
;*  Update CX/DX signatures so caller knows we are a MRCI server
                               ;   entry: cx='ab'  dx='cd'
        xchg    ch,cl                   ; cx='ba'  dx='cd'
        xchg    dh,dl                   ; cx='ba'  dx='dc'
        xchg    dx,cx                   ; cx='dc'  dx='ba'
        xor     ax,ax                   ; Indicate success
        iret                            ; Return to caller
dms10:  mov     ax,1                    ; Indicate failure
;*  EmptyIRET - address for fpOldintMRCI if intMRCI was unhooked.
;
;   To improve performance of Server when it chains a non-intMRCI request,
;   if we are the first hooker of intMRCI, we store the pointer to this
;   IRET.  This removes the need for Server to check that the fpOldintMRCI
;   value is valid, to prevent from INTing into hyperspace.
;
EmptyIRET:
        iret
Server  endp
;***    Operate - MRCI server operation entry point
;
;       Handle Compress/Decompress requests.
;
;       Entry
;              ax = Requested operation (micapXXXX)
;              cx = File System Caller flag
;                       mcAPPLICATION (0) - Application client
;                       mcSYSTEM (1)      - File System client
;           ds:si -> MRCREQUEST packet
;
;       Exit-Success
;           ax = 0
;           See MRCI spec for details on returned values.
;
;       Exit-Failure
;           See MRCI spec for details on returned values.
;
;       Uses
;           All (but CS:IP, SS:SP)
Operate proc    far
    assume  ds:nothing,es:nothing,ss:nothing
        and     ax,micapMine            ; Is this an operation we support?
        jz      opre                    ;   NO, fail request
;*  Make sure we are not being reentered
        EnterCriticalSection cx         ; Grab critical section
        jc      opre1                   ;   Already grabbed, go fail
        SaveReg <cx>                    ; Save fFileSystemCaller flag
;*  Dispatch operation
;
;   NOTE: We assume Standard Compress is the most common operation,
;         so we optimize the flow to reduce jumps for that operation.
;
        cmp     ax,micapSTANDARD        ; Standard compress?
        jnz     opr10                   ;   YES, go do it
        cmp     ax,micapDECOMPRESS      ; Decompress?
        jz      opre                    ;   NO, invalid operation (user had
                                        ;   more than one bit set!)
;*  Do Decompress
opr20:  call    DoDecompress
        jmp     short oprLCS
;*  Do Standard Compress
opr10:  call    DoCompress
;*  Release critical section
oprLCS: RestoreReg <cx>                 ; Get fFileSystemCaller flag
        LeaveCriticalSection cx         ; Leave critical section
        ret                             ; Return status
opre:   mov     ax,MRCI_ERROR_NOT_SUPPORTED
        ret
opre1:  mov     ax,MRCI_ERROR_BUSY
        ret
Operate endp
;***    DoCompress - Unimplemented compress routine
;
;
DoCompress  proc    near
DoCompress  endp
;***    DoDecompress - Unimplemented decompress routine
;
;
DoDecompress  proc    near
DoDecompress  endp
;******************
;* INIT-TIME CODE *
;******************
EndOfResidentCode  label  byte  ; Truncate device here after init is complete
;** BEGIN: Include code for MRCQuery here!
;
        DefineMRCQuery
;
;** END:   Include code for MRCQuery here!
;**     InitMRCServer - Initialize our MRCI Server
;
;       If a MRCI server IS NOT present, then we install our server.
;
;       If a MRCI server IS present, then we get the pointer to its
;       MRCINFO structure, and check to see if we are "better" than the
;       present server.  If so, then we install, and edit the old
;       server's MRCINFO structure so that its clients can call us.
;
;       Entry
;           none.
;
;       Exit-Success
;           ax = 0
;           Server installed
;           pmi = pointer to MRCI server MRCINFO structure
;
;       Exit-Failure
;           ax = 1, another, better server already present
;
;       Uses
;           ax,bx,si,ds,es,flags
InitMRCServer   proc    near
    assume  ds:nothing,es:nothing,ss:nothing
        call    MRCQuery                ; Check for MRCI server presence
        or      ax,ax                   ; Is server present?
        jz      ims20                   ;   NO, install ourselves
;*  Existing Server present
;
;   es:di -> old server's MCRINFO structure
;
;   We need to copy our MRCINFO structure over the old server's MCRINFO
;   structure, so that clients of the previous server that call the
;   server indirectly using the old MRCINFO structure will call us,
;   instead.
;
;   NOTE: We are guaranteed that no one is using the old server right now,
;         because we have the CPU.
;
;         So, we disable interrupts while we update the old MRCINFO structure.
        ;; dbgPrint 'Existing server present'
        call    ShouldWeInstall
        or      ax,ax                   ; Do install?
        jz      ims10                   ;   YES, go install
        ret                             ;   NO, return "did not install"

;* Copy our MRCINFO to old server's MRCINFO

ims10: cli ; Protect old MRCINFO from being
; in an inconsistent state.
push cs
pop ds
assume ds:code
mov si,OFFSET mi ; ds:si -> our MRCINFO structure
mov cx,SIZE mi
cld
rep movsb ; Copy our MRCINFO to old one
;* Fall through to hook interrupt chain.
;
; NOTE: Interrupts remain off, until we have hooked the interrupt chain.
; We have to leave the old server in the chain, so that it can
; chain on to previous non-MRCI hookers.

;* Hook into intMRCI chain
ims20: xor ax,ax ; Segment of interrupt vector table
mov ds,ax
assume ds:nothing
mov si,intMRCI*4 ; ds:si -> intMRCI vector
cli ; Editing the interrupt vector table
mov ax,OFFSET Server
xchg ds:[si].offst,ax ; Set offset, ax = old offset
mov bx,cs
xchg ds:[si].segmt,bx ; Store segment, bx = old segment
or ax,ax ; Old pointer valid?
jnz ims30 ; YES, go save
mov ax,OFFSET EmptyIRET ; NO, point at on of our IRETs
mov bx,cx
ims30: mov cs:fpOldintMRCI.offst,ax ; Save old offset, for chaining
mov cs:fpOldintMRCI.segmt,bx ; Save old segment, for chaining
sti ; Done editing the intvec table
ret
InitMRCServer endp

;***    ShouldWeInstall - check if our server should supercede another server
;
;       We can install only if all of the following are true:
;           1) Our capabilities are at least as good as present server
;           2) Our MRCI version is at least as high
;           3) If the vendor is the same, our vendor server version must
;              be greater than the present server.  If the vendor does not
;              match, then we cannot compare vendor versions, so we assume
;              we should install.
;
;       Entry
;           es:di -> MRCINFO structure of current installed server
;
;       Exit-Success
;           ax = 0, we should install
;
;       Exit-Failure
;           ax = 1, we should not install
;           Existing driver is more capable than we are.
;
;       Uses
;           ax,bx,flags
ShouldWeInstall proc    near
;*  Our capabilities must be at least as good as present server
        mov     ax,es:[di].mi_flCapability ; server capabilities
        and     ax,not micapMine        ; ax = server caps that I do not have
        or      ax,ax                   ; Does the server have caps I do not?
        jnz     swie                    ;   YES, do NOT install
;*  We are at least as good on capabilities, so...
;*  Compare MRCI versions
        mov     ax,es:[di].mi_wMRCIVersion ; ax = present server MRCI version
        cmp     ax,MRCIVERSION          ; Are we at least as good as server?
        ja      swie                    ;   NO, do not install
        jb      swi10                   ; We are better than server, install
;*  We support the same MRCI version, so...
;*  See if we supersede the old <vendor,vendor version>
        mov     ax,es:[di].mi_lVendor.loword
        cmp     ax,MYVENDORlo           ; Match low word?
        jne     swi10                   ;   NO, DO install
        mov     ax,es:[di].mi_lVendor.hiword
        cmp     ax,MYVENDORhi           ; Match high word?
        jne     swi10                   ;   NO, DO install
        mov     ax,es:[di].mi_wVendorVersion
        cmp     ax,MYVERSION            ; Am I a newer driver version?
        jae     swie                    ;   NO, do NOT install
;*  Tests pass -- we should install
swi10:  xor     ax,ax                   ; Indicate DO install
        ret
swie:   mov     ax,1                    ; Indicate do NOT install
        ret
ShouldWeInstall endp
;***    GetInDOSFlagPointer - Get address of InDOS flag
;
;       Entry
;           none
;
;       Exit
;           fpInDOS = pointer to DOS InDOS flag
;
;       Uses
;           ax,flags
GetInDOSFlagPointer proc    near
        SaveReg <bx,es>
        mov     ah,34h
        int     21h                     ; Get In DOS flag
        mov     cs:fpInDOS.offst,bx
        mov     cs:fpInDOS.segmt,es
        RestoreReg <es,bx>
        ret
GetInDOSFlagPointer endp
code    ends
        end