{$optimize 7}
{---------------------------------------------------------------}
{                                                               }
{  Memory Manager                                               }
{                                                               }
{  This memory manager provides a stack-based memory allocation }
{  and deallocation mechanism to allow compact symbol tables    }
{  that can be disposed of with a single call after a function  }
{  has been compiled.  Two separate stacks, or pools, are       }
{  available.  The local pool is typically disposed of when     }
{  the compilation of a function is complete.  It is used for   }
{  local memory allocations such as local strings and symbols.  }
{  The global pool is used for global values like macro         }
{  definitions and global symbols.                              }
{                                                               }
{  External Variables:                                          }
{       localID - userID for the local pool                     }
{       globalID - userID for the global pool                   }
{                                                               }
{  External Subroutines:                                        }
{       DisposeLocalPool - dump the local memory pool           }
{       Calloc - clear and allocate memory                      }
{       GCalloc - allocate & clear memory from the global pool  }
{       GInit - initialize a global pool                        }
{       GMalloc - allocate memory from the global pool          }
{       GLongMalloc - allocate global memory                    }
{       LInit - initialize a local pool                         }
{       LMalloc - allocate memory from the local pool           }
{       Malloc - allocate memory                                }
{       MMQuit - Dispose of memory allocated with private user  }
{               IDs                                             }
{                                                               }
{---------------------------------------------------------------}

unit MM;

{$LibPrefix '0/obj/'}

interface

uses CCommon;

{$segment 'CC'}

var
   localID,globalID: integer;           {user ID's for the local & global pools}

{---------------------------------------------------------------}

function Calloc (bytes: integer): ptr; extern;

{ Allocate memory from a pool and set it to 0.                  }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }
{                                                               }
{ Globals:                                                      }
{       useGlobalPool - should the memory come from the global  }
{               (or local) pool                                 }


function GCalloc (bytes: integer): ptr; extern;

{ Allocate and clear memory from the global pool.               }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }


procedure GInit;

{ Initialize a global pool                                      }


function GLongMalloc (bytes: longint): ptr;

{ Allocate a potentially large amount of global memory.         }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }


function GMalloc (bytes: integer): ptr;

{ Allocate memory from the global pool.                         }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }


procedure LInit;

{ Initialize a local pool                                       }


function LMalloc (bytes: integer): ptr;

{ Allocate memory from the local pool.                          }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }


function Malloc (bytes: integer): ptr; extern;

{ Allocate memory from a pool.                                  }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }
{                                                               }
{ Globals:                                                      }
{       useGlobalPool - should the memory come from the global  }
{               (or local) pool                                 }


procedure MMQuit;

{ Dispose of memory allocated with private user IDs             }

{---------------------------------------------------------------}

implementation

const
   poolSize      = 4096;                {size of a memory pool}

var
   globalPtr: ptr;                      {pointer to the next free global byte}
   globalSize: integer;                 {bytes remaining in the global pool}
   localPtr: ptr;                       {pointer to the next free local byte}
   localSize: integer;                  {bytes remaining in the local pool}

{---------------------------------------------------------------}

                                        {GS memory manager}
                                        {-----------------}

 procedure DisposeAll (userID: integer); tool($02, $11);
 
 function NewHandle (blockSize: longint; userID, memAttributes: integer;
                     memLocation: ptr): handle; tool($02, $09);

{---------------------------------------------------------------}

procedure GInit;

{ Initialize a global pool                                      }

var
   myhandle: handle;                    {for dereferencing the block}

begin {GInit}
globalID := UserID | $0200;             {set the global user ID}
DisposeAll(globalID);                   {dump any old pool areas}
globalSize := poolSize;                 {allocate a new pool}
myhandle := NewHandle(poolSize, globalID, $C010, nil);
if ToolError <> 0 then TermError(5);
globalPtr := myhandle^;
end; {GInit}


function GMalloc {bytes: integer): ptr};

{ Allocate memory from the global pool.                         }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }

var
   myhandle: handle;                    {for dereferencing the block}

begin {GMalloc}
if bytes > globalSize then begin        {allocate a new pool, if needed}
   if bytes > poolSize then
      globalSize := bytes
   else
      globalSize := poolSize;
   myhandle := NewHandle(globalSize, globalID, $C010, nil);
   if ToolError <> 0 then TermError(5);
   globalPtr := myhandle^;
   end; {if}
GMalloc := globalPtr;                   {allocate memory from the pool}
globalSize := globalSize - bytes;
globalPtr := pointer(ord4(globalPtr) + bytes);
end; {GMalloc}


function GLongMalloc {bytes: longint): ptr};

{ Allocate a potentially large amount of global memory.         }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }

var
   myhandle: handle;                    {for dereferencing the block}

begin {GLongMalloc}
myhandle := NewHandle(bytes, globalID, $C000, nil);
if ToolError <> 0 then TermError(5);
GLongMalloc := myhandle^;
end; {GLongMalloc}


procedure LInit;

{ Initialize a local pool                                       }

var
   myhandle: handle;                    {for dereferencing the block}

begin {LInit}
localID := UserID | $0400;              {set the local user ID}
DisposeAll(localID);                    {dump any old pool areas}
localSize := poolSize;                  {allocate a new pool}
myhandle := NewHandle(poolSize, localID, $C010, nil);
if ToolError <> 0 then TermError(5);
localPtr := myhandle^;
end; {LInit}


function LMalloc {bytes: integer): ptr};

{ Allocate memory from the local pool.                          }
{                                                               }
{ Parameters:                                                   }
{       bytes - number of bytes to allocate                     }
{       ptr - points to the first byte of the allocated memory  }

var
   myhandle: handle;                    {for dereferencing the block}

begin {LMalloc}
if bytes > localSize then begin         {allocate a new pool, if needed}
   if bytes > poolSize then
      localSize := bytes
   else
      localSize := poolSize;
   myhandle := NewHandle(localSize, localID, $C010, nil);
   if ToolError <> 0 then TermError(5);
   localPtr := myhandle^;
   end; {if}
LMalloc := localPtr;                    {allocate memory from the pool}
localSize := localSize - bytes;
localPtr := pointer(ord4(localPtr) + bytes);
end; {LMalloc}


procedure MMQuit;

{ Dispose of memory allocated with private user IDs             }

begin {MMQuit}
DisposeAll(globalID);
DisposeAll(localID);
globalID := 0;                          {do not use old IDs after restart}
localID := 0;
end; {MMQuit}

end.

{$append 'mm.asm'}
