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 to continue.'); Readln; Clrscr; Writeln('Press 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} {***********************************************************************}