{***************************************************************************
*   E M S : a unit that can be used as an interface between a program and  *
*          the Expanded Memory Manager (EMM) - this allows access to EMS   *
*          memory                                                          *
**------------------------------------------------------------------------**
*  Author           : MICHAEL TISCHER                                      *
*  developed on     : 06/09/1989                                           *
*  last update on   : 06/09/1989                                           *
***************************************************************************}

unit Ems;

interface

uses Dos;

{-- declaration of functions and procedures that can be called from  ------}
{-- other programs                                                   ------}

function  EmsGetFreePage   : integer;
function  EmsGetPtr        ( PhysPage : byte ) : pointer;
function  EmsAlloc         ( Amount : integer ) : integer;
procedure EmsMap           ( Handle, LogPage : integer; PhysPage : byte );
procedure EmsFree          ( Handle : integer );
procedure EmsRestoreMapping( Handle : integer );
procedure EmsSaveMapping   ( Handle : integer );

{-- constants, public -----------------------------------------------------}

const {--------------------------------------------- EMS error codes ------}

      EmsErrOk        = $00;   { everything o.k., no error                 }
      EmsErrSw        = $80;   { error in EMM (software)                   }
      EmsErrHw        = $81;   { EMS hardware error                        }
      EmsErrInvHandle = $83;   { invalid EMS handle                        }
      EmsErrFkt       = $84;   { invalid function called                   }
      EmsErrNoHandles = $85;   { no more handles free                      }
      EmsErrSaResMap  = $86;   { error while saving or restoring Mapping   }
      EmsErrToMany    = $87;   { more pages were requested than are        }
                               { physically available                      }
      EmsErrNoPages   = $88;   { more pages requested than are free        }
      EmsErrNullPages = $89;   { null page requested                       }
      EmsErrLogPInv   = $8A;   { logical page does not belong to handle    }
      EmsErrPhyPInv   = $8B;   { invalid physical page number              }
      EmsErrMapFull   = $8C;   { Mapping memory region is full             }
      EmsErrMapSaved  = $8D;   { Mapping already saved                     }
      EmsErrMapRes    = $8E;   { attempt to restore Mapping without        }
                               { previously saving it                      }

{-- global variables accessible to other programs -------------------------}

var EmsInst    : boolean;      { contains TRUE if EMS memory is available }
    EmsPages    : integer;                     { total number of EMS pages }
    EmsVersion,                  { EMS version number (32 = 3.2, 40 = 4.0) }
    EmsError    : byte;                          { stores EMM error number }

implementation

{-- constants internal to this program ------------------------------------}

const EMS_INT = $67;              { interrupt vector for accessing the EMM }

{-- global variables internal to this module ------------------------------}

var EmsFrameSeg : word;           { segment address of the EMS page frames }

{***************************************************************************
*  EmsInit : Initialializes the unit                                       *
***************************************************************************}

procedure EmsInit;

type EmmName  = array [1..8] of char; { name of the EMM from driver header }
     EmmNaPtr = ^EmmName;               { pointer to name in driver header }

const Name : EmmName = 'EMMXXXX0';                    { name of EMS driver }

var Regs  : Registers;            { processor registers for interrupt call }

begin
  {-- start by determining if EMS memory and the proper EMM are installed -}

  Regs.ax := $35 shl 8 + EMS_INT;              { get interrupt vector with }
  msdos( Regs );                               { DOS function $35          }

  EmsInst := ( EmmNaPtr(Ptr(Regs.ES,10))^ = Name );  { compare driver name }

  if ( EmsInst ) then                               { is an EMM installed? }
    begin                                                            { yes }

      {-- get total number of EMS pages -----------------------------------}
      Regs.AH := $42;             { function no. for "get number of pages" }
      intr( EMS_INT, Regs );                                    { call EMM }
      EmsPages := Regs.DX;                   { store total number of pages }

      {-- get segment address of EMS page frame ---------------------------}
      Regs.AH := $41;  { Function no. for "get segment add. of page frame" }
      intr( EMS_INT, Regs );                                    { call EMM }
      EmsFrameSeg := Regs.BX;                      { store segment address }

      {-- get version number of EMM ---------------------------------------}
      Regs.AH := $46;          { function no. for "get EMM version number" }
      intr( EMS_INT, Regs );                                    { call EMM }
      EmsVersion := ( Regs.AL and 15 ) + ( Regs.AL shr 4 ) * 10;

      EmsError := EmsErrOk;                                { no errors yet }

    end;
end;

{***************************************************************************
*  EmsGetPtr : returns a pointer to one of the four physical pages of the  *
*              EMS page frame                                              *
**------------------------------------------------------------------------**
*  Input   : PhysPage = number of the physical page                        *
*  Output  : pointer to this page                                          *
***************************************************************************}

function EmsGetPtr( PhysPage : byte ) : pointer;

begin
  EmsGetPtr := ptr( EmsFrameSeg, PhysPage shl 14 );
end;

{***************************************************************************
*  EmsGetFreePage : gets the number of free EMS pages (1 page = 16K)       *
**------------------------------------------------------------------------**
*  Output  : the number of free pages                                      *
***************************************************************************}

function EmsGetFreePage : integer;

var Regs : Registers;             { processor registers for interrupt call }

begin
  Regs.AH := $42;                 { function no. for "get number of pages" }
  intr( EMS_INT, Regs );                                        { call EMM }
  EmsGetFreePage := Regs.BX;                 { return number of free pages }
end;
{***************************************************************************
*  EmsAlloc : allocates a given number of EMS pages                        *
**------------------------------------------------------------------------**
*  Input   : Amount = number of pages to allocate                          *
*  Output  : handle for later access to the allocated pages                *
*  Info    : if an error occurs, the variable EmsError will contain a      *
*            value not equal to 0 (an error code) after the function call  *
***************************************************************************}

function EmsAlloc( Amount : integer ) : integer;

var Regs : Registers;             { processor registers for interrupt call }

begin
  Regs.AH := $43;                      { function no. for "allocate pages" }
  Regs.BX := Amount;                     { number of pages is passed to BX }
  intr( EMS_INT, Regs );                                        { call EMM }
  EmsAlloc := Regs.DX;                        { the handle is passed to DX }
  EmsError := Regs.AH;                                            { error? }
end;
{***************************************************************************
* EmsMap : loads one of the allocated logical pages into one of the 4      *
*           physical pages of the EMS page frame                           *
*-------------------------------------------------------------------------**
*  Input   : Handle   = handle that identifies the allocated page          *
*            LogPage  = number of the logical page to be loaded            *
*            PhysPage = the physical page number                           *
*  Info    : if an error occurs, the variable EmsError will contain a      *
*            value other than 0 (error code) after the function call       *
***************************************************************************}

procedure EmsMap( Handle, LogPage : integer; PhysPage : byte );

var Regs : Registers;             { processor registers for interrupt call }

begin
  Regs.AH := $44;            { function no. for "map expanded memory page" }
  Regs.DX := Handle;                { load the parameters in the registers }
  Regs.BX := LogPage;
  Regs.Al := PhysPage;
  intr( EMS_INT, Regs );                                        { call EMM }
  EmsError := Regs.AH;                                            { error? }
end;

{***************************************************************************
*  EmsFree : frees EMS pages previously allocated with EmsAlloc            *
**------------------------------------------------------------------------**
*  Input   : Handle = the handle under which the pages were allocated      *
*  Info    : if an error occurs, the variable EmsError will contain a      *
*            value other than 0 (error code) after the function call       *
***************************************************************************}

procedure EmsFree( Handle : integer );

var Regs : Registers;             { processor registers for interrupt call }

begin
  Regs.AH := $45;             { function number for "release handle & EMS" }
  Regs.DX := Handle;                      { load parameter in the register }
  intr( EMS_INT, Regs );                                        { call EMM }
  EmsError := Regs.AH;                                            { error? }
end;

{***************************************************************************
* EmsSaveMapping : saves a mapping of the current logical EMS pages in     *
*                   the four physical pages of the EMS page frame          *
**------------------------------------------------------------------------**
*  Input   : Handle = the handle under which the pages were allocated      *
*  Info    : if an error occurs, the variable EmsError will contain a      *
*            value other than 0 (error code) after the function call       *
***************************************************************************}

procedure EmsSaveMapping( Handle : integer );

var Regs : Registers;             { processor registers for interrupt call }

begin
  Regs.AH := $47;                     { function number for "save mapping" }
  Regs.DX := Handle;                  { load the parameter in the register }
  intr( EMS_INT, Regs );                                        { call EMM }
  EmsError := Regs.AH;                                            { error? }
end;

{***************************************************************************
*  EmsRestoreMapping : retrieves a mapping previously saved with the       *
*                      procedure EmsSaveMapping                            *
**------------------------------------------------------------------------**
*  Input   : Handle = the handle under which the pages were allocated      *
*  Info    : if an error occurs, the variable EmsError will contain a      *
*            value other than 0 (error code) after the function call       *
***************************************************************************}

procedure EmsRestoreMapping( Handle : integer );

var Regs : Registers;             { processor registers for interrupt call }

begin
  Regs.AH := $48;                  { function number for "restore mapping" }
  Regs.DX := Handle;                      { load parameter in the register }
  intr( EMS_INT, Regs );                                        { call EMM }
  EmsError := Regs.AH;                                            { error? }
end;

{**----------------------------------------------------------------------**}
{** Starting code of the unit                                            **}
{**----------------------------------------------------------------------**}

begin
  EmsInit;                                           { initialize the unit }
end.

