[Back to MEMORY SWAG index] [Back to Main SWAG index] [Original]

    {*************************** EMS.PAS ************************
    *** Demonstrate calls to Extended memory manager          ***
    ************************************************************}

    USES

      Crt,Dos;

    {***************************************************************}

    TYPE
      PtrType = RECORD                  {Define a pointer record    }
        Offset  : Word;                 {  type so we can access the}
        Segment : Word                  {  individual pointer fields}
        END;
      DeviceName = ARRAY[1..8] OF Char; {Defined to test device Name}

    CONST

      EmsInt      = $67;                {EMS Interrupt number       }
      IOCtlFunc   = $44;                {IOCtl DOS Function number  }
      PageLen     = 16384;              {Length of memory page      }
      MsgLen      = 16;                 {Message len plus len byte  }
      MsgsPerPage = PageLen DIV MsgLen; {Number of messages in page }
      NumMsgs     = 5000;               {Number EMS messages        }

      {*** Emm functions ***}

      GetStatus        = $40;
      GetPageFrameAddr = $41;
      GetUnallocPages  = $42;
      GetEmmVersion    = $46;
      AllocatePages    = $43;
      MapHandlePage    = $44;
      DeallocatePages  = $45;

    VAR
      P0, P1, P2, P3 : Pointer;     {Pointers to physical pages     }
      EmmHandle      : Integer;     {Handle for EMM allocated pages }
      Tmp            : FILE;        {Temp file to test if EMM exists}
      MsgBuf         : Pointer;     {Pointer into mapped memory     }
      Buff           : String[15];  {Buffer for msg stored in EM    }
      I              : Integer;     {Dummy variable                 }
      EmmRegs        : Registers;   {Registers for interrupt calls  }
      Page,Index     : Integer;     {Used to address page frame     }
      EmsVector      : Pointer;     {EMM address from Int $67       }
      StrNum         : String[4];   {Holds record # for EMM msg     }

    {******** Function to convert word value to Hex string *********}

    FUNCTION Hex(IntNbr : Word): String;
    CONST
      HexDigit :ARRAY[0..15] OF Char = '0123456789ABCDEF';
    VAR
      S        : String[2];         {Temporary String}
      TempByte : Byte;
    BEGIN
      TempByte := Hi(IntNbr);                  {Convert upper nibble}
      S := HexDigit[TempByte DIV 16] +
           HexDigit[TempByte MOD 16];
      TempByte := Lo(IntNbr);                  {Convert lower nibble}
      Hex := S + HexDigit[TempByte DIV 16] +
                 HexDigit[TempByte MOD 16];
    END;

    {******** Create a string that contains a pointer value ********}

    FUNCTION PrintPointer(P : Pointer): String;

    BEGIN
      PrintPointer := Hex(PtrType(P).Segment) + ':' +
                      Hex(PtrType(P).Offset);
      END;

    {*********** Print the EMM Status to the screen ****************}

    PROCEDURE EmmPrintStatus(Status: Byte);

    CONST
      EmmStatus : ARRAY [$80..$8F] OF String =
        ('Driver malfunction',
         'Hardware malfunction',
         '',
         'Bad Handle',
         'Undefined FUNCTION',
         'No free handles',
         'Page map context Error',
         'Insufficient memory pages',
         'Not enough free pages',
         'Can''t allocate zero (0) pages',
         'Logical page out of range',
         'Physical page out of range',
         'Page map hardware RAM full',
         'Page map already has a Handle',
         'Page map not mapped to Handle',
         'Undefined subfunction number');

    BEGIN
      CASE Status OF
        0        : WriteLn('Ok');
        $80..$8F : WriteLn('EMM: ',EmmStatus[Status])
        ELSE WriteLn('EMM: Unknown status = $',Hex(Status))
        END
    END;

    {******** Generic procedure to call the EMM interrupt **********}

    PROCEDURE CallEmm(EmmFunction : Byte; VAR R : Registers);

    BEGIN
      R.AH := EmmFunction;
      Intr(EmsInt,R);
      IF (R.AH <> 0) THEN BEGIN
        EmmPrintStatus(EmmRegs.AH);
        Halt(EmmRegs.AH)
        END
      END;

    {******************  Main Program  *****************************}

    BEGIN

      ClrScr;

    {$DEFINE CheckFile}              {Undefine to test second method}

    {$IFDEF CheckFile}                  {Check EMM driver - Method 1}

      GetIntVec(EmsInt,EmsVector);
      PtrType(EmsVector).Offset := 10;
      IF (DeviceName(EmsVector^) <> 'EMMXXXX0') THEN BEGIN
        WriteLn('No EMM driver present');
        Halt(1)
        END;

    {$ELSE}                             {Check EMM driver - Method 2}

      {***** Determine if EMM is installed by opening EMMXXXX0 *****}

      {$I-}
      Assign(Tmp,'EMMXXXX0');
      Reset(Tmp);
      {$I+}
      IF (IOResult <> 0) THEN BEGIN      {Opened file without error?}
        WriteLn('No EMM driver present');
        WriteLn('IO error #',IOResult:3);
        Halt(1)
        END;

      EmmRegs.AH := IOCtlFun              {Call IOCtl function to   }
      EmmRegs.AL := $00;                  { test whether EMMXXXX0 is}
      EmmRegs.BX := FileRec(Tmp).Handle;  { a file or a device      }

      MsDos(EmmRegs);
      Close(Tmp);

      IF (EmmRegs.Flags AND 1) = 0 THEN            {Call successfull}
        IF (EmmRegs.DX AND $80) = $80 THEN   {Handle is for a device}
          WriteLn('Handle refers to a device')
        ELSE BEGIN
          WriteLn('Handle refers to a FILE');
          WriteLn('Unable to contact EMM driver if present');
          Halt(1)
          END
      ELSE BEGIN                                 {Call unsuccessfull}
        CASE EmmRegs.AX OF
          1 : WriteLn('Invalid IOCTL subfunction');
          5 : WriteLn('Access to IOCTL denied');
          6 : WriteLn('Invalid Handle')
          ELSE WriteLn('Unknown error # ',Hex(EmmRegs.AX))
          END;
        WriteLn('Unable to contact EMM driver');
        Halt(1)
        END;

    {$ENDIF}

      WriteLn('EMM driver present');

      {********  Print the current status of the EMM driver ********}

      CallEmm(GetStatus,EmmRegs);
      WriteLn('EMM Status Ok');

      {******** Print the version number of EMM driver *************}

      CallEmm(GetEmmVersion,EmmRegs);

      WriteLn('EMS driver version = ',
               (EmmRegs.AL SHR 4):1,'.',
               (EmmRegs.AL AND $0F):1);

      IF EmmRegs.AL < $32 THEN BEGIN
        WriteLn('Error - EMM is version is earlier than 3.2');
        Halt(1)
        END;

      {***** Print the page frame & physical window addresses ******}

      CallEmm(GetPageFrameAddr,EmmRegs);

      PtrType(P0).Segment := EmmRegs.BX; { Window 0 -> P0 = BX:0000 }
      PtrType(P0).Offset := $0;
      PtrType(P1).Segment := EmmRegs.BX; { Window 1 -> P1 = BX:4000 }
      PtrType(P1).Offset := $4000;
      PtrType(P2).Segment := EmmRegs.BX; { Window 2 -> P2 = BX:8000 }
      PtrType(P2).Offset := $8000;
      PtrType(P3).Segment := EmmRegs.BX; { Window 3 -> P3 = BX:C000 }
      PtrType(P3).Offset := $C000;

      WriteLn('Page frame segment address = ',Hex(EmmRegs.BX));
      WriteLn('Physical page 0 address = ',PrintPointer(P0));
      WriteLn('Physical page 1 address = ',PrintPointer(P1));
      WriteLn('Physical page 2 address = ',PrintPointer(P2));
      WriteLn('Physical page 3 address = ',PrintPointer(P3));

      {***** Print # of unallocated pages and total # of pages *****}

      CallEmm(GetUnallocPages,EmmRegs);
      WriteLn('Total EMS pages = ',EmmRegs.DX:4);
      WriteLn('Unused EMS pages = ',EmmRegs.BX:4);

      {***** Allocate some pages of expanded memory *****}

      EmmRegs.BX := (NumMsgs + MsgsPerPage) DIV MsgsPerPage;
      CallEmm(AllocatePages,EmmRegs);
      WriteLn('Allocated ',EmmRegs.BX,
              ' pages to handle # ',EmmRegs.DX);
      EmmHandle := EmmRegs.DX;

      {***** Load EMS RAM with data *****}

      MsgBuf := P0;               {* Set Message pointer to Page 0 *}

      FOR I := 0 TO NumMsgs-1 DO BEGIN
        Str(I:4,StrNum);                       {Create msg string   }
        Buff := ' EMS msg # ' + StrNum;
        IF (I MOD 100) = 0 THEN Write('.');    {Dsp status on screen}
        Page := I DIV MsgsPerPage;
        Index := I MOD MsgsPerPage;
        PtrType(MsgBuf).Offset := Index * SizeOf(Buff);

         {**** Map indicated logical page into physical page 0 ****}

        EmmRegs.AH := MapHandlePage;           {Map EMS page cmd    }
        EmmRegs.BX := Page;                    {Logical page number }
        EmmRegs.AL := 0;                       {Physical page 0     }
        EmmRegs.DX := EmmHandle;               {EMM RAM handle      }

        Intr(EmsInt,EmmRegs);

        IF EmmRegs.AH = 0 THEN
          Move(Buff[0],MsgBuf^,SizeOf(Buff))   {Set message into mem}
        ELSE BEGIN
          EmmPrintStatus(EmmRegs.AH);
          I := NumMsgs
          END
        END;

        WriteLn;

      {******  Allow user to access any message in the buffer ******}

      I := $FF;
      WHILE I <> -1 DO BEGIN
        MsgBuf := P3;                 {Set MsgBuf to physical page 3}
        Write('Enter message # to retrieve, or -1 to quit: ');
        ReadLn(I);
        IF (I >= 0) AND (I < NumMsgs) THEN BEGIN
          Page := I DIV MsgsPerPage;
          Index := I MOD MsgsPerPage;

            {**** Map indicated page into physical page 3 ****}

          EmmRegs.AH := MapHandlePage;         {Map EMM page command}
          EmmRegs.BX := Page;                  {Logical page number }
          EmmRegs.AL := 3;                     {Physical page 3     }
          EmmRegs.DX := EmmHandle;             {EMM RAM handle      }

          Intr(EmsInt,EmmRegs);

          IF EmmRegs.AH = 0 THEN BEGIN
            Inc(PtrType(MsgBuf).Offset,Index * SizeOf(Buff));
            Move(MsgBuf^,Buff[0],SizeOf(Buff));
            Write('Retrieved message -> ',Buff);
            WriteLn(' from page #',Page:2,' Index',Index:5);
            END
          ELSE BEGIN
            EmmPrintStatus(EmmRegs.AH);
            I := -1;
            END
          END
        END;

         {***** Free the EMS RAM back to the EMM driver ****}

      EmmRegs.DX := EmmHandle;
      CallEmm(DeallocatePages,EmmRegs);
      WriteLn('Released all memory for handle ',EmmRegs.DX:2);
    END.

[Back to MEMORY SWAG index] [Back to Main SWAG index] [Original]