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

    UNIT XMS;               {  XMS Routines, Last Updated Dec 11/93        }
                            {  Copyright (C) 1993, Greg Estabrooks         }
                            {  NOTE: Requires TP 6.0+ To compile.          }
    INTERFACE
    {**********************************************************************}
    TYPE
        _32Bit = LONGINT;
        XMSMovStruct = RECORD
                        Amount      :_32Bit;  { 32 bit number of bytes to move}
                        SourceHandle:WORD;    { Handle of Source Block.     }
                        SourceOffset:_32Bit;  { 32 bit offset to source.    }
                        DestHandle  :WORD;    { Handle of destination.      }
                        DestOffset  :_32Bit;  { 32 bit offset to destination}
                       END;
                                    { If SourceHandle is 0 then SourceOffset}
                                    { Is Interpereted as a SEGMENT:OFFSET   }
                                    { into conventional memory.             }
                                    { The Same applies to DestHandle.       }
    { Potential XMS Error Codes:                                            }
    {   BL=80h if the function is not implemented                           }
    {      81h if a VDISK device is detected                                }
    {      82h if an A20 error occurs                                       }
    {      8Eh if a general driver error occurs                             }
    {      8Fh if an unrecoverable driver error occurs                      }
    {      90h if the HMA does not exist                                    }
    {      91h if the HMA is already in use                                 }
    {      92h if DX is less than the /HMAMIN= parameter                    }
    {      93h if the HMA is not allocated                                  }
    {      94h if the A20 line is still enabled                             }
    {      A0h if all extended memory is allocated                          }
    {      A1h if all available extended memory handles are in use          }
    {      A2h if the handle is invalid                                     }
    {      A3h if the SourceHandle is invalid                               }
    {      A4h if the SourceOffset is invalid                               }
    {      A5h if the DestHandle is invalid                                 }
    {      A6h if the DestOffset is invalid                                 }
    {      A7h if the Length is invalid                                     }
    {      A8h if the move has an invalid overlap                           }
    {      A9h if a parity error occurs                                     }
    {      AAh if the block is not locked                                   }
    {      ABh if the block is locked                                       }
    {      ACh if the block's lock count overflows                          }
    {      ADh if the lock fails                                            }
    {      B0h if a smaller UMB is available                                }
    {      B1h if no UMBs are available                                     }
    {      B2h if the UMB segment number is invalid                         }

    VAR
            XMSControl :POINTER;    { Holds the address of the XMS API.     }
            XMSError   :BYTE;       { Holds any XMS error codes.            }

    FUNCTION XMSDriver :BOOLEAN;
                    {  Routine to determine if an XMS driver is installed.  }
                    {  If it is installed it loads XMSControl with the      }
                    {  location of the XMS API for the other routines.      }

    FUNCTION XMSControlAdr :POINTER;
                    {  This Routine returns a pointer to the XMS Controller.}

    FUNCTION XMSVer :WORD;
                    {  This routine returns the version of the XMS driver   }
                    {  that is currently installed.                         }

    FUNCTION XMSRev :WORD;
                    { Returns XMS Revision Number. Usually used with XMSVer.}

    FUNCTION XMSGetFreeMem :WORD;
                       {  Routine to Determine how much total XMS memory is }
                       {  free.                                             }

    FUNCTION XMSGetLargeBlock :WORD;
                       {  Routine to Determine the size of the largest free }
                       {  XMS is block.                                     }

    FUNCTION XMSGetMem( Blocks:WORD ) :WORD;
                        {  Routine to allocate XMS for program use.         }
                        {  Blocks = k's being requested, XMSErr = ErrorCode.}
                        {  Returns 16 bit handle to mem allocated.          }

    PROCEDURE XMSFreeMem( Handle:WORD );
                        {  Routine to free previously allocated XMS Memory. }
    PROCEDURE XMSMoveblock( VAR Movstruct :XMSMovStruct );
                        {  Routine to move memory blocks around in XMS memory.}

    PROCEDURE XMSLockBlock( Handle :WORD );
                            { Routine to lock and XMS block. Locked blocks  }
                            { are guarnteed not to move.                    }
                            { Locked Blocks should be unlocked as soon as   }
                            { possible.                                     }

    PROCEDURE XMSUnLockBlock( Handle :WORD );
                            { Routine to unlock a previously lock XMS block.}

    PROCEDURE XMSReallocate( Handle ,NewSize :WORD );
                            { Routine to reallocate and XMS Block so that it}
                            { becomes equal to NewSize.                     }

    FUNCTION HMAExists :BOOLEAN;
                    {  This routine returns Whether or not HMA Exists.      }

    PROCEDURE HMARequest( RequestType :WORD );
                       { Attempt to reserve the 64k HMA area for the caller.}
                       { NOTE: RequestType must be either FFFF = Application}
                       { OR If caller is a TSR the RequestType = Amount of  }
                       { Space wanted.                                      }

    PROCEDURE HMARelease;
                          { Routine to release previously allocated HMA.    }
                          { NOTE: Any Code/Data store in that HMA Memory    }
                          { Will become invalid and inaccessible.           }

    PROCEDURE GlobaleEnableA20;
                         { Routine to Enable the A20 Line. Should only be   }
                         { used by programs that have control of the HMA.   }
                         { NOTE: Remeber to disable the Line before         }
                         {       releaseing control of the system.          }

    PROCEDURE GlobaleDisableA20;
                          { Routine to Disable the A20 Line. On some systems}
                          { the Toggling of the A20 Line can take a long    }
                          { time.                                           }

    PROCEDURE LocalEnableA20;
                         { Routine to Enable the A20 Line for current Program}
                         { NOTE: Rember to so a LocalDisableA20 before      }
                         {       releasing system control.                  }

    PROCEDURE LocalDisableA20;
                          { Routine to Locally Disable the A20 Line.        }

    FUNCTION QueryA20 :BOOLEAN;
                         { Routine to test whether the A20 is Physically    }
                         { enabled or not.                                  }

    FUNCTION PtrToLong( P:POINTER ) :LONGINT;
                         { Routine to convert a pointer to a 32 bit number. }

    IMPLEMENTATION
    {**********************************************************************}

    FUNCTION XMSDriver :BOOLEAN; ASSEMBLER;
                    {  Routine to determine if an XMS driver is installed.  }
                    {  If it is installed it loads XMSControl with the      }
                    {  location of the XMS API for the other routines.      }
    ASM
      Mov AX,$4300                  {  Function to check for Driver.        }
      Int $2F                       {  Call Dos Int 2Fh.                    }
      Cmp AL,$80                    {  Check Result, if its 80h driver.     }
      Je @Installed                 {  If It is return TRUE.                }
      Mov AL,0                      {  Else Return FALSE.                   }
      Jmp @Exit
    @Installed:
      Mov AX,$4310                  {  Function to return pointer to Driver.}
      Int $2F                       {  Call Interrupt.                      }
      Mov XMSControl.WORD,BX        {  Pointer info returned in ES:BX.      }
      Mov XMSControl+2.WORD,ES
      Mov AL,1                      {  Set True Flag.                       }
    @Exit:
    END;{XMSDriver}

    FUNCTION XMSControlAdr :POINTER; ASSEMBLER;
                    {  This Routine returns a pointer to the XMS Controller.}
    ASM
      Push ES                       {  Push ES onto the stack.              }
      Push BX                       {  Push BX onto the stack.              }
      Mov AX,$4310                  {  Function to return pointer to Driver.}
      Int $2F                       {  Call Interrupt.                      }
      Mov DX,ES                     {  Pointer info returned in ES:BX so    }
      Mov AX,BX                     {  move it into DX:AX.                  }
      Pop BX                        {  Pop BX Off the Stack.                }
      Pop ES                        {  Pop ES Off the Stack.                }
    END;{XMSControlAdr}

    FUNCTION XMSVer :WORD; ASSEMBLER;
                    {  This routine returns the version of the xms driver   }
                    {  that is currently installed.Version is returned as a }
                    {  16 bit BCD number.                                   }
    ASM
      Mov AH,0                      {  Function to return XMS version.      }
      Call [XMSControl]             {  Call XMS Api.                        }
                       {  Possible returns are :                            }
                       {  AX = XMS version , BX = driver revision number    }
                       {  DX = 1 if HMA exists, 0 if not.                   }
    END;{XMSVer}

    FUNCTION XMSRev :WORD; ASSEMBLER;
                    { Returns XMS Revision Number. Usually used with XMSVer.}
    ASM
      Push BX                       {  Save BX.                             }
      Mov AH,0                      {  Function to return XMS revision.     }
      Call [XMSControl]             {  Call XMS Api.                        }
      Mov AX,BX                     {  Move result into proper register.    }
      Pop BX                        {  Restore BX.                          }
    END;{XMSRev}

    FUNCTION XMSGetFreeMem :WORD; ASSEMBLER;
                       {  Routine to Determine how much total XMS memory is }
                       {  free.                                             }
    ASM
      Push DX                       {  Save DX and BX.                      }
      Push BX
      Mov XMSError,0                {  Clear error flag.                    }
      Mov AH,$08                    {  Function to get free XMS mem         }
      Call [XMSControl]             {  Call XMS Api                         }
      Mov XMSError,BL               {  Return any error code to user.       }
      Mov AX,DX                     {  Load AX with Total Free k'S          }
      Pop BX                        {  Restore BX and DX.                   }
      Pop DX
                       {  DX = Total Free in k's                            }
                       {  AX = Largest free block in k's                    }
                       {  BL = Err Code.                                    }
    END;{XMSGetFreeMem}

    FUNCTION XMSGetLargeBlock :WORD; ASSEMBLER;
                       {  Routine to Determine the size of the largest free }
                       {  XMS is block.                                     }
    ASM
      Push BX                       {  Save BX.                             }
      Mov XMSError,0                {  Clear error flag.                    }
      Mov AH,$08                    {  Function to get free XMS mem         }
      Call [XMSControl]             {  Call XMS Api                         }
      Mov XMSError,BL               {  Return any error code to user.       }
      Pop BX                        {  Restore BX.                          }
                       {  DX = Total Free in k's                            }
                       {  AX = Largest free block in k's                    }
    END;{XMSGetLargeBlock}

    FUNCTION XMSGetMem( Blocks:WORD ) :WORD; ASSEMBLER;
                        {  Routine to allocate XMS for programs use         }
                        {  Blocks = k's being requested, XMSErr = ErrorCode }
                        {  Returns 16 bit handle to mem allocated           }
    ASM
      Push DX                       {  Save DX and BX.                      }
      Push BX
      Mov XMSError,0                {  Clear error flag.                    }
      Mov AH,9                      {  Function Allocate Extended Memory    }
      Mov DX,Blocks                 {  Load k Blocks to be allocated        }
      Call [XMSControl]             {  Call XMS API                         }
      Mov XMSError,BL               {  Return any error code to user.       }
      Mov AX,DX                     {  Load 16 Bit Handle to allocated Mem  }
      Pop BX                        {  Restore BX and DX.                   }
      Pop DX
             {NOTE: If there was an Error then the handle is invalid. }
    END;{XMSGetMem}

    PROCEDURE XMSFreeMem( Handle:WORD ); ASSEMBLER;
                        {  Routine to free previously allocated XMS Memory  }
    ASM
      Push DX                       {  Save DX and BX.                      }
      Push BX
      Mov XMSError,0                {  Clear error flag.                    }
      Mov AH,$0A                    {  Function Free Allocated Memory       }
      Mov DX,Handle                 {  Load Handle of Memory to free        }
      Call [XMSControl]             {  Call API                             }
      Mov XMSError,BL               {  Return any error code to user.       }
      Pop BX                        {  Restore BX and DX.                   }
      Pop DX
    END;{XMSFreeMem}

    PROCEDURE XMSMoveblock( VAR Movstruct :XMSMovStruct ); ASSEMBLER;
             {  Routine to move memory blocks around in XMS memory.         }
             {  Length must be even.                                        }
    ASM
      Push DS                       {  Save DS and SI                       }
      Push SI
      Push BX
      Mov XMSError,0                {  Clear error flag.                    }
      LDS SI,MovStruct              {  Point DS:SI to move Structure        }
      Mov AH,$0B                    {  Function to Move Extended memory block}
      Call [XMSControl]             {  Call XMS API                         }
      Mov XMSError,BL               {  Save any error code for user.        }
      Pop BX
      Pop SI                        {  Restore DS and SI                    }
      Pop DS
    END;{XMSMoveBlock}

    PROCEDURE XMSLockBlock( Handle :WORD ); ASSEMBLER;
                            { Routine to lock and XMS block. Locked blocks  }
                            { are guarnteed not to move.                    }
                            { Locked Blocks should be unlocked as soon as   }
                            { possible.                                     }
    ASM
      Push DX                       {  Save DX and BX.                      }
      Push BX
      Mov XMSError,0                {  Clear Error Flag.                    }
      Mov AH,$0C                    {  Function to lock XMS Block.          }
      Mov DX,Handle                 {  Handle of block to lock.             }
      Call [XMSControl]             {  Call XMS Api.                        }
      Mov XMSError,BL               {  Save any error codes.                }
      Pop BX                        {  Restore BX and DX.                   }
      Pop DX
    END;{XMSLockBlock}

    PROCEDURE XMSUnLockBlock( Handle :WORD ); ASSEMBLER;
                            { Routine to unlock a previously lock XMS block.}
    ASM
      Push DX                       {  Save DX and BX.                      }
      Push BX
      Mov XMSError,0                {  Clear Error Flag.                    }
      Mov AH,$0D                    {  Function to unlock XMS Block.        }
      Mov DX,Handle                 {  Handle of block to unlock.           }
      Call [XMSControl]             {  Call XMS Api.                        }
      Mov XMSError,BL               {  Save any error codes.                }
      Pop BX                        {  Restore BX and DX.                   }
      Pop DX
    END;{XMSUnLockBlock}

    PROCEDURE XMSReallocate( Handle ,NewSize :WORD ); ASSEMBLER;
                            { Routine to reallocate and XMS Block so that it}
                            { becomes equal to NewSize.                     }
    ASM
      Push DX                       {  Save DX and BX.                      }
      Push BX
      Mov XMSError,0                {  Clear Error Flag.                    }
      Mov BX,NewSize                {  Load New size of XMS Block.          }
      Mov DX,Handle                 {  Handle of an unlocked XMS Block.     }
      Mov AH,$0F                    {  Function to Reallocate XMS Block.    }
      Mov DX,Handle                 {  Handle of block to lock.             }
      Call [XMSControl]             {  Call XMS Api.                        }
      Mov XMSError,BL               {  Save any error codes.                }
      Pop BX                        {  Restore BX and DX.                   }
      Pop DX
    END;{XMSReallocate}

    FUNCTION HMAExists :BOOLEAN; ASSEMBLER;
                    {  This routine returns Whether or not HMA Exists       }
    ASM
      Push DX                       {  Save DX.                             }
      Mov AH,0                      {  Function to return HMA Status        }
      Call [XMSControl]             {  Call XMS Api                         }
      Mov AL,DL                     {  Mov Status into proper register      }
      Pop DX                        {  Restore DX.                          }
                       {  Possible returns are :                            }
                       {  AX = XMS version , BX = driver revision number    }
                       {  DX = 1 if HMA exists, 0 if not                    }
    END;{HMAExists}

    PROCEDURE HMARequest( RequestType :WORD ); ASSEMBLER;
                       { Attempt to reserve the 64k HMA area for the caller.}
                       { NOTE: RequestType must be either FFFF = Application}
                       { OR If caller is a TSR the RequestType = Amount of  }
                       { Space wanted.                                      }
    ASM
      Push DX                       {  Save DX.                             }
      Push BX
      Mov AH,1                      {  Function to request HMA.             }
      Mov XMSError,0                {  Clear error flag.                    }
      Mov DX,RequestType            {  Load whether area is for an App or TSR.}
      Call [XMSControl]             {  Call XMS API                         }
      Mov XMSError,BL               {  Return any error code to user.       }
      Pop Bx
      Pop DX                        {  Restore DX.                          }
    END;{HMARequest}

    PROCEDURE HMARelease; ASSEMBLER;
                          { Routine to release previously allocated HMA.    }
                          { NOTE: Any Code/Data store in that HMA Memory    }
                          { Will become invalid and inaccessible.           }
    ASM
      Push DX                       {  Save DX.                             }
      Mov AH,2                      {  Function to release HMA.             }
      Mov XMSError,0                {  Clear error flag.                    }
      Call [XMSControl]             {  Call XMS API                         }
      Mov XMSError,BL               {  Return any error code to user.       }
      Pop DX                        {  Restore DX.                          }
    END;{HMARelease}

    PROCEDURE GlobaleEnableA20; ASSEMBLER;
                         { Routine to Enable the A20 Line. Should only be   }
                         { used by programs that have control of the HMA.   }
                         { NOTE: Remeber to disable the Line before         }
                         {       releaseing control of the system.          }
    ASM
      Push BX                       { Push BX onto the Stack.               }
      Mov XMSError,0                { Clear Error flag.                     }
      Mov AH,3                      { Function to Enable A20 line.          }
      Call [XMSControl]             { Call XMS Api.                         }
      Mov XMSError,BL               { Save any errors.                      }
      Pop BX                        { Pop BX Off the Stack.                 }
    END;{GlobalEnableA20}

    PROCEDURE GlobaleDisableA20; ASSEMBLER;
                          { Routine to Disable the A20 Line. On some systems}
                          { the Toggling of the A20 Line can take a long    }
                          { time.                                           }
    ASM
      Push BX                       { Push BX onto the Stack.               }
      Mov XMSError,0                { Clear Error flag.                     }
      Mov AH,4                      { Function to Disable A20 line.         }
      Call [XMSControl]             { Call XMS Api.                         }
      Mov XMSError,BL               { Save any errors.                      }
      Pop BX                        { Pop BX Off the Stack.                 }
    END;{GlobalDisableA20}

    PROCEDURE LocalEnableA20; ASSEMBLER;
                         { Routine to Enable the A20 Line for current Program}
                         { NOTE: Rember to so a LocalDisableA20 before      }
                         {       releasing system control.                  }
    ASM
      Push BX                       { Push BX onto the Stack.               }
      Mov XMSError,0                { Clear Error flag.                     }
      Mov AH,5                      { Function to Enable A20 line.          }
      Call [XMSControl]             { Call XMS Api.                         }
      Mov XMSError,BL               { Save any errors.                      }
      Pop BX                        { Pop BX Off the Stack.                 }
    END;{LocalEnableA20}

    PROCEDURE LocalDisableA20; ASSEMBLER;
                          { Routine to Locally Disable the A20 Line.        }
    ASM
      Push BX                       { Push BX onto the Stack.               }
      Mov XMSError,0                { Clear Error flag.                     }
      Mov AH,6                      { Function to Disable A20 line.         }
      Call [XMSControl]             { Call XMS Api.                         }
      Mov XMSError,BL               { Save any errors.                      }
      Pop BX                        { Pop BX Off the Stack.                 }
    END;{LocalDisableA20}

    FUNCTION QueryA20 :BOOLEAN; ASSEMBLER;
                         { Routine to test whether the A20 is Physically    }
                         { enabled or not.                                  }
    ASM
      Push BX                       { Push BX onto the Stack.               }
      Mov XMSError,0                { Clear Error flag.                     }
      Mov AH,7                      { Function to test the A20 line.        }
      Call [XMSControl]             { Call XMS Api.                         }
      Mov XMSError,BL               { Save any errors.                      }
      Pop BX                        { Pop BX Off the Stack.                 }
    END;{QueryA20}

    FUNCTION PtrToLong( P:POINTER ) :LONGINT; ASSEMBLER;
                         { Routine to convert a pointer to a 32 bit number. }
    ASM
      Mov AX,P.WORD[0]                 { Load low WORD into AX.             }
      Mov DX,P.WORD[2]                 { Load high WORD into DX.            }
    END;{PtrToLong}

    BEGIN
    END.

    {---------------------------- CUT HERE FOR DEMO -------------------}
    {***********************************************************************}
    PROGRAM XMSDemo1;            { Demonstration of the XMS Unit.           }
                                 { Last Updated Dec 10/93, Greg Estabrooks. }
    USES CRT,                    { IMPORT Clrscr,Writeln.                   }
         XMS;                    { IMPORT XMSDriver,XMSVer,XMSGetFreeMem,   }
                                 { XMSGetLargeBlock,XMSGetMem,XMSMove,      }
                                 { XMSError,XMSMovStruct,XMSFreeMem.        }
    VAR
       XMSHandle  :WORD;            { Holds the handle of our XMS Area.     }
       MovInf     :XMSMovStruct;    { Move Structure for Moving XMS Blocks. }
    BEGIN
      Clrscr;                       { Clear away any screen clutter.        }
      IF XMSDriver THEN             { If XMS Driver installed do demo.      }
      BEGIN
        Write('XMS Driver Version ');   { Show Version Installed.           }
        Writeln(HI(XMSVer),'.',LO(XMSVer),'.',XMSRev,' Installed');
        Writeln('Total Free XMS Memory : ',XMSGetFreeMem,'k');
        Writeln('Largest Free XMS Block: ',XMSGetLargeBlock,'k');
        Writeln;

        Writeln('Attempting to Allocate 16k of XMS');
        XMSHandle := XMSGetMem(16); { Attempt to allocate 16k of XMS.       }
        Writeln('ErrorCode Returned : ',XMSError);
        Writeln('Current free XMS Memory : ',XMSGetFreeMem);
        Writeln;

        Writeln('Saving Screen to XMS.');
        WITH MovInf DO
          BEGIN
            Amount := 4000;         { Length of the Video Screen.           }
            SourceHandle := 0;      { If SourceHandle is 0 then SourceOffset}
                                    { Is Interpereted as a SEGMENT:OFFSET   }
                                    { into conventional memory.             }
            SourceOffset := PtrToLong(Ptr($B800,0));
            DestHandle := XMSHandle;{ Destination is our XMS block.         }
            DestOffset := 0;
          END;
        XMSMoveBlock(MovInf);
        Writeln('Press <ENTER> to continue.');
        Readln;

        Clrscr;
        Writeln('Press <ENTER> to Restore Screen.');
        Readln;

        WITH MovInf DO
          BEGIN
            Amount := 4000;         { Length of the Video Screen.           }
            SourceHandle := XMSHandle;
            SourceOffset := 0;
            DestHandle := 0;
            DestOffset := PtrToLong(Ptr($B800,0));;
          END;
        XMSMoveBlock(MovInf);
        GotoXY(1,11);
        XMSFreeMem(XMSHandle);      { Free allocate XMS.                    }
        Writeln('Ending Free XMS Memory : ',XMSGetFreeMem,'k');
      END
      ELSE
        Writeln('XMS Driver not Installed!',^G);
      Readln;
    END.{XMSDemo1}
    {***********************************************************************}

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