{$A-,B-,D-,E-,F-,G-,I-,L-,N-,O-,R-,S+,V-,X-} Unit UMB_Heap; {----------------------------------------------------------------------------} interface Procedure Extend_Heap; { Use Upper Memory Blocks (UMB) to extend } { the Turbo Pascal 6.0 heap. This procedure } { should be called as soon as possible in } { your code. } var UMB_Heap_Debug : Boolean; { If true, releases UMBs immediately to make } { sure they're available for the next run } { without rebooting. Used when debugging in } { the IDE. If not used then, the UMBs may } { not get freed between executions. } {----------------------------------------------------------------------------} implementation const Max_Blocks = 4; { It's not likely more than 4 UMBs are needed } type PFreeRec = ^TFreeRec; { From pg. 216 of the TP6 programmer's guide. } TFreeRec = record { It's used for traversing the free blocks of } Next : PFreeRec; { the heap. } Size : Pointer; end; var XMS_Driver : Pointer; { Pointer to the XMS driver. } Num_Blocks : Word; Block_Address, Block_Size : Array[1..Max_Blocks+1] of Pointer; SaveExitProc : Pointer; {----------------------------------------------------------------------------} { Swap to pointers. Needed when sorting the UMB addresses. } Procedure Pointer_Swap(var A,B : Pointer); var Temp : Pointer; Begin Temp := A; A := B; B := Temp; End; {----------------------------------------------------------------------------} Function XMS_Driver_Present : Boolean; { XMS software present? } var Result : Boolean; Begin Result := False; { Assume no XMS driver } asm @Begin: mov ax,4300h int 2Fh cmp al,80h jne @Fail mov ax,4310h int 2Fh mov word ptr XMS_Driver+2,es { Get the XMS driver entry point } mov word ptr XMS_Driver,bx mov Result,1 jmp @End @Fail: mov Result,0 @End: end; XMS_Driver_Present := Result; End; {----------------------------------------------------------------------------} Procedure Allocate_UMB_Heap; { Add the four largest UMBs to the heap } var i,j : Word; UMB_Strategy, DOS_Strategy, Segment,Size : Word; Get_Direct : Boolean; { Get UMB direct from XMS if TRUE, else from DOS } Begin Num_Blocks := 0; for i := 1 to Max_Blocks do begin Block_Address[i] := nil; Block_Size[i] := nil; end; asm mov ax,5800h int 21h { Get and save the DOS allocation strategy } mov [DOS_Strategy],ax mov ax,5802h int 21h { Get and save the UMB allocation strategy } mov [UMB_Strategy],ax mov ax,5801h mov bx,0000h int 21h { Set the DOS allocation strategy so that } mov ax,5803h { it uses only high memory } { DON'T TRUST THIS FUNCTION. DOS WILL GO } { AHEAD AND TRY TO ALLOCATE LOWER MEMORY } { EVEN AFTER YOU TELL IT NOT TO! } mov bx,0001h int 21h { Set the UMB allocation strategy so that } end; { UMBs are added to the DOS mem chain } Get_Direct := True; { Try to get UMBs directly from the XMS } { if possible. } for i := 1 to Max_Blocks do begin Segment := 0; Size := 0; if Get_Direct then { Get a UMB direct from the XMS driver. } begin asm @Begin: mov ax,01000h mov dx,0FFFFh { Ask for the impossible to ... } push ds { Get the size of the next largest UMB } mov cx,ds mov es,cx call es:[XMS_Driver] cmp dx,100h { Don't bother with anything < 1K } jl @End mov ax,01000h call es:[XMS_Driver] { Get the next largest UMB } cmp ax,1 jne @End cmp bx,0A000h { It better be above 640K } jl @End { We can't trust DOS 5.00 } mov [Segment],bx mov [Size],dx @End: pop ds end; if ((i = 1) and (Size = 0)) then { if we couldn't get the UMB } Get_Direct := False; { from the XMS driver, don't } end; { try again the next time. } if (not Get_Direct) then { Get a UMB via DOS } begin asm @Begin: mov ax,4800h mov bx,0FFFFh { Ask for the impossible to ... } int 21h { Get the size of the next largest UMB } cmp bx,100h { Don't bother with anything < 1K } jl @End mov ax,4800h int 21h { Get the next largest UMB } jc @End cmp ax,0A000h { It better be above 640K } jl @End { We can't trust DOS 5.00 } mov [Segment],ax mov [Size],bx @End: end; end; if (Segment > 0) then { Did it work? } begin Block_Address[i] := Ptr(Segment,0); Inc(Num_Blocks); end; Block_Size[i] := Ptr(Size,0); end; if (Num_Blocks > 0) then { Sort the UMB addrs in ASC order } begin for i := 1 to Num_Blocks-1 do for j := i+1 to Num_Blocks do if (Seg(Block_Address[i]^) > Seg(Block_Address[j]^)) then begin Pointer_Swap(Block_Address[i],Block_Address[j]); Pointer_Swap(Block_Size[i],Block_Size[j]); end; end; asm mov ax,5803h mov bx,[UMB_Strategy] int 21h { Restore the UMB allocation strategy } mov ax,5801h mov bx,[DOS_Strategy] int 21h { Restore the DOS allocation strategy } end; End; {----------------------------------------------------------------------------} Procedure Release_UMB; far; { Exit procedure to release UMBs } var i : Word; Segment : Word; Begin ExitProc := SaveExitProc; if (Num_Blocks > 0) then begin asm mov ax,5803h mov bx,0000h int 21h { Set the UMB status to release UMBs } end; for i := 1 to Num_Blocks do begin Segment := Seg(Block_Address[i]^); if (Segment > 0) then asm mov ax,$4901 mov bx,[Segment] mov es,bx int 21h { Release the UMB } end; end; end; End; {----------------------------------------------------------------------------} Procedure Extend_Heap; var i : Word; Temp : PFreeRec; Begin if XMS_Driver_Present then begin Allocate_UMB_Heap; if UMB_Heap_Debug then Release_UMB; if (Num_Blocks > 0) then begin { Attach UMBs to the FreeList } for i := 1 to Num_Blocks do PFreeRec(Block_Address[i])^.Size := Block_Size[i]; for i := 1 to Num_Blocks do PFreeRec(Block_Address[i])^.Next := Block_Address[i+1]; PFreeRec(Block_Address[Num_Blocks])^.Next := nil; if (FreeList = HeapPtr) then with PFreeRec(FreeList)^ do begin Next := Block_Address[1]; Size := Ptr(Seg(HeapEnd^)-Seg(HeapPtr^),0); end else with PFreeRec(HeapPtr)^ do begin Next := Block_Address[1]; Size := Ptr(Seg(HeapEnd^)-Seg(HeapPtr^),0); end; { HEAPPTR MUST BE IN THE LAST FREE BLOCK SO THAT TP6 DOESN'T TRY TO USE ANY MEMORY BETWEEN 640K AND HEAPPTR } HeapPtr := Block_Address[Num_Blocks]; HeapEnd := Ptr(Seg(Block_Address[Num_Blocks]^)+Seg(Block_Size[Num_Blocks]^),0); end; end; End; {----------------------------------------------------------------------------} BEGIN UMB_Heap_Debug := False; Num_Blocks := 0; SaveExitProc := ExitProc; ExitProc := @Release_UMB; END. {----------------------------------------------------------------------------}