{---------------- 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.