home *** CD-ROM | disk | FTP | other *** search
/ Collection of Hack-Phreak Scene Programs / cleanhpvac.zip / cleanhpvac / ASMCODE.ZIP / GUSHEAP.PAS < prev    next >
Pascal/Delphi Source File  |  1994-11-02  |  17KB  |  501 lines

  1. { ────────────────────────────────────────────────────────────────────────
  2.  
  3.   This code is Copyright (c) 1994 by Jonathan E. Wright and AmoebaSoft.
  4.  
  5.   To communicate with the author, send internet mail to: NELNO@DELPHI.COM
  6.  
  7.   About this code:
  8.     This code was converted on the fly from my EMM heap manager and
  9.     bacically adapted to manage GUS memory.  It's probably not too
  10.     efficient and my contain a bug or two, but I haven't found it yet.
  11.  
  12.     If you use this code in any of your programs, or as a basis for anything
  13.     else you may write, please give credit to Nelno the Amoeba.  A postcard
  14.     from your country or town would also be nice.  Send it to:
  15.  
  16.     Nelno
  17.     58 1/2 Woodland Rd.
  18.     Asheville, NC 28804-3823
  19.     USA
  20.  
  21.   ──────────────────────────────────────────────────────────────────────── }
  22.  
  23. Unit GUSHeap;
  24.  
  25. Interface
  26.  
  27. USES
  28.   Types;
  29.  
  30. CONST
  31.   GUS_BankSize = 262144;
  32.  
  33.   GUS_ErrorCode : INTEGER = 0;
  34.   GUS_MemAvail  : LONGINT = 0;
  35.  
  36. TYPE
  37.   GUS_Ptr = RECORD
  38.              GPtr      : LONGINT;  { location from start of GUS memory }
  39.              OfsPtr    : LONGINT;  { offset from start of bank }
  40.              Bank      : BYTE;
  41.              BlockSize : LONGINT;
  42.            END;
  43.  
  44. PROCEDURE GUS_GetMem (VAR GUS_Block : GUS_Ptr; Size : LONGINT);
  45. PROCEDURE GUS_FreeMem (GUS_Block : GUS_Ptr);
  46. PROCEDURE GUS_InitHeap (MemSize : WORD);
  47. PROCEDURE GUS_DestroyHeap;
  48. FUNCTION  GUS_MaxAvail : LONGINT;
  49.  
  50. Implementation
  51.  
  52. CONST
  53.   MaxFreeBlocks = 1024;
  54.  
  55.   GUS_HeapInitialized : BOOLEAN = FALSE;
  56.  
  57. TYPE
  58.   FreeListPtr = ^FreeListArray;
  59.  
  60.   FreeListType = RECORD
  61.                    Bank      : BYTE;
  62.                    GPtr      : LONGINT; { Block location from start of GUS bank }
  63.                    BlockSize : LONGINT;
  64.                  END;
  65.  
  66.   FreeListArray = ARRAY [1..MaxFreeBlocks] OF FreeListType;
  67.  
  68. VAR
  69.   BankPtr      : WORD; (* current bank where next allocation is being done  *)
  70.   OffsPtr      : LONGINT; (* Offset in current page where next allocation will *)
  71.                      (* be performed                                      *)
  72.   FreeBanks    : WORD;
  73.   TotalBanks   : WORD;
  74.  
  75.   GUS_FreeList : FreeListPtr;
  76.   FreeBlocks   : WORD;
  77.  
  78.   SavedExit    : POINTER;
  79.  
  80. { ╔═══════════════════════════════════════════════════════════════════════╗
  81.   ║                                                                       ║
  82.   ║ PROCEDURE NewExit; FAR;                                               ║
  83.   ║                                                                       ║
  84.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  85.  
  86. PROCEDURE NewExit; FAR;
  87.  
  88. BEGIN
  89.   ExitProc := SavedExit;
  90.  
  91.   IF DebugKeys THEN Print ('Deallocated GUS Heap.', $0F);
  92.   GUS_DestroyHeap;
  93. END;
  94.  
  95. { ╔═══════════════════════════════════════════════════════════════════════╗
  96.   ║                                                                       ║
  97.   ║ Sets all entries in the freelist to 0                                 ║
  98.   ║                                                                       ║
  99.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  100.  
  101.   PROCEDURE GUS_InitFreeList;
  102.  
  103.   VAR
  104.     Count : INTEGER;
  105.  
  106.   BEGIN
  107.     FOR Count := 1 to MaxFreeBlocks DO
  108.     BEGIN
  109.       GUS_FreeList^ [Count].Bank := 0;
  110.       GUS_FreeList^ [Count].GPtr := 0;
  111.       GUS_FreeList^ [Count].BlockSize := 0;
  112.     END;
  113.   END;
  114.  
  115. { ╔═══════════════════════════════════════════════════════════════════════╗
  116.   ║                                                                       ║
  117.   ║ Searches the GUS_FreeList array for any blocks that are greater than  ║
  118.   ║ or equal to RequiredSize.  Returns the element of GUS_FreeList where  ║
  119.   ║ the block is described, or returns 0 if no block was found            ║
  120.   ║                                                                       ║
  121.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  122.  
  123.   FUNCTION  GUS_SearchFreeList (RequiredSize : LONGINT) : WORD;
  124.  
  125.   VAR
  126.     Count   : INTEGER;
  127.     FoundAt : WORD;
  128.  
  129.   BEGIN
  130.     FoundAt := 0;
  131.     Count := 0;
  132.  
  133.     IF FreeBlocks > 0 THEN
  134.     BEGIN
  135.       REPEAT
  136.         INC (Count);
  137.  
  138.         IF GUS_FreeList^ [Count].BlockSize >= RequiredSize THEN
  139.           FoundAt := Count;
  140.       UNTIL (Count >= FreeBlocks) or (FoundAt > 0);
  141.     END;
  142.  
  143.     GUS_SearchFreeList := FoundAt;
  144.   END;
  145.  
  146. { ╔═══════════════════════════════════════════════════════════════════════╗
  147.   ║                                                                       ║
  148.   ║ Adjusts freelist entry n to reflect usage of block of size Size.      ║
  149.   ║ If entire block is used, entry is removed from free list, all entries ║
  150.   ║ above it are moved down one to fill gap, and FreeBlocks is decremented║
  151.   ║                                                                       ║
  152.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  153.  
  154.   PROCEDURE GUS_AdjustFreeList (n : WORD; Size : LONGINT);
  155.  
  156.   VAR
  157.     Count : INTEGER;
  158.  
  159.   BEGIN
  160.     IF (Size = GUS_FreeList^ [n].BlockSize) AND (Size <> GUS_BankSize) THEN
  161.     BEGIN
  162.       IF FreeBlocks > 1 THEN
  163.       BEGIN
  164.         FOR Count := n + 1 to FreeBlocks DO
  165.           GUS_FreeList^ [Count - 1] := GUS_FreeList^ [Count];
  166.       END;
  167.  
  168.       GUS_FreeList^ [FreeBlocks].BlockSize := 0;
  169.       GUS_FreeList^ [FreeBlocks].GPtr := 0;
  170.       GUS_FreeList^ [FreeBlocks].Bank := 0;
  171.  
  172.       DEC (FreeBlocks);
  173.     END
  174.     ELSE
  175.     BEGIN
  176.       GUS_FreeList^ [n].GPtr := GUS_FreeList^ [n].GPtr + Size;
  177.       GUS_FreeList^ [n].BlockSize := GUS_FreeList^ [n].BlockSize - Size;
  178.     END;
  179.   END;
  180.  
  181. { ╔═══════════════════════════════════════════════════════════════════════╗
  182.   ║                                                                       ║
  183.   ║ searches the freelist and combines free spaces contiguous to free     ║
  184.   ║ block n                                                               ║
  185.   ║                                                                       ║
  186.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  187.  
  188.   PROCEDURE GUS_CombineFreeList (n : WORD);
  189.  
  190.   VAR
  191.     I : INTEGER;
  192.  
  193.   BEGIN
  194.     I := 1;
  195.  
  196.     REPEAT
  197.       IF (GUS_FreeList^ [I].Bank = GUS_FreeList^ [n].Bank) AND (n <> I) THEN
  198.       BEGIN
  199.         IF GUS_FreeList^ [I].GPtr + GUS_FreeList^ [I].BlockSize = GUS_FreeList^ [n].GPtr THEN
  200.         BEGIN
  201.           (* Make free list entry's size bigger to encompass the new *)
  202.           (* free block at the end of it                             *)
  203.  
  204.           GUS_FreeList^ [n].BlockSize := GUS_FreeList^ [n].BlockSize + GUS_FreeList^ [I].BlockSize;
  205.           GUS_FreeList^ [n].GPtr := GUS_FreeList^ [I].GPtr;
  206.  
  207.           Writeln ('I = ', I);
  208.           GUS_AdjustFreeList (I, GUS_FreeList^ [I].BlockSize);
  209.         END
  210.         ELSE IF GUS_FreeList^ [I].GPtr = GUS_FreeList^ [n].GPtr + GUS_FreeList^ [n].BlockSize THEN
  211.         BEGIN
  212.           (* Make free list entry's offset equal to the new offset and *)
  213.           (* increase it's size to contain both free blocks            *)
  214.  
  215.           GUS_FreeList^ [n].BlockSize := GUS_FreeList^ [I].BlockSize + GUS_FreeList^ [n].BlockSize;
  216.  
  217.           GUS_AdjustFreeList (I, GUS_FreeList^ [I].BlockSize);
  218.  
  219.         END;
  220.       END;
  221.  
  222.       INC (I);
  223.     UNTIL (I > FreeBlocks);
  224.  
  225.   END;
  226.  
  227.  
  228. { ╔═══════════════════════════════════════════════════════════════════════╗
  229.   ║                                                                       ║
  230.   ║ Adds a free block to the end of the free list, as long as that block  ║
  231.   ║ doesn't start at the end of another free list entry, in which case    ║
  232.   ║ the first free list entry's size is enlarged by the size of the new   ║
  233.   ║ free block.                                                           ║
  234.   ║                                                                       ║
  235.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  236.  
  237.   PROCEDURE GUS_AddToFreeList (Page : WORD; Offset, Size : LONGINT);
  238.  
  239.   VAR
  240.     I : INTEGER;
  241.     ListUpdated : BOOLEAN;
  242.  
  243.   BEGIN
  244.     IF FreeBlocks < MaxFreeBlocks THEN
  245.     BEGIN
  246.       I := 1;
  247.       ListUpdated := FALSE;
  248.  
  249.       REPEAT
  250.         IF GUS_FreeList^ [I].Bank = Page THEN
  251.         BEGIN
  252.           IF GUS_FreeList^ [I].GPtr + GUS_FreeList^ [I].BlockSize = Offset THEN
  253.           BEGIN
  254.             (* Make free list entry's size bigger to encompass the new *)
  255.             (* free block at the end of it                             *)
  256.  
  257.             GUS_FreeList^ [I].BlockSize := GUS_FreeList^ [I].BlockSize + Size;
  258.             ListUpdated := TRUE;
  259.  
  260.             GUS_CombineFreeList (I);
  261.           END
  262.           ELSE IF GUS_FreeList^ [I].GPtr = Offset + Size THEN
  263.           BEGIN
  264.             (* Make free list entry's offset equal to the new offset and *)
  265.             (* increase it's size to contain both free blocks            *)
  266.  
  267.             GUS_FreeList^ [I].BlockSize := GUS_FreeList^ [I].BlockSize + Size;
  268.             GUS_FreeList^ [I].GPtr := Offset;
  269.             ListUpdated := TRUE;
  270.  
  271.             GUS_CombineFreeList (I);
  272.           END;
  273.         END;
  274.  
  275.         INC (I);
  276.       UNTIL (I > FreeBlocks) OR (ListUpdated);
  277.  
  278.       IF NOT (ListUpdated) THEN
  279.       BEGIN
  280.         INC (FreeBlocks);
  281.  
  282.         GUS_FreeList^ [FreeBlocks].Bank := Page;
  283.         GUS_FreeList^ [FreeBlocks].GPtr := Offset;
  284.         GUS_FreeList^ [FreeBlocks].BlockSize := Size;
  285.       END;
  286.     END
  287.     ELSE ErrorHandler (251, 24);
  288.   END;
  289.  
  290. { ╔═══════════════════════════════════════════════════════════════════════╗
  291.   ║                                                                       ║
  292.   ║ Allocates a block of free memory from the current GUS_ handle         ║
  293.   ║                                                                       ║
  294.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  295.  
  296.   PROCEDURE GUS_GetMem (VAR GUS_Block : GUS_Ptr; Size : LONGINT);
  297.  
  298.   VAR
  299.     PageToAllocate  : WORD;
  300.     OffsToAllocate  : WORD;
  301.     FreeListElement : WORD;
  302.  
  303.   BEGIN
  304.     GUS_Block.BlockSize := Size;
  305.  
  306.     IF GUS_Block.BlockSize <= GUS_BankSize THEN
  307.     BEGIN
  308.       { search the free list for a block that is >= requested size }
  309.  
  310.       FreeListElement := GUS_SearchFreeList (GUS_Block.BlockSize);
  311.  
  312.       IF FreeListElement > 0 THEN
  313.       BEGIN
  314.         GUS_Block.Bank := GUS_FreeList^ [FreeListElement].Bank;
  315.         GUS_Block.OfsPtr := GUS_FreeList^ [FreeListElement].GPtr;
  316.         GUS_Block.GPtr := BankPtr * GUS_BankSize + GUS_Block.OfsPtr;
  317.  
  318.         GUS_AdjustFreeList (FreeListElement, GUS_Block.BlockSize);
  319.  
  320.         GUS_MemAvail := GUS_MemAvail - Size;
  321.       END
  322.       ELSE
  323.       BEGIN
  324.         { check if block allocation will extend past current page. if
  325.           so: add the unusable area to the free list, increment to
  326.           next page, and set OffsPtr to 0                             }
  327.  
  328.         IF OffsPtr + GUS_Block.BlockSize > GUS_BankSize THEN
  329.         BEGIN
  330.           GUS_AddToFreeList (BankPtr, OffsPtr, GUS_BankSize - OffsPtr);
  331. {          GUS_MemAvail := GUS_MemAvail + GUS_BankSize - OffsPtr;}
  332.  
  333.           INC (BankPtr);
  334.           OffsPtr := 0;
  335.  
  336.           { check for heap overflow }
  337.  
  338.           IF BankPtr >= TotalBanks THEN ErrorHandler (251, 18);
  339.         END;
  340.  
  341.         { if no overflow, then set GUS_Block's values to }
  342.  
  343.         IF GUS_ErrorCode = 0 THEN
  344.         BEGIN
  345.           GUS_Block.Bank:= BankPtr;
  346.           GUS_Block.OfsPtr := OffsPtr;
  347.           GUS_Block.GPtr := BankPtr * GUS_BankSize + GUS_Block.OfsPtr;
  348.           GUS_MemAvail := GUS_MemAvail - Size;
  349.  
  350.           INC (OffsPtr, GUS_Block.BlockSize);
  351.           IF OffsPtr >= GUS_BankSize THEN
  352.           BEGIN
  353.             INC (BankPtr);
  354.             OffsPtr := 0;
  355.  
  356.             IF BankPtr >= TotalBanks THEN ErrorHandler (251, 24);
  357.           END;
  358.         END
  359.         ELSE
  360.           ErrorHandler (251, GUS_ErrorCode);
  361.       END;
  362.     END
  363.     ELSE ErrorHandler (251, 23);
  364.   END;
  365.  
  366. { ╔═══════════════════════════════════════════════════════════════════════╗
  367.   ║                                                                       ║
  368.   ║ Frees a previously allocated block and places its location in the     ║
  369.   ║ free list if it is not at the top of the heap, in which case the      ║
  370.   ║ top of heap pointers (BankPtr and OffsPtr) are adjusted down.         ║
  371.   ║                                                                       ║
  372.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  373.  
  374.   PROCEDURE GUS_FreeMem (GUS_Block : GUS_Ptr);
  375.  
  376.   BEGIN
  377.     IF ((BankPtr = GUS_Block.Bank) AND (GUS_Block.OfsPtr + GUS_Block.BlockSize = OffsPtr)) THEN
  378.     BEGIN
  379.       { block was the last one allocated from current page }
  380.       OffsPtr := GUS_Block.OfsPtr;
  381.       GUS_MemAvail := GUS_MemAvail + GUS_Block.BlockSize;
  382.     END
  383.     ELSE IF (BankPtr = GUS_Block.Bank + 1) AND (GUS_Block.OfsPtr + GUS_Block.BlockSize = GUS_BankSize) THEN
  384.     BEGIN
  385.       OffsPtr := GUS_Block.OfsPtr;
  386.       BankPtr := GUS_Block.Bank;
  387.       GUS_MemAvail := GUS_MemAvail + GUS_Block.BlockSize;
  388.     END
  389.     ELSE
  390.     BEGIN
  391.       IF GUS_Block.BlockSize = 0 THEN
  392.         ErrorHandler (251, 252)
  393.       ELSE
  394.       BEGIN
  395.         GUS_AddToFreeList (GUS_Block.Bank, GUS_Block.OfsPtr, GUS_Block.BlockSize);
  396.         GUS_MemAvail := GUS_MemAvail + GUS_Block.BlockSize;
  397.       END;
  398.     END;
  399.   END;
  400.  
  401. { ╔═══════════════════════════════════════════════════════════════════════╗
  402.   ║                                                                       ║
  403.   ║ Initializes GUS heap variables                                        ║
  404.   ║                                                                       ║
  405.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  406.  
  407.   PROCEDURE GUS_InitHeap (MemSize : WORD);
  408.  
  409.   VAR
  410.     MemAllocated : LONGINT;
  411.  
  412.   BEGIN
  413.     FreeBlocks := 0;
  414.     BankPtr    := 0;
  415.     OffsPtr    := 0;
  416.     GUS_ErrorCode := 0;
  417.     GUS_MemAvail := LONGINT (MemSize) * 1024;
  418.  
  419.     TotalBanks := MemSize DIV 256;
  420.     FreeBanks := TotalBanks;
  421.  
  422.     PRINT (ST (MemSize) + 'K UltraSound memory available.', 15);
  423.     NEW (GUS_FreeList);
  424.     GUS_HeapInitialized := TRUE;
  425.   END;
  426.  
  427. { ╔═══════════════════════════════════════════════════════════════════════╗
  428.   ║                                                                       ║
  429.   ║ Disables all GUS_ heap functions and returns all Turbo heap memory    ║
  430.   ║                                                                       ║
  431.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  432.  
  433.   PROCEDURE GUS_DestroyHeap;
  434.  
  435.   BEGIN
  436.     IF GUS_HeapInitialized = TRUE THEN
  437.     BEGIN
  438.       DISPOSE (GUS_FreeList);
  439.       GUS_HeapInitialized := FALSE;
  440.     END;
  441.   END;
  442.  
  443. { ╔═══════════════════════════════════════════════════════════════════════╗
  444.   ║                                                                       ║
  445.   ║                                                                       ║
  446.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  447.  
  448.   FUNCTION GUS_GetError : BYTE;
  449.  
  450.   BEGIN
  451.     GUS_GetError := GUS_ErrorCode;
  452.   END;
  453.  
  454. { ╔═══════════════════════════════════════════════════════════════════════╗
  455.   ║                                                                       ║
  456.   ║ Returns the amount of Expanded memory left in the heap                ║
  457.   ║                                                                       ║
  458.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  459.  
  460.   FUNCTION  GUS_MaxAvail : LONGINT;
  461.  
  462.   VAR
  463.     Count  : INTEGER;
  464.     Memory : LONGINT;
  465.  
  466.   BEGIN
  467.     IF BankPtr < 4 THEN
  468.       GUS_MaxAvail := GUS_BankSize
  469.     ELSE
  470.     BEGIN
  471.       Memory := 0;
  472.  
  473.       FOR Count := 1 to FreeBlocks DO
  474.         IF GUS_FreeList^ [Count].BlockSize > Memory THEN
  475.           Memory := GUS_FreeList^ [Count].BlockSize;
  476.  
  477.       GUS_MaxAvail := Memory;
  478.     END;
  479.   END;
  480.  
  481. { ╔═══════════════════════════════════════════════════════════════════════╗
  482.   ║                                                                       ║
  483.   ║ Returns the GUS_ heap to its original state, freeing all memory       ║
  484.   ║ Use with caution!                                                     ║
  485.   ║                                                                       ║
  486.   ╚═══════════════════════════════════════════════════════════════════════╝ }
  487.  
  488. PROCEDURE GUS_ReleaseHeap;
  489.  
  490. BEGIN
  491.   GUS_InitFreeList;
  492.  
  493.   BankPtr := 0;
  494.   OffsPtr := 0;
  495.   FreeBlocks := 0;
  496. END;
  497.  
  498. BEGIN
  499.   SavedExit := ExitProc;
  500.   ExitProc := @NewExit;
  501. END.