
{*******************************************************}
{                                                       }
{       Turbo Pascal Version 7.0                        }
{       Turbo Vision Unit                               }
{                                                       }
{       Copyright (c) 1992 Borland International        }
{                                                       }
{*******************************************************}

Unit Memory;

{$IFNDEF OS2}
{$O+,F+,X+,I-,S-,Q-}

Interface

Const
  MaxHeapSize: Word = 655360 Div 16;    { 640K }
  LowMemSize: Word = 4096 Div 16;       {   4K }
  MaxBufMem: Word = 65536 Div 16;       {  64K }

Procedure InitMemory;
Procedure DoneMemory;
Procedure InitDosMem;
Procedure DoneDosMem;
Function LowMemory: Boolean;
Function MemAlloc (Size: Word): Pointer;
Function MemAllocSeg (Size: Word): Pointer;
Procedure NewCache (Var P: Pointer; Size: Word);
Procedure DisposeCache (P: Pointer);
Procedure NewBuffer (Var P: Pointer; Size: Word);
Procedure DisposeBuffer (P: Pointer);
Function GetBufferSize (P: Pointer): Word;
Function SetBufferSize (P: Pointer; Size: Word): Boolean;
Procedure GetBufMem (Var P: Pointer; Size: Word);
Procedure FreeBufMem (P: Pointer);
Procedure SetMemTop (MemTop: Pointer);

Implementation

Type
  PtrRec = Record
             Ofs, Seg: Word;
           End;

Type
  PCache = ^TCache;
  TCache = Record
             Size: Word;
             Master: ^Pointer;
             Data: Record End;
           End;

Type
  PBuffer = ^TBuffer;
  TBuffer = Record
              Size: Word;
              Master: ^Word;
            End;

Const
  CachePtr: Pointer = Nil;
  HeapResult: Integer = 0;
  BufHeapPtr: Word = 0;
  BufHeapEnd: Word = 0;

Function HeapNotify (Size: Word): Integer; Far; Assembler;
Asm
  CMP   Size, 0
  JNE   @@3
  @@1:  MOV     AX, CachePtr. Word [2]
  CMP   AX, HeapPtr. Word [2]
  JA    @@3
  JB    @@2
  MOV   AX, CachePtr. Word [0]
  CMP   AX, HeapPtr. Word [0]
  JAE   @@3
  @@2:  XOr     AX, AX
  PUSH  AX
  PUSH  AX
  Call  DisposeCache
  JMP   @@1
  @@3:  MOV     AX, HeapResult
End;

Procedure FreeCacheMem;
Begin
  While CachePtr <> HeapEnd Do DisposeCache (CachePtr);
End;

Procedure InitMemory;
Var
  HeapSize: Word;
Begin
  HeapError := @HeapNotify;
  If BufHeapPtr = 0 Then
  Begin
    HeapSize := PtrRec (HeapEnd).Seg - PtrRec (HeapOrg).Seg;
    If HeapSize > MaxHeapSize Then HeapSize := MaxHeapSize;
    BufHeapEnd := PtrRec (HeapEnd).Seg;
    PtrRec (HeapEnd).Seg := PtrRec (HeapOrg).Seg + HeapSize;
    BufHeapPtr := PtrRec (HeapEnd).Seg;
  End;
  CachePtr := HeapEnd;
End;

Procedure DoneMemory;
Begin
  FreeCacheMem;
End;

Procedure InitDosMem;
Begin
  SetMemTop (Ptr (BufHeapEnd, 0) );
End;

Procedure DoneDosMem;
Var
  MemTop: Pointer;
Begin
  MemTop := Ptr (BufHeapPtr, 0);
  If BufHeapPtr = PtrRec (HeapEnd).Seg Then
  Begin
    FreeCacheMem;
    MemTop := HeapPtr;
  End;
  SetMemTop (MemTop);
End;

Function LowMemory: Boolean; Assembler;
Asm
  MOV   AX, HeapEnd. Word [2]
  SUB   AX, HeapPtr. Word [2]
  SUB   AX, LowMemSize
  SBB   AX, AX
  NEG   AX
End;

Function MemAlloc (Size: Word): Pointer;
Var
  P: Pointer;
Begin
  HeapResult := 1;
  GetMem (P, Size);
  HeapResult := 0;
  If (P <> Nil) And LowMemory Then
  Begin
    FreeMem (P, Size);
    P := Nil;
  End;
  MemAlloc := P;
End;

Function MemAllocSeg (Size: Word): Pointer;
Var
  P, T: Pointer;
Begin
  Size := (Size + 7) And $FFF8;
  P := MemAlloc (Size + 8);
  If P <> Nil Then
  Begin
    If PtrRec (P).Ofs = 0 Then
    Begin
      PtrRec (T).Ofs := Size And 15;
      PtrRec (T).Seg := PtrRec (P).Seg + Size ShR 4;
    End Else
    Begin
      T := P;
      PtrRec (P).Ofs := 0;
      Inc (PtrRec (P).Seg);
    End;
    FreeMem (T, 8);
  End;
  MemAllocSeg := P;
End;

Procedure NewCache (Var P: Pointer; Size: Word); Assembler;
Asm
  LES   DI, P
  MOV   AX, Size
  ADD   AX, (Type TCache) + 15
  MOV   CL, 4
  ShR   AX, CL
  MOV   DX, CachePtr. Word [2]
  SUB   DX, AX
  JC    @@1
  CMP   DX, HeapPtr. Word [2]
  JBE   @@1
  MOV     CX, HeapEnd. Word [2]
  SUB   CX, DX
  CMP   CX, MaxBufMem
  JA    @@1
  MOV   CachePtr. Word [2], DX
  PUSH  DS
  MOV   DS, DX
  XOr   SI, SI
  MOV   DS: [SI].TCache. Size, AX
  MOV   DS: [SI].TCache. Master. Word [0], DI
  MOV   DS: [SI].TCache. Master. Word [2], ES
  POP   DS
  MOV   AX, Offset TCache. Data
  JMP   @@2
  @@1:  XOr     AX, AX
  CWD
  @@2:  CLD
  STOSW
  XCHG  AX, DX
  STOSW
End;

Procedure DisposeCache (P: Pointer); Assembler;
Asm
  MOV   AX, CachePtr. Word [2]
  XOr   BX, BX
  XOr   CX, CX
  MOV   DX, P. Word [2]
  @@1:  MOV     ES, AX
  CMP   AX, DX
  JE    @@2
  ADD   AX, ES: [BX].TCache. Size
  CMP   AX, HeapEnd. Word [2]
  JE    @@2
  PUSH  ES
  Inc   CX
  JMP   @@1
  @@2:  PUSH    ES
  LES   DI, ES: [BX].TCache. Master
  XOr   AX, AX
  CLD
  STOSW
  STOSW
  POP   ES
  MOV   AX, ES: [BX].TCache. Size
  JCXZ  @@4
  @@3:  POP     DX
  PUSH  DS
  PUSH  CX
  MOV   DS, DX
  ADD   DX, AX
  MOV   ES, DX
  MOV   SI, DS: [BX].TCache. Size
  MOV   CL, 3
  ShL   SI, CL
  MOV   CX, SI
  ShL   SI, 1
  Dec   SI
  Dec   SI
  MOV   DI, SI
  STD
  REP   MOVSW
  LDS   SI, ES: [BX].TCache. Master
  MOV   DS: [SI].Word [2], ES
  POP   CX
  POP   DS
  LOOP  @@3
  @@4:  ADD     CachePtr. Word [2], AX
End;

Procedure MoveSeg (Source, Dest, Size: Word); Near; Assembler;
Asm
  PUSH  DS
  MOV   AX, Source
  MOV   DX, Dest
  MOV   BX, Size
  CMP   AX, DX
  JB    @@3
  CLD
  @@1:  MOV     CX, 0FFFH
  CMP   CX, BX
  JB    @@2
  MOV   CX, BX
  @@2:  MOV     DS, AX
  MOV   ES, DX
  ADD   AX, CX
  ADD   DX, CX
  SUB   BX, CX
  ShL   CX, 1
  ShL   CX, 1
  ShL   CX, 1
  XOr   SI, SI
  XOr   DI, DI
  REP   MOVSW
  Or    BX, BX
  JNE   @@1
  JMP   @@6
  @@3:  ADD     AX, BX
  ADD   DX, BX
  STD
  @@4:  MOV     CX, 0FFFH
  CMP   CX, BX
  JB    @@5
  MOV   CX, BX
  @@5:    SUB   AX, CX
  SUB   DX, CX
  SUB   BX, CX
  MOV   DS, AX
  MOV   ES, DX
  ShL   CX, 1
  ShL   CX, 1
  ShL   CX, 1
  MOV   SI, CX
  Dec   SI
  ShL   SI, 1
  MOV   DI, SI
  REP   MOVSW
  Or    BX, BX
  JNE   @@4
  @@6:  POP     DS
End;

Function GetBufSize (P: PBuffer): Word;
Begin
  GetBufSize := (P^. Size + 15) ShR 4 + 1;
End;

Procedure SetBufSize (P: PBuffer; NewSize: Word);
Var
  CurSize: Word;
Begin
  CurSize := GetBufSize (P);
  MoveSeg (PtrRec (P).Seg + CurSize, PtrRec (P).Seg + NewSize,
  BufHeapPtr - PtrRec (P).Seg - CurSize);
  Inc (BufHeapPtr, NewSize - CurSize);
  Inc (PtrRec (P).Seg, NewSize);
  While PtrRec (P).Seg < BufHeapPtr Do
  Begin
    Inc (P^. Master^, NewSize - CurSize);
    Inc (PtrRec (P).Seg, (P^. Size + 15) ShR 4 + 1);
  End;
End;

Procedure NewBuffer (Var P: Pointer; Size: Word);
Var
  BufSize: Word;
  Buffer: PBuffer;
Begin
  BufSize := (Size + 15) ShR 4 + 1;
  If BufHeapPtr + BufSize > BufHeapEnd Then P := Nil Else
  Begin
    Buffer := Ptr (BufHeapPtr, 0);
    Buffer^. Size := Size;
    Buffer^. Master := @PtrRec (P).Seg;
    P := Ptr (BufHeapPtr + 1, 0);
    Inc (BufHeapPtr, BufSize);
  End;
End;

Procedure DisposeBuffer (P: Pointer);
Begin
  Dec (PtrRec (P).Seg);
  SetBufSize (P, 0);
End;

Function GetBufferSize (P: Pointer): Word;
Begin
  Dec (PtrRec (P).Seg);
  GetBufferSize := PBuffer (P)^. Size;
End;

Function SetBufferSize (P: Pointer; Size: Word): Boolean;
Var
  NewSize: Word;
Begin
  Dec (PtrRec (P).Seg);
  NewSize := (Size + 15) ShR 4 + 1;
  SetBufferSize := False;
  If BufHeapPtr + NewSize - GetBufSize (P) <= BufHeapEnd Then
  Begin
    SetBufSize (P, NewSize);
    PBuffer (P)^. Size := Size;
    SetBufferSize := True;
  End;
End;

Procedure GetBufMem (Var P: Pointer; Size: Word);
Begin
  NewCache (P, Size);
End;

Procedure FreeBufMem (P: Pointer);
Begin
  DisposeCache (P);
End;

Procedure SetMemTop (MemTop: Pointer); Assembler;
Asm
  MOV   BX, MemTop. Word [0]
  ADD   BX, 15
  MOV   CL, 4
  ShR   BX, CL
  ADD   BX, MemTop. Word [2]
  MOV   AX, PrefixSeg
  SUB   BX, AX
  MOV   ES, AX
  MOV   AH, 4AH
  Int   21H
End;

{$ELSE}

{$X+,I-,S-,Q-}

interface

uses Use32;

const
  LowMemSize: Word = 4096 div 16;       {   4K }

procedure InitMemory;
procedure DoneMemory;
procedure InitDosMem;
procedure DoneDosMem;
function LowMemory: Boolean;
function MemAlloc(Size: Word): Pointer;
procedure NewCache(var P: Pointer; Size: Word);
procedure DisposeCache(P: Pointer);
procedure NewBuffer(var P: Pointer; Size: Word);
procedure DisposeBuffer(P: Pointer);
function GetBufferSize(P: Pointer): Word;
function SetBufferSize(P: Pointer; Size: Word): Boolean;

{ The following procedure is not implemented

function MemAllocSeg(Size: Word): Pointer;

}

implementation

type
  PtrRec = record
    Ofs: Longint;
  end;

type
  PCache = ^TCache;
  TCache = record
    Next: PCache;
    Master: ^Pointer;
    Size: Word;
    Data: record end;
  end;

  PBuffer = ^TBuffer;
  TBuffer = record
    Next: PBuffer;
    Size: Word;
    Data: record end;
  end;

const
  CacheList: PCache = nil;
  SafetyPool: Pointer = nil;
  BufferList: PBuffer = nil;
  SafetyPoolSize: Word = 0;
  DisablePool: Boolean = False;

function FreeCache: Boolean;
begin
  FreeCache := False;
  if CacheList <> nil then
  begin
    DisposeCache(CacheList^.Next^.Master^);
    FreeCache := True;
  end;
end;

function FreeSafetyPool: Boolean;
begin
  FreeSafetyPool := False;
  if SafetyPool <> nil then
  begin
    FreeMem(SafetyPool, SafetyPoolSize);
    SafetyPool := nil;
    FreeSafetyPool := True;
  end;
end;

function HeapNotify(Size: Word): Integer;
begin
  if FreeCache then HeapNotify := 2 else
    if DisablePool then HeapNotify := 1 else
      if FreeSafetyPool then HeapNotify := 2 else HeapNotify := 0;
end;

procedure InitMemory;
begin
  HeapError := @HeapNotify;
  SafetyPoolSize := LowMemSize * 16;
  LowMemory;
end;

procedure DoneMemory;
begin
  while FreeCache do;
  FreeSafetyPool;
end;

procedure InitDosMem;
begin
end;

procedure DoneDosMem;
begin
end;

function LowMemory: Boolean;
begin
  LowMemory := False;
  if SafetyPool = nil then
  begin
    SafetyPool := MemAlloc(SafetyPoolSize);
    if SafetyPool = nil then LowMemory := True;
  end;
end;

function MemAlloc(Size: Word): Pointer;
var
  P: Pointer;
begin
  DisablePool := True;
  GetMem(P, Size);
  DisablePool := False;
  MemAlloc := P;
end;

procedure NewCache(var P: Pointer; Size: Word);
var
  Cache: PCache;
begin
  Inc(Size, SizeOf(TCache));
  if MaxAvail >= Size then GetMem(Cache,Size) else Cache := nil;
  if Cache <> nil then
  begin
    if CacheList = nil then Cache^.Next := Cache else
    begin
      Cache^.Next := CacheList^.Next;
      CacheList^.Next := Cache;
    end;
    CacheList := Cache;
    Cache^.Master := @P;
    Cache^.Size := Size;
    Inc(PtrRec(Cache).Ofs, SizeOf(TCache));
  end;
  P := Cache;
end;

procedure DisposeCache(P: Pointer);
var
  Cache, C: PCache;
begin
  PtrRec(Cache).Ofs := PtrRec(P).Ofs - SizeOf(TCache);
  C := CacheList;
  while (C^.Next <> Cache) and (C^.Next <> CacheList) do C := C^.Next;
  if C^.Next = Cache then
  begin
    if C = Cache then CacheList := nil else
    begin
      if CacheList = Cache then CacheList := C;
      C^.Next := Cache^.Next;
    end;
    Cache^.Master^ := nil;
    FreeMem(Cache,Cache^.Size);
  end;
end;

procedure NewBuffer(var P: Pointer; Size: Word);
var
  Buffer: PBuffer;
begin
  Inc(Size, SizeOf(TBuffer));
  Buffer := MemAlloc(Size);
  if Buffer <> nil then
  begin
    Buffer^.Next := BufferList;
    Buffer^.Size := Size;
    BufferList := Buffer;
    Inc(PtrRec(Buffer).Ofs, SizeOf(TBuffer));
  end;
  P := Buffer;
end;

procedure DisposeBuffer(P: Pointer);
var
  Buffer,PrevBuf: PBuffer;
begin
  if P <> nil then
  begin
    Dec(PtrRec(P).Ofs, SizeOf(TBuffer));
    Buffer := BufferList;
    PrevBuf := nil;
    while (Buffer <> nil) and (P <> Buffer) do
    begin
      PrevBuf := Buffer;
      Buffer := Buffer^.Next;
    end;
    if Buffer <> nil then
    begin
      if PrevBuf = nil then BufferList := Buffer^.Next else PrevBuf^.Next := Buffer^.Next;
      FreeMem(Buffer,Buffer^.Size);
    end;
  end;
end;

function GetBufferSize(P: Pointer): Word;
begin
  if P = nil then GetBufferSize := 0
 else
  begin
    Dec(PtrRec(P).Ofs,SizeOf(TBuffer));
    GetBufferSize := PBuffer(P)^.Size;
  end;
end;

function SetBufferSize(P: Pointer; Size: Word): Boolean;
begin
  SetBufferSize := False;
end;

{$ENDIF}

End.
