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

    {---------------- Extended Memory Access Unit -----------------}
    UNIT XMS;
    (**) INTERFACE (**)
    VAR
      XMSErrorCode : byte;     { Error Code - Defined in XMS Spec }
      XMSAddr      : Pointer;  { Entry Point for HIMEM.SYS Driver }

    FUNCTION XMSDriverLoaded: Boolean;
    FUNCTION XMSTotalMemoryAvailable: Word;
    FUNCTION XMSLargestBlockAvailable: Word;
    FUNCTION XMSAllocateBlock(KBSize: Word): Word;
    FUNCTION XMSReleaseBlock(Handle: Word): Boolean;
    FUNCTION XMSMoveDataTo(SourceAddr: Pointer; NumBytes: LongInt;
               XMSHandle: Word; XMSOffset: LongInt): Boolean;
    FUNCTION XMSGetDataFrom(XMSHandle: Word; XMSOffset: LongInt;
               NumBytes: LongInt; LowMemAddr: Pointer): Boolean;

    (**) IMPLEMENTATION (**)

    TYPE
      XMSMoveStruct = record
        movelen   : LongInt;   { length of block to move in bytes }
        case integer of
          { Case 0 Variant for Low Memory to XMS }
          0: (SHandle   : Word;      { source handle = 0
                                       for conventional memory }
              SPtr      : pointer;   { source address }
              XMSHdl    : Word;      { XMS destination handle }
              XMSOffset : LongInt);  { 32 bit XMS offset }
          { Case 1 Variant for XMS to Low Memory }
          1: (XMSH      : Word;      { XMS source handle }
              XMSOfs    : LongInt;   { starting offset in XMS}
              DHandle   : Word;      { 0 when conventional memory
                                       destination }
              DPtr      : pointer);  { address in conventional memory }
        END;

    VAR moveparms : XMSMoveStruct;   { structure for moving to and
                                       from XMS }

    {**************************************************************}
    { XMSDriverLoaded - Returns true IF Extended Memory Driver     }
    {                   HIMEM.SYS Loaded                           }
    {                 - Sets Entry Point Address - XMSAddr         }
    {**************************************************************}
    FUNCTION XMSDriverLoaded: Boolean;
    CONST
      himemseg: Word = 0;
      himemofs: Word = 0;
    BEGIN
      XMSErrorCode := 0;
      ASM
        mov ax,4300h      { Check to see IF HIMEM.SYS installed }
        int 2fh
        cmp al,80h        { Returns AL = 80H IF installed }
        jne @1
        mov ax,4310h      { Now get the entry point }
        int 2fh
        mov himemofs,bx
        mov himemseg,es
      @1:
      END;
      XMSDriverLoaded := (himemseg <> 0);
      XMSAddr := Ptr(himemseg,himemofs);
    END;

    {**************************************************************}
    { XMSTotalMemoryAvailable - Returns Total XMS Memory Available }
    {**************************************************************}
    FUNCTION XMSTotalMemoryAvailable: Word;
    BEGIN
      XMSErrorCode := 0;
      XMSTotalMemoryAvailable := 0;
      IF XMSAddr = nil THEN        { Check IF HIMEM.SYS Loaded }
        IF NOT XMSDriverLoaded THEN exit;
      ASM
        mov  ah,8
        call XMSAddr
        or   ax,ax
        jnz  @1
        mov  XMSErrorCode,bl  { Set Error Code }
        xor  dx,dx
        @1:
        mov  @Result,dx       { DX = total free extended memory }
      END;
    END;

    {**************************************************************}
    { XMSLargestBlockAvailable - Returns Largest Contiguous        }
    {                            XMS Block Available               }
    {**************************************************************}
    FUNCTION XMSLargestBlockAvailable: Word;
    BEGIN
      XMSErrorCode := 0;
      XMSLargestBlockAvailable := 0;
      IF XMSAddr = nil THEN         { Check IF HIMEM.SYS Loaded }
        IF NOT XMSDriverLoaded THEN exit;
      ASM
        mov  ah,8
        call XMSAddr
        or   ax,ax
        jnz  @1
        mov  XMSErrorCode,bl { On Error, Set Error Code }
        @1:
        mov  @Result,ax      { AX=largest free XMS block }
      END;
    END;

    {***************************************************************}
    { XMSAllocateBlock - Allocates Block of XMS Memory              }
    {                  - Input - KBSize: No of Kilobytes requested  }
    {                  - Returns Handle for memory IF successful    }
    {***************************************************************}
    FUNCTION XMSAllocateBlock(KBSize: Word): Word;
    BEGIN
      XMSAllocateBlock := 0;
      XMSErrorCode := 0;
      IF XMSAddr = nil THEN { Check IF HIMEM.SYS Loaded }
        IF NOT XMSDriverLoaded THEN exit;
      ASM
        mov  ah,9
        mov  dx,KBSize
        call XMSAddr
        or   ax,ax
        jnz  @1
        mov  XMSErrorCode,bl { On Error, Set Error Code }
        xor  dx,dx
        @1:
        mov  @Result,dx      { DX = handle for extended memory }
      END;
    END;

    {**************************************************************}
    { XMSReleaseBlock - Releases Block of XMS Memory               }
    {                 - Input:   Handle identifying memory to be   }
    {                   released                                   }
    {                 - Returns  true IF successful                }
    {**************************************************************}
    FUNCTION XMSReleaseBlock(Handle: Word): Boolean;
    VAR OK : Word;
    BEGIN
      XMSErrorCode := 0;
      XMSReleaseBlock := false;
      IF XMSAddr = nil THEN   { Check IF HIMEM.SYS Loaded }
        IF NOT XMSDriverLoaded THEN exit;
      ASM
        mov  ah,0Ah
        mov  dx,Handle
        call XMSAddr
        or   ax,ax
        jnz  @1
        mov  XMSErrorCode,bl  { On Error, Set Error Code }
        @1:
        mov  OK,ax
      END;
      XMSReleaseBlock := (OK <> 0);
    END;

    {**************************************************************}
    { XMSMoveDataTo - Moves Block of Data from Conventional        }
    {                 Memory to XMS Memory                         }
    {               - Data Must have been previously allocated     }
    {               - Input - SourceAddr : address of data in      }
    {                                      conventional memory     }
    {                       - NumBytes   : number of bytes to move }
    {                       - XMSHandle  : handle of XMS block     }
    {                       - XMSOffset  : 32 bit destination      }
    {                                      offset in XMS block     }
    {               - Returns true IF completed successfully       }
    {**************************************************************}
    FUNCTION XMSMoveDataTo(SourceAddr: Pointer; NumBytes: LongInt;
               XMSHandle: Word; XMSOffset: LongInt): Boolean;
    VAR Status    : Word;
    BEGIN
      XMSErrorCode := 0;
      XMSMoveDataTo := false;
      IF XMSAddr = nil THEN  { Check IF HIMEM.SYS Loaded }
        IF NOT XMSDriverLoaded THEN exit;
      MoveParms.MoveLen   := NumBytes;
      MoveParms.SHandle   := 0;         { Source Handle=0 For
                                          Conventional Memory}
      MoveParms.SPtr      := SourceAddr;
      MoveParms.XMSHdl    := XMSHandle;
      MoveParms.XMSOffset := XMSOffset;
      ASM
        mov  ah,0Bh
        mov  si,offset MoveParms
        call XMSAddr
        mov  Status,ax       { Completion Status }
        or   ax,ax
        jnz  @1
        mov  XMSErrorCode,bl { Save Error Code }
        @1:
      END;
      XMSMoveDataTo := (Status <> 0);
    END;

    {**************************************************************}
    { XMSGetDataFrom - Moves Block From XMS to Conventional Memory }
    {                - Data Must have been previously allocated    }
    {                  and moved to XMS                            }
    {               - Input - XMSHandle  : handle of source        }
    {                                      XMS block               }
    {                       - XMSOffset  : 32 bit source offset    }
    {                                      in XMS block            }
    {                       - NumBytes   : number of bytes to move }
    {                       - LowMemAddr : destination addr in     }
    {                                      conventional memory     }
    {               - Returns true IF completed successfully       }
    {**************************************************************}
    FUNCTION XMSGetDataFrom(XMSHandle: Word; XMSOffset: LongInt;
               NumBytes: LongInt; LowMemAddr: Pointer): Boolean;
    VAR Status    : Word;
    BEGIN
      XMSErrorCode := 0;
      XMSGetDataFrom := false;
      IF XMSAddr = nil THEN   { Check IF HIMEM.SYS Loaded }
        IF NOT XMSDriverLoaded THEN exit;
      MoveParms.MoveLen := NumBytes;  { Set-Up Structure to Pass }
      MoveParms.XMSh    := XMSHandle;
      MoveParms.XMSOfs  := XMSOffset;
      MoveParms.DHandle := 0;         { Dest Handle=0 For
                                        Conventional Memory}
      MoveParms.DPtr    := LowMemAddr;
      ASM
        mov  ah,0Bh
        mov  si,offset MoveParms
        call XMSAddr
        mov  Status,ax       { Completion Status }
        or   ax,ax
        jnz  @1
        mov  XMSErrorCode,bl { Set Error Code }
        @1:
      END;
      XMSGetDataFrom := (Status <> 0);
    END;

    BEGIN
      XMSAddr      := nil; { Initialize XMSAddr }
      XMSErrorCode := 0;
    END.

    { *********************************************************************** }
    { *********************************************************************** }
    { *********************************************************************** }
    {                                XMS DEMO PROGRAM                         }

    {$X+}
    Program XMSTest;
    USES crt, XMS;
    CONST
      NumVars     = 131072;   { 131072 total no of variables in array }
      BytesPerVar = 4;        { ie. 2 for integers, 4 for LongInts ...}
    VAR
      I       : LongInt;
      Result  : LongInt;
      Hdl     : Word;          { Handle for Extended memory allocated }
      HiMemOK : boolean;
    BEGIN
      ClrScr;
      HiMemOK := XMSDriverLoaded;
      WriteLn('HIMEM.SYS Driver Loaded=', HiMemOK);
      IF NOT HiMemOK THEN Halt;
      WriteLn('Total Extended Memory: ', XMSTotalMemoryAvailable, ' KB');
      WriteLn('Largest Free Extended Memory Block: ',
               XMSLargestBlockAvailable, ' KB');

      {Allocate Memory - Hdl is memory block handle or identifier}
      Hdl := XMSAllocateBlock((NumVars * BytesPerVar + 1023) DIV 1024);
             {1023 to Round Up to next KB}
      WriteLn((NumVars * BytesPerVar + 1023) DIV 1024,'KB Handle=',Hdl);
      WriteLn('Total Extended Memory Available After Allocation: ',
              XMSTotalMemoryAvailable, ' KB');

      { Fill the variables with 1 ... NumVars for exercise }
      WriteLn('Filling Memory Block ');
      FOR I := 1 TO NumVars DO
        BEGIN
          { parameters in Move Data are - Address of Data to Move
                                        - Number of Bytes
                                        - Memory Handle
                                        - Offset in XMS Area }
          IF NOT XMSMoveDataTo(@I, BytesPerVar, Hdl, (I - 1) *
            BytesPerVar) THEN
            WriteLn('Error on Move to XMS: ',I,' Error: ', XMSErrorCode);
          IF I MOD 1024 = 0 THEN Write(I:6,^M);
        END;
      WriteLn;
      { Now read a couple of locations just to show how}
      I := 1;  { First Element }
      IF NOT XMSGetDataFrom(Hdl, (I - 1) * BytesPerVar,
        BytesPerVar, @Result) THEN
        WriteLn('Error on XMSGetDataFrom')
        ELSE WriteLn('XMS Data [',I,']=',Result); { Print it }
      I := NumVars;  { Last Element }
      IF NOT XMSGetDataFrom(Hdl, (I - 1) * BytesPerVar, BytesPerVar,
        @Result) THEN
        WriteLn('Error on XMSGetDataFrom')
        ELSE WriteLn('XMS Data [',I,']=',Result); { Print it }

      WriteLn('Release status=', XMSReleaseBlock(Hdl));
      WriteLn('Press a key.');
      ReadKey;
    END.

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