{********[ SOURCE FILE OF GRAPHICAL FREE VISION ]**********}
{                                                          }
{          System independent clone of MEMORY.PAS          }
{                                                          }
{   Interface Copyright (c) 1992 Borland International     }
{                                                          }
{   Copyright (c) 1996, 1997, 1998, 1999 by Leon de Boer   }
{   ldeboer@attglobal.net  - primary e-mail address        }
{   ldeboer@starwon.com.au - backup e-mail address         }
{                                                          }
{****************[ THIS CODE IS FREEWARE ]*****************}
{                                                          }
{     This sourcecode is released for the purpose to       }
{   promote the pascal language on all platforms. You may  }
{   redistribute it and/or modify with the following       }
{   DISCLAIMER.                                            }
{                                                          }
{     This SOURCE CODE is distributed "AS IS" WITHOUT      }
{   WARRANTIES AS TO PERFORMANCE OF MERCHANTABILITY OR     }
{   ANY OTHER WARRANTIES WHETHER EXPRESSED OR IMPLIED.     }
{                                                          }
{*****************[ SUPPORTED PLATFORMS ]******************}
{     16 and 32 Bit compilers                              }
{        DOS      - Turbo Pascal 7.0 +      (16 Bit)       }
{        DPMI     - Turbo Pascal 7.0 +      (16 Bit)       }
{                 - FPC 0.9912+ (GO32V2)    (32 Bit)       }
{        WINDOWS  - Turbo Pascal 7.0 +      (16 Bit)       }
{                 - Delphi 1.0+             (16 Bit)       }
{        WIN95/NT - Delphi 2.0+             (32 Bit)       }
{                 - Virtual Pascal 2.0+     (32 Bit)       }
{                 - Speedsoft Sybil 2.0+    (32 Bit)       }
{                 - FPC 0.9912+             (32 Bit)       }
{        OS2      - Virtual Pascal 1.0+     (32 Bit)       }
{                                                          }
{******************[ REVISION HISTORY ]********************}
{  Version  Date        Fix                                }
{  -------  ---------   ---------------------------------  }
{  1.00     19 feb 96   Initial DOS/DPMI code released.    }
{  1.10     18 Jul 97   Windows conversion added.          }
{  1.20     29 Aug 97   Platform.inc sort added.           }
{  1.30     05 May 98   Virtual pascal 2.0 code added.     }
{  1.40     01 Oct 99   Complete multiplatform rewrite     }
{  1.41     03 Nov 99   FPC Windows support added          }
{**********************************************************}

UNIT Memory;

{====Include file to sort compiler platform out =====================}
{$I platform.inc}
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
                                  INTERFACE
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}

{====================================================================}

{==== Compiler directives ===========================================}

{$IFNDEF PPC_FPC}{ FPC doesn't support these switches }
  {$F+} { Force far calls }
  {$A+} { Word Align Data }
  {$B-} { Allow short circuit boolean evaluations }
  {$O+} { This unit may be overlaid }
  {$G+} { 286 Code optimization - if you're on an 8088 get a real computer }
  {$P-} { Normal string variables }
  {$N-} { No 80x87 code generation }
  {$E+} { Emulation is on }
{$ENDIF}

{$X+} { Extended syntax is ok }
{$R-} { Disable range checking }
{$S-} { Disable Stack Checking }
{$I-} { Disable IO Checking }
{$Q-} { Disable Overflow Checking }
{$V-} { Turn off strict VAR strings }
{====================================================================}

USES FVCommon;

{***************************************************************************}
{                            INTERFACE ROUTINES                             }
{***************************************************************************}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                           MEMORY ACCESS ROUTINES                          }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-MemAlloc-----------------------------------------------------------
Allocates the requested size of memory if this takes memory free below
the safety pool then a nil pointer is returned.
01Oct99 LdB
---------------------------------------------------------------------}
FUNCTION MemAlloc (Size: Word): Pointer;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                 MEMORY MANAGER SYSTEM CONTROL ROUTINES                    }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-LowMemory----------------------------------------------------------
Returns if the free memory left is below the safety pool value.
01Oct99 LdB
---------------------------------------------------------------------}
FUNCTION LowMemory: Boolean;

{-InitMemory---------------------------------------------------------
Initializes the memory and safety pool manager. This should be called
prior to using any of the memory manager routines.
01Oct99 LdB
---------------------------------------------------------------------}
PROCEDURE InitMemory;

{-DoneMemory---------------------------------------------------------
Closes the memory and safety pool manager. This should be called after
using the memory manager routines so as to clean up.
01Oct99 LdB
---------------------------------------------------------------------}
PROCEDURE DoneMemory;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                           CACHE MEMORY ROUTINES                           }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-NewCache-----------------------------------------------------------
Create a new cache of given size in pointer P failure will return nil.
01Oct99 LdB
---------------------------------------------------------------------}
PROCEDURE NewCache (Var P: Pointer; Size: Word);

{-DisposeCache-------------------------------------------------------
Dispose of a cache buffer given by pointer P.
01Oct99 LdB
---------------------------------------------------------------------}
PROCEDURE DisposeCache (P: Pointer);

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                          BUFFER MEMORY ROUTINES                           }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-GetBufferSize------------------------------------------------------
Returns the size of memory buffer given by pointer P.
01Oct99 LdB
---------------------------------------------------------------------}
FUNCTION GetBufferSize (P: Pointer): Word;

{-SetBufferSize------------------------------------------------------
Change the size of buffer given by pointer P to the size requested.
01Oct99 LdB
---------------------------------------------------------------------}
FUNCTION SetBufferSize (var P: Pointer; Size: Word): Boolean;

{-DisposeBuffer------------------------------------------------------
Dispose of buffer given by pointer P.
01Oct99 LdB
---------------------------------------------------------------------}
PROCEDURE DisposeBuffer (P: Pointer);

{-NewBuffer----------------------------------------------------------
Create a new buffer of given size in ptr P failure will return nil.
01Oct99 LdB
---------------------------------------------------------------------}
PROCEDURE NewBuffer (Var P: Pointer; Size: Word);

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                        DOS MEMORY CONTROL ROUTINES                        }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-InitDosMem---------------------------------------------------------
Initialize memory manager routine for a shell to launch a DOS window.
Interface for compatability only under DPMI/WIN/NT/OS2 platforms.
01Oct99 LdB
---------------------------------------------------------------------}
PROCEDURE InitDosMem;

{-DoneDosMem---------------------------------------------------------
Finished shell to a DOS window so reset memory manager again.
Interface for compatability only under DPMI/WIN/NT/OS2 platforms.
01Oct99 LdB
---------------------------------------------------------------------}
PROCEDURE DoneDosMem;

{***************************************************************************}
{                         PUBLIC INITIALIZED VARIABLES                      }
{***************************************************************************}
CONST
   LowMemSize    : Word = 4096 DIV 16;                {   4K }
   SafetyPoolSize: Word = 8192;                       { Safety pool size }
{$IFDEF PROC_REAL}                                    { REAL MODE DOS CODE }
   MaxHeapSize   : Word = 655360 DIV 16;              { 640K }
   MaxBufMem     : Word = 65536 DIV 16;               {  64K }
{$ENDIF}

{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
                                IMPLEMENTATION
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
{$IFDEF OS_WINDOWS}                                   { WIN/NT CODE }
   {$IFDEF PPC_FPC}                                   { FPC WINDOWS COMPILER }
   USES Windows;                                      { Standard unit }
   {$ELSE}                                            { OTHER COMPILERS }
   USES WinProcs, WinTypes;                           { Standard units }
   {$ENDIF}
{$ENDIF}

{$IFDEF OS_OS2}                                       { OS2 CODE }
  {$IFDEF PPC_FPC}
     USES DosCalls;                                        { Standard unit }
  {$ELSE}
     USES Os2Base;                                         { Standard unit }
  {$ENDIF}
{$ENDIF}

{***************************************************************************}
{                      PRIVATE RECORD TYPE DEFINITIONS                      }
{***************************************************************************}

{---------------------------------------------------------------------------}
{                         TBuffer RECORD DEFINITION                         }
{---------------------------------------------------------------------------}
TYPE
   PBuffer = ^TBuffer;                                { Buffer pointer }
   TBuffer =
{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}
   PACKED
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
   RECORD
     {$IFDEF PROC_REAL}                               { REAL MODE DOS CODE }
     Size  : Word;                                    { Buffer size }
     Master: ^Word;                                   { Master buffer }
     {$ELSE}                                          { DPMI/WIN/NT/OS2 CODE }
     Next: PBuffer;                                   { Next buffer }
     Size: Word;                                      { Buffer size }
     Data: RECORD END;                                { Buffer data }
     {$ENDIF}
   END;

{---------------------------------------------------------------------------}
{                     POINTER TYPE CONVERSION RECORDS                       }
{---------------------------------------------------------------------------}
TYPE
   PtrRec =
{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}
   PACKED
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
   RECORD
     Ofs, Seg: Word;                                  { Pointer to words }
   END;

{---------------------------------------------------------------------------}
{                          TCache RECORD DEFINITION                         }
{---------------------------------------------------------------------------}
TYPE
   PCache = ^TCache;                                  { Cache pointer }
{$IFDEF PROC_REAL}                                    { REAL MODE DOS CODE }
   TCache =
{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}
   PACKED
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
   RECORD
      Size  : Word;                                   { Cache size }
      Master: ^Pointer;                               { Master cache }
      Data  : RECORD END;                             { Cache data }
   END;
{$ELSE}                                               { DPMI/WIN/NT/OS2 CODE }
   TCache =
{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}
   PACKED
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
   RECORD
     Next  : PCache;                                  { Next cache }
     Master: ^Pointer;                                { Master cache }
     Size  : Word;                                    { Size of cache }
     Data  : RECORD END;                              { Cache data }
   End;
{$ENDIF}

{***************************************************************************}
{                       INITIALIZED PRIVATE VARIABLES                       }
{***************************************************************************}
CONST
   DisablePool: Boolean = False;                      { Disable safety pool }
   SafetyPool : Pointer = Nil;                        { Safety pool memory }
{$IFDEF PROC_REAL}                                    { REAL MODE DOS CODE }
   HeapResult: Integer = 0;                           { Heap result }
   BufHeapPtr: Word = 0;                              { Heap position }
   BufHeapEnd: Word = 0;                              { Heap end }
   CachePtr  : Pointer = Nil;                         { Cache list }
{$ELSE}                                               { DPMI/WIN/NT/OS2 CODE }
   CacheList : PCache = Nil;                          { Cache list }
   BufferList: PBuffer = Nil;                         { Buffer list }
{$ENDIF}

{***************************************************************************}
{                          PRIVATE UNIT ROUTINES                            }
{***************************************************************************}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{             PRIVATE UNIT ROUTINES - REAL MODE DOS PLATFORMS               }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{$IFDEF PROC_REAL}                                    { REAL MODE DOS CODE }
{---------------------------------------------------------------------------}
{  GetBufSize -> Platforms DOS REAL MODE - Updated 01Oct99 LdB              }
{---------------------------------------------------------------------------}
FUNCTION GetBufSize (P: PBuffer): Word; {$IFNDEF PPC_FPC}FAR;{$ENDIF}
BEGIN
   GetBufSize := (P^.Size + 15) SHR 4 + 1;            { Buffer paragraphs }
END;

{---------------------------------------------------------------------------}
{  FreeCacheMem -> Platforms DOS REAL MODE - Updated 01Oct99 LdB            }
{---------------------------------------------------------------------------}
PROCEDURE FreeCacheMem; {$IFNDEF PPC_FPC}FAR;{$ENDIF}
BEGIN
   While (CachePtr <> HeapEnd) Do
     DisposeCache(CachePtr);                          { Release blocks }
END;

{---------------------------------------------------------------------------}
{  SetMemTop -> Platforms DOS REAL MODE - Updated 01Oct99 LdB               }
{---------------------------------------------------------------------------}
PROCEDURE SetMemTop (MemTop: Pointer); ASSEMBLER;
ASM
   MOV BX, MemTop.Word[0];                            { Top of memory }
   ADD BX, 15;
   MOV CL, 4;
   SHR BX, CL;                                        { Size in paragraphs }
   ADD BX, MemTop.Word[2];
   MOV AX, PrefixSeg;                                 { Add prefix seg }
   SUB BX, AX;
   MOV ES, AX;
   MOV AH, 4AH;
   INT 21H;                                           { Call to DOS }
END;

{---------------------------------------------------------------------------}
{  MoveSeg -> Platforms DOS REAL MODE - Updated 01Oct99 LdB                 }
{---------------------------------------------------------------------------}
PROCEDURE MoveSeg (Source, Dest, Size: Word); NEAR; ASSEMBLER;
ASM
   PUSH DS;                                           { Save register }
   MOV AX, Source;
   MOV DX, Dest;                                      { Destination }
   MOV BX, Size;
   CMP AX, DX;                                        { Does Source=Dest? }
   JB @@3;
   CLD;                                               { Go forward }
@@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, 3;                                         { Mult x8 }
   XOR SI, SI;
   XOR DI, DI;
   REP MOVSW;
   OR BX, BX;
   JNE @@1;
   JMP @@6;
@@3:                                                  { Source=Dest }
   ADD AX, BX;                                        { Hold register }
   ADD DX, BX;                                        { Must go backwards }
   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, 3;                                         { Mult x8 }
   MOV SI, CX;
   DEC SI;
   SHL SI, 1;
   MOV DI, SI;
   REP MOVSW;                                         { Move data }
   OR BX, BX;
   JNE @@4;
@@6:
   POP DS;                                            { Recover register }
END;

{---------------------------------------------------------------------------}
{  SetBufSize -> Platforms DOS REAL MODE - Updated 01Oct99 LdB              }
{---------------------------------------------------------------------------}
PROCEDURE SetBufSize (P: PBuffer; NewSize: Word); {$IFNDEF PPC_FPC}FAR;{$ENDIF}
VAR CurSize: Word;
BEGIN
   CurSize := GetBufSize(P);                          { Current size }
   MoveSeg(PtrRec(P).Seg + CurSize, PtrRec(P).Seg+
     NewSize, BufHeapPtr - PtrRec(P).Seg - CurSize);  { Move data }
   Inc(BufHeapPtr, NewSize - CurSize);                { Adjust heap space }
   Inc(PtrRec(P).Seg, NewSize);                       { Adjust pointer }
   While PtrRec(P).Seg < BufHeapPtr Do Begin
     Inc(P^.Master^, NewSize - CurSize);              { Adjust master }
     Inc(PtrRec(P).Seg, (P^.Size + 15) SHR 4 + 1);    { Adjust paragraphs }
   End;
END;
{$ENDIF}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{            PRIVATE UNIT ROUTINES - DPMI/WIN/NT/OS2 PLATFORMS              }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{$IFNDEF PROC_REAL}                                   { DPMI/WIN/NT/OS2 CODE }
{---------------------------------------------------------------------------}
{  FreeCache -> Platforms DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB             }
{---------------------------------------------------------------------------}
FUNCTION FreeCache: Boolean; {$IFNDEF PPC_FPC}FAR;{$ENDIF}
BEGIN
   FreeCache := False;                                { Preset fail }
   If (CacheList <> Nil) Then Begin
     DisposeCache(CacheList^.Next^.Master^);          { Dispose cache }
     FreeCache := True;                               { Return success }
   End;
END;

{---------------------------------------------------------------------------}
{  FreeCache -> Platforms DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB             }
{---------------------------------------------------------------------------}
FUNCTION FreeSafetyPool: Boolean; {$IFNDEF PPC_FPC}FAR;{$ENDIF}
BEGIN
   FreeSafetyPool := False;                           { Preset fail }
   If (SafetyPool <> Nil) Then Begin                  { Pool exists }
     FreeMem(SafetyPool, SafetyPoolSize);             { Release memory }
     SafetyPool := Nil;                               { Clear pointer }
     FreeSafetyPool := True;                          { Return true }
   End;
END;
{$ENDIF}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                 PRIVATE UNIT ROUTINES - ALL PLATFORMS                     }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  HeapNotify -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB        }
{---------------------------------------------------------------------------}
FUNCTION HeapNotify (Size: Word): Integer; {$IFNDEF PPC_FPC}FAR;{$ENDIF}
{$IFDEF PROC_REAL}                                    { REAL MODE DOS CODE }
ASSEMBLER;
ASM
   CMP Size, 0;                                       { Check for zero size }
   JNE @@3;                                           { Exit if size = zero }
@@1:
   MOV AX, CachePtr.Word[2];
   CMP AX, HeapPtr.Word[2];                           { Compare segments }
   JA @@3;
   JB @@2;
   MOV AX, CachePtr.Word[0];
   CMP AX, HeapPtr.Word[0];                           { Compare offsets }
   JAE @@3;
@@2:
   XOR AX, AX;                                        { Clear register }
   PUSH AX;                                           { Push zero }
   PUSH AX;                                           { Push zero }
   CALL DisposeCache;                                 { Dispose cache }
   JMP @@1;
@@3:
   MOV AX, HeapResult;                                { Return result }
END;
{$ELSE}                                               { DPMI/WIN/NT/OS2 }
BEGIN
   If FreeCache Then HeapNotify := 2 Else             { Release cache }
     If DisablePool Then HeapNotify := 1 Else         { Safetypool disabled }
       If FreeSafetyPool Then HeapNotify := 2 Else    { Free safety pool }
         HeapNotify := 0;                             { Return success }
END;
{$ENDIF}


{***************************************************************************}
{                            INTERFACE ROUTINES                             }
{***************************************************************************}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                           MEMORY ACCESS ROUTINES                          }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  MemAlloc -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB          }
{---------------------------------------------------------------------------}
FUNCTION MemAlloc (Size: Word): Pointer;
VAR P: Pointer;
BEGIN
   {$IFDEF PROC_REAL}                                 { REAL MODE DOS CODE }
   HeapResult := 1;                                   { Stop error calls }
   GetMem(P, Size);                                   { Get memory }
   HeapResult := 0;                                   { Reset error calls }
   If (P <> Nil) AND LowMemory Then Begin             { Low memory }
     FreeMem(P, Size);                                { Release memory }
     P := Nil;                                        { Clear pointer }
   End;
   MemAlloc := P;                                     { Return result }
   {$ELSE}                                            { DPMI/WIN/NT/OS2 }
   DisablePool := True;                               { Disable safety }
   GetMem(P, Size);                                   { Allocate memory }
   DisablePool := False;                              { Enable safety }
   MemAlloc := P;                                     { Return result }
   {$ENDIF}
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                 MEMORY MANAGER SYSTEM CONTROL ROUTINES                    }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  LowMemory -> Platforms DOS/DPMI/WIN/NT/OS2 - Checked 29Jun98 LdB         }
{---------------------------------------------------------------------------}
FUNCTION LowMemory: Boolean;
{$IFDEF PROC_REAL}                                    { REAL MODE DOS CODE }
ASSEMBLER;
ASM
   MOV AX, HeapEnd.Word[2];                           { Get heap end }
   SUB AX, HeapPtr.Word[2];
   SUB AX, LowMemSize;                                { Subtract size }
   SBB AX, AX;
   NEG AX;                                            { Return result }
END;
{$ELSE}                                               { DPMI/WIN/NT/OS2 CODE }
BEGIN
   LowMemory := False;                                { Preset false }
   If (SafetyPool = Nil) Then Begin                   { Not initialized }
    SafetyPool := MemAlloc(SafetyPoolSize);           { Allocate safety pool }
    If (SafetyPool = Nil) Then LowMemory := True;     { Return if low memory }
   End;
END;
{$ENDIF}

{---------------------------------------------------------------------------}
{  InitMemory -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB        }
{---------------------------------------------------------------------------}
PROCEDURE InitMemory;
{$IFDEF PROC_REAL} VAR HeapSize: Word; {$ENDIF}
BEGIN
   {$IFDEF PROC_REAL}                                 { REAL MODE DOS CODE }
   HeapError := @HeapNotify;                          { Point to error proc }
   If (BufHeapPtr = 0) Then Begin
     HeapSize := PtrRec(HeapEnd).Seg
       - PtrRec(HeapOrg).Seg;                         { Calculate size }
     If (HeapSize > MaxHeapSize) Then
       HeapSize := MaxHeapSize;                       { Restrict max size }
     BufHeapEnd := PtrRec(HeapEnd).Seg;               { Set heap end }
     PtrRec(HeapEnd).Seg := PtrRec(HeapOrg).Seg
      + HeapSize;                                     { Add heapsize }
     BufHeapPtr := PtrRec(HeapEnd).Seg;               { Set heap pointer }
   End;
   CachePtr := HeapEnd;                               { Cache starts at end }
   {$ELSE}                                            { DPMI/WIN/NT/OS2 CODE }
   {$IFNDEF PPC_FPC}
   HeapError := @HeapNotify;                          { Set heap error proc }
   {$ENDIF}
   SafetyPoolSize := LowMemSize * 16;                 { Fix safety pool size }
   LowMemory;                                         { Check for low memory }
   {$ENDIF}
END;

{---------------------------------------------------------------------------}
{  DoneMemory -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB        }
{---------------------------------------------------------------------------}
PROCEDURE DoneMemory;
BEGIN
   {$IFDEF PROC_REAL}                                 { REAl MODE DOS CODE }
   FreeCacheMem;                                      { Release cache memory }
   {$ELSE}                                            { DPMI/WIN/NT/OS2 }
   While FreeCache Do;                                { Free cache memory }
   FreeSafetyPool;                                    { Release safety pool }
   {$ENDIF}
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                           CACHE MEMORY ROUTINES                           }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  NewCache -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB          }
{---------------------------------------------------------------------------}
PROCEDURE NewCache (Var P: Pointer; Size: Word);
{$IFDEF PROC_REAL}                                    { REAL MODE DOS CODE }
ASSEMBLER;
ASM
   LES DI, P;                                         { Addres of var P }
   MOV AX, Size;
   ADD AX, (TYPE TCache)+15;                          { Add offset }
   MOV CL, 4;
   SHR AX, CL;
   MOV DX, CachePtr.Word[2];                          { Reteive cache ptr }
   SUB DX, AX;
   JC @@1;
   CMP DX, HeapPtr.Word[2];                           { Heap ptr end }
   JBE @@1;
   MOV CX, HeapEnd.Word[2];
   SUB CX, DX;
   CMP CX, MaxBufMem;                                 { Compare to maximum }
   JA @@1;
   MOV CachePtr.Word[2], DX;                          { Exchange ptr }
   PUSH DS;
   MOV DS, DX;
   XOR SI, SI;
   MOV DS:[SI].TCache.Size, AX;                       { Get cache size }
   MOV DS:[SI].TCache.Master.Word[0], DI;
   MOV DS:[SI].TCache.Master.Word[2], ES;             { Get master ptr }
   POP DS;
   MOV AX, OFFSET TCache.Data;
   JMP @@2;
@@1:
   XOR AX, AX;
   CWD;                                               { Make double word }
@@2:
   CLD;
   STOSW;                                             { Write low word }
   XCHG AX, DX;
   STOSW;                                             { Write high word }
END;
{$ELSE}                                               { DPMI/WIN/NT/OS2 CODE }
VAR Cache: PCache;
BEGIN
   Inc(Size, SizeOf(TCache));                         { Add cache size }
   If (MaxAvail >= Size) Then GetMem(Cache, Size)     { Allocate memory }
     Else Cache := Nil;                               { Not enough memory }
   If (Cache <> Nil) Then Begin                       { Cache is valid }
     If (CacheList = Nil) Then Cache^.Next := Cache
     Else Begin
       Cache^.Next := CacheList^.Next;                { Insert in list }
       CacheList^.Next := Cache;                      { Complete link }
     End;
     CacheList := Cache;                              { Hold cache ptr }
     Cache^.Size := Size;                             { Hold cache size }
     Cache^.Master := @P;                             { Hold master ptr }
{$ifdef fpc}
     Inc(Pointer(Cache), SizeOf(TCache));             { Set cache offset }
{$else fpc}
     Inc(PtrRec(Cache).Ofs, SizeOf(TCache));          { Set cache offset }
{$endif fpc}
   End;
   P := Cache;                                        { Return pointer }
END;
{$ENDIF}

{---------------------------------------------------------------------------}
{  DisposeCache -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB      }
{---------------------------------------------------------------------------}
PROCEDURE DisposeCache (P: Pointer);
{$IFDEF PROC_REAL}                                    { REAL MODE DOS CODE }
ASSEMBLER;
ASM
   MOV AX, CachePtr.Word[2];                          { Cache high word }
   XOR BX, BX;
   XOR CX, CX;
   MOV DX, P.Word[2];                                 { P high word }
@@1:
   MOV ES, AX;
   CMP AX, DX;                                        { Check for match }
   JE @@2;
   ADD AX, ES:[BX].TCache.Size;                       { Move to next cache }
   CMP AX, HeapEnd.Word[2];
   JE @@2;                                            { Are we at heap end }
   PUSH ES;
   INC CX;                                            { No so try next }
   JMP @@1;
@@2:
   PUSH ES;
   LES DI, ES:[BX].TCache.Master;                     { Pointe to master }
   XOR AX, AX;
   CLD;
   STOSW;                                             { Clear master ptr }
   STOSW;
   POP ES;
   MOV AX, ES:[BX].TCache.Size;                       { Next cache }
   JCXZ @@4;
@@3:
   POP DX;
   PUSH DS;
   PUSH CX;                                           { Hold registers }
   MOV DS, DX;
   ADD DX, AX;
   MOV ES, DX;
   MOV SI, DS:[BX].TCache.Size;                       { Get cache size }
   MOV CL, 3;
   SHL SI, CL;                                        { Multiply x8 }
   MOV CX, SI;
   SHL SI, 1;
   DEC SI;                                            { Adjust position }
   DEC SI;
   MOV DI, SI;
   STD;
   REP MOVSW;                                         { Move cache memory }
   LDS SI, ES:[BX].TCache.Master;
   MOV DS:[SI].Word[2], ES;                           { Store new master }
   POP CX;
   POP DS;                                            { Recover registers }
   LOOP @@3;
@@4:
   ADD CachePtr.Word[2], AX;                          { Add offset }
END;
{$ELSE}                                               { DPMI/WIN/NT/OS2 CODE }
VAR Cache, C: PCache;
BEGIN
{$ifdef fpc}
   Cache:=pointer(p)-SizeOf(TCache);
{$else fpc}
   PtrRec(Cache).Ofs := PtrRec(P).Ofs-SizeOf(TCache); { Previous cache }
   PtrRec(Cache).Seg := PtrRec(P).Seg;                { Segment }
{$endif fpc}
   C := CacheList;                                    { Start at 1st cache }
   While (C^.Next <> Cache) AND (C^.Next <> CacheList)
     Do C := C^.Next;                                 { Find previous }
   If (C^.Next = Cache) Then Begin                    { Cache found }
     If (C = Cache) Then CacheList := Nil Else Begin  { Only cache in list }
       If CacheList = Cache Then CacheList := C;      { First in list }
       C^.Next := Cache^.Next;                        { Remove from list }
     End;
     Cache^.Master^ := Nil;                           { Clear master }
     FreeMem(Cache, Cache^.Size);                     { Release memory }
   End;
END;
{$ENDIF}

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                          BUFFER MEMORY ROUTINES                           }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  GetBufferSize -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB     }
{---------------------------------------------------------------------------}
FUNCTION GetBufferSize (P: Pointer): Word;
BEGIN
   {$IFDEF PROC_REAL}                                 { DOS CODE }
   Dec(PtrRec(P).Seg);                                { Segment prior }
   GetBufferSize := PBuffer(P)^.Size;                 { Size of this buffer }
   {$ELSE}                                            { DPMI/WIN/NT/OS2 CODE }
   If (P <> Nil) Then                                 { Check pointer }
     Begin
{$ifdef fpc}
       Dec(Pointer(P),SizeOf(TBuffer));                 { Correct to buffer }
{$else fpc}
       Dec(PtrRec(P).Ofs,SizeOf(TBuffer));              { Correct to buffer }
{$endif fpc}
       GetBufferSize := PBuffer(P)^.Size;               { Return buffer size }
     End
   Else
     GetBufferSize := 0;                       { Invalid pointer }
   {$ENDIF}
END;

{---------------------------------------------------------------------------}
{  SetBufferSize -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB     }
{---------------------------------------------------------------------------}
FUNCTION SetBufferSize (var P: Pointer; Size: Word): Boolean;
VAR NewSize: Word;
BEGIN
   SetBufferSize := False;                            { Preset failure }
   {$IFDEF PROC_REAL}                                 { REAL MODE DOS CODE }
   Dec(PtrRec(P).Seg);                                { Prior segment }
   NewSize := (Size + 15) SHR 4 + 1;                  { Paragraph size }
   If (BufHeapPtr+NewSize-GetBufSize(P)<=BufHeapEnd)  { Check enough heap }
   Then Begin
     SetBufSize(P, NewSize);                          { Set the buffer size }
     PBuffer(P)^.Size := Size;                        { Set the size }
     SetBufferSize := True;                           { Return success }
   End;
   {$ELSE}                                            { DPMI/WIN/NT/OS2 CODE }
 {$ifdef fpc}
   Dec(Pointer(P),SizeOf(TBuffer));                 { Correct to buffer }
   SetBufferSize := ReAllocMem(P, Size + SizeOf(TBuffer)) <> nil;
   if SetBufferSize then
      TBuffer(P^).Size := Size + SizeOf(TBuffer);
   Inc(Pointer(P), SizeOf(TBuffer));                 { Correct to buffer }
{$endif fpc}
   {$ENDIF}
END;

{---------------------------------------------------------------------------}
{  DisposeBuffer -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB     }
{---------------------------------------------------------------------------}
PROCEDURE DisposeBuffer (P: Pointer);
{$IFNDEF PROC_REAL} VAR Buffer,PrevBuf: PBuffer; {$ENDIF}
BEGIN
   If (P <> Nil) Then Begin
     {$IFDEF PROC_REAL}                               { REAL MODE DOS CODE }
     Dec(PtrRec(P).Seg);                              { Prior segement }
     SetBufSize(P, 0);                                { Release memory }
     {$ELSE}                                          { DPMI/WIN/NT/OS2 CODE }
{$ifdef fpc}
     Dec(Pointer(P), SizeOf(TBuffer));                { Actual buffer pointer }
{$else fpc}
     Dec(PtrRec(P).Ofs, SizeOf(TBuffer));             { Actual buffer pointer }
{$endif fpc}
     Buffer := BufferList;                            { Start on first }
     PrevBuf := Nil;                                  { Preset prevbuf to nil }
     While (Buffer <> Nil) AND (P <> Buffer) Do Begin { Search for buffer }
       PrevBuf := Buffer;                             { Hold last buffer }
       Buffer := Buffer^.Next;                        { Move to next buffer }
     End;
     If (Buffer <> Nil) Then Begin                    { Buffer was found }
       If (PrevBuf = Nil) Then                        { We were first on list }
         BufferList := Buffer^.Next Else              { Set bufferlist entry }
         PrevBuf^.Next := Buffer^.Next;               { Remove us from chain }
       FreeMem(Buffer, Buffer^.Size);                 { Release buffer }
     End;
     {$ENDIF}
   End;
END;

{---------------------------------------------------------------------------}
{  NewBuffer -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB         }
{---------------------------------------------------------------------------}
PROCEDURE NewBuffer (Var P: Pointer; Size: Word);
VAR BufSize: Word; Buffer: PBuffer;
BEGIN
   {$IFDEF PROC_REAL}                                 { REAL MODE DOS CODE }
   BufSize := (Size + 15) SHR 4 + 1;                  { Paragraphs to alloc }
   If (BufHeapPtr+BufSize > BufHeapEnd) Then P := Nil { Exceeeds heap }
   Else Begin
     Buffer := Ptr(BufHeapPtr, 0);                    { Current position }
     Buffer^.Size := Size;                            { Set size }
     Buffer^.Master := @PtrRec(P).Seg;                { Set master }
     P := Ptr(BufHeapPtr + 1, 0);                     { Position ptr }
     Inc(BufHeapPtr, BufSize);                        { Allow space on heap }
   End;
   {$ELSE}                                            { DPMI/WIN/NT/OS2 CODE }
   BufSize := Size + SizeOf(TBuffer);                 { Size to allocate }
   Buffer := MemAlloc(BufSize);                       { Allocate the memory }
   If (Buffer <> Nil) Then Begin
     Buffer^.Next := BufferList;                      { First part of chain }
     BufferList := Buffer;                            { Complete the chain }
     Buffer^.Size := BufSize;                         { Hold the buffer size }
{$ifdef fpc}
     Inc(Pointer(Buffer), SizeOf(TBuffer));           { Buffer to data area }
{$else fpc}
     Inc(PtrRec(Buffer).Ofs, SizeOf(TBuffer));        { Buffer to data area }
{$endif fpc}
   End;
   P := Buffer;                                       { Return the buffer ptr }
   {$ENDIF}
END;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}
{                        DOS MEMORY CONTROL ROUTINES                        }
{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{---------------------------------------------------------------------------}
{  InitDosMem -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB        }
{---------------------------------------------------------------------------}
PROCEDURE InitDosMem;
BEGIN
   {$IFDEF PROC_REAL}                                 { REAl MODE DOS CODE }
   SetMemTop(Ptr(BufHeapEnd, 0));                     { Move heap to empty }
   {$ENDIF}
END;

{---------------------------------------------------------------------------}
{  DoneDosMem -> Platforms DOS/DPMI/WIN/NT/OS2 - Updated 01Oct99 LdB        }
{---------------------------------------------------------------------------}
PROCEDURE DoneDosMem;
{$IFDEF PROC_REAL} VAR MemTop: Pointer; {$ENDIF}
BEGIN
   {$IFDEF PROC_REAL}                                 { REAL MODE DOS CODE }
   MemTop := Ptr(BufHeapPtr, 0);                      { Top of memory }
   If (BufHeapPtr = PtrRec(HeapEnd).Seg) Then Begin   { Is memory empty }
     FreeCacheMem;                                    { Release memory }
     MemTop := HeapPtr;                               { Set pointer }
   End;
   SetMemTop(MemTop);                                 { Release memory }
   {$ENDIF}
END;

END.
