//--------------------------------------------------------------------------
//
//      XMS.H: header file for XMS/UMB/HMA class library.
//      Copyright (c) J.English 1993.
//      Author's address: je@unix.brighton.ac.uk
//
//      Permission is granted to use copy and distribute the
//      information contained in this file provided that this
//      copyright notice is retained intact and that any software
//      or other document incorporating this file or parts thereof
//      makes the source code for the library of which this file
//      is a part freely available.
//
//--------------------------------------------------------------------------
//
//      Revision history:
//      2.0     Nov 1993        This file added to provide general XMS
//                              facilities
//                              
//--------------------------------------------------------------------------

#ifndef __XMS_H
#define __XMS_H

//--------------------------------------------------------------------------
//
//      Class XMS: wrapper class for XMS driver functions.
//
//      Declaring an object of this class allocates a new XMS handle.
//      The constructor takes an optional long integer parameter giving
//      the size of the XMS block to be allocated in bytes, which may be
//      rounded up; if it is omitted, a block size of zero will be assumed.
//      The following operations are available:
//
//      object.valid ()       -- Returns non-zero if the object has been
//                               created successfully.
//      !object               -- Returns the (non-zero) error code if the
//                               object has NOT been created successfully.
//      object.size ()        -- Returns the current block size in bytes.
//      object.resize (n)     -- Resizes the block to "n" bytes.  Returns
//                               a result of type XMS::error.
//      object.at (n)         -- Returns a descriptor pointing to the
//                               n-th byte of an XMS block (see below).
//      object [n]            -- A synonym for "object.at (n)".
//      XMS::copy (to,from,n) -- Copies "n" bytes from "from" to "to"; can
//                               be used to copy from real memory to XMS or
//                               vice versa or from XMS to XMS. For accessing
//                               real memory, "to" or "from" can be a pointer
//                               of any type; for accessing XMS, they must be
//                               descriptors generated by "at" or the "[]"
//                               operator (above).  Returns a result of type
//                               XMS::error.
//      XMS::available ()     -- Returns the total number of bytes of XMS
//                               currently available.
//      XMS::largest ()       -- Returns the size of the largest contiguous
//                               unallocated XMS block in bytes.
//
//      Note that copy operations should specify an even number of bytes,
//      and that source and destination should not overlap if an XMS-to-XMS
//      copy is made.  Performance may be improved if copies to or from real
//      memory are made to word aligned addresses (doubleword aligned for
//      32-bit processors).
//
//      BEWARE: If programs are terminated abnormally, any existing XMS
//      blocks will remain unrecoverably allocated.

class XMS
{
    struct XMSptr                           { int handle; long offset; };

  public:
    enum error {                            // XMS error codes ...
        SUCCESS                             = 0x00,
        NO_DRIVER                           = 0x01,
        INVALID_OBJECT                      = 0x02,
        BLOCK_TOO_BIG                       = 0x03,
        NOT_IMPLEMENTED                     = 0x80,
        VDISK_DETECTED                      = 0x81,
        A20_ERROR                           = 0x82,
        GENERAL_DRIVER_ERROR                = 0x8E,
        UNRECOVERABLE_DRIVER_ERROR          = 0x8F,
        NO_HMA                              = 0x90,
        HMA_IN_USE                          = 0x91,
        HMA_REQUEST_TOO_SMALL               = 0x92,
        NO_MORE_MEMORY                      = 0xA0,
        NO_MORE_HANDLES                     = 0xA1,
        BAD_HANDLE                          = 0xA2,
        BAD_SRC_HANDLE                      = 0xA3,
        BAD_SRC_OFFSET                      = 0xA4,
        BAD_DST_HANDLE                      = 0xA5,
        BAD_DST_OFFSET                      = 0xA6,
        BAD_LENGTH                          = 0xA7,
        OVERLAP_ERROR                       = 0xA8,
        PARITY_ERROR                        = 0xA9,
        BLOCK_LOCKED                        = 0xAB
    };

    XMS    (long size = 0);                 // constructor
    ~XMS   ();                              // destructor

    error  operator!  ()                    { return status; }
    int    valid      ()                    { return (status == SUCCESS); }
    long   size       ()                    { return allocation; }
    error  resize     (long newsize);       // resize block

    XMSptr at         (long offset);        // offset into allocated block
    XMSptr operator[] (long offset)         { return at (offset); }

    static error copy (const XMSptr& to,    // copy XMS -> XMS
                       const XMSptr& from,  // (NB: to/from mustn't overlap)
                       unsigned      len);  // length rounded down if odd

    static error copy (void far*     to,    // copy XMS -> conventional
                       const XMSptr& from,
                       unsigned      len);  // length rounded down if odd

    static error copy (const XMSptr& to,    // copy conventional -> XMS
                       void far*     from,
                       unsigned      len);  // length rounded down if odd

    static long       available ();         // get total size of free XMS
    static long       largest   ();         // get size of largest free block

  private:
    error status;
    int   handle;
    long  allocation;
};

//--------------------------------------------------------------------------
//
//      Class UMB: wrapper class for XMS driver UMB functions.
//
//      Declaring an object of this class attempts to allocate an upper
//      memory block (UMB) of the specified size.  The constructor takes
//      a parameter specifying the block size in bytes, which may be
//      rounded up.  The following operations are available:
//
//      UMB::largest ()     -- Returns the size of the largest available
//                             UMB block in bytes.
//      object.addr ()      -- Returns a pointer to the allocated UMB, or
//                             a null pointer if the allocation failed.
//      object.valid ()     -- Returns non-zero if the object has been
//                             created successfully.
//      !object             -- Returns the (non-zero) XMS error code if the
//                             object has NOT been created successfully.
//      object.size ()      -- Returns the actual allocated UMB size in bytes.
//
class UMB
{
  public:
    UMB  (long size);                   // constructor
    ~UMB ();                            // destructor

    static long largest   ();           // size of largest available block

    void huge*  addr      ()            { return address; }
    XMS::error  operator! ()            { return status; }
    int         valid     ()            { return (status == XMS::SUCCESS); }
    long        size      ()            { return allocation; }

  private:
    XMS::error status;
    void huge* address;
    long       allocation;
};

//--------------------------------------------------------------------------
//
//      Class HMA: wrapper class for XMS driver HMA functions.
//
//      Declaring an object of this class attempts to allocate the HMA.
//      The constructor takes an optional long integer parameter which
//      gives the size of the HMA block to be allocated in bytes; if it
//      is omitted, the maximum possible block size (0xFFF0 bytes) will
//      be assumed.  The following operations are available:
//
//      object.valid ()       -- Returns non-zero if the HMA has been
//                               allocated successfully.
//      !object               -- Returns the (non-zero) XMS error code if the
//                               HMA has NOT been allocated successfully.
//      object.size ()        -- Returns the allocated HMA size in bytes.
//      object.at (n)         -- Returns a descriptor pointing to the
//                               n-th byte of the HMA (see below).
//      object [n]            -- A synonym for "object.at (n)".
//      XMS::copy (to,from,n) -- Copies "n" bytes from "from" to "to"; can
//                               be used to copy from real memory to HMA or
//                               vice versa or from HMA to HMA. For accessing
//                               real memory, "to" or "from" can be a pointer
//                               of any type; for accessing HMA, they must be
//                               descriptors generated by "at" or the "[]"
//                               operator (above).  Returns the number of
//                               bytes actually copied.  Note that the source
//                               and destination areas for HMS-to-HMS copy
//                               operations should not overlap.
//
class HMA
{
    struct HMAptr                            { HMA* hma; unsigned offset; };

  public:
    HMA  (unsigned size = 0xFFF0);           // constructor
    ~HMA ();                                 // destructor

    XMS::error operator! ()                  { return status; }
    int        valid     ()                  { return (status == XMS::SUCCESS); }
    unsigned   size      ()                  { return allocation; }

    HMAptr at         (unsigned offset);     // offset into allocated block
    HMAptr operator[] (unsigned offset)      { return at (offset); }

    static unsigned copy (const HMAptr& to,  // copy HMA -> HMA
                          const HMAptr& from,
                          unsigned      len);

    static unsigned copy (void far*     to,  // copy HMA -> conventional
                          const HMAptr& from,
                          unsigned      len);

    static unsigned copy (const HMAptr& to,  // copy conventional -> HMA
                          void far*     from,
                          unsigned      len);

  private:
    XMS::error status;
    unsigned   allocation;
};

#endif
