1 /* @(#) pf_mem.c 98/01/26 1.3 */
2 /***************************************************************
3 ** Memory allocator for systems that don't have real one.
4 ** This might be useful when bringing up a new computer with no OS.
6 ** For PForth based on 'C'
9 ** Copyright 1994 3DO, Phil Burk, Larry Polansky, Devid Rosenboom
11 ** The pForth software code is dedicated to the public domain,
12 ** and any third party may reproduce, distribute and modify
13 ** the pForth software code or any derivative works thereof
14 ** without any compensation or license. The pForth software
15 ** code is provided on an "as is" basis without any warranty
16 ** of any kind, including, without limitation, the implied
17 ** warranties of merchantability and fitness for a particular
18 ** purpose and their equivalents under the laws of any jurisdiction.
20 ****************************************************************
22 ***************************************************************/
29 static char *gMemPoolPtr;
30 static uint32 gMemPoolSize;
32 /* CUSTOM: Make the memory pool bigger if you want. */
33 #ifndef PF_MEM_POOL_SIZE
34 #define PF_MEM_POOL_SIZE (0x100000)
37 #define PF_MEM_BLOCK_SIZE (16)
39 #ifndef PF_MALLOC_ADDRESS
40 static char MemoryPool[PF_MEM_POOL_SIZE];
41 #define PF_MALLOC_ADDRESS MemoryPool
44 /**********************************************************
45 ** Doubly Linked List Tools
46 **********************************************************/
48 typedef struct DoublyLinkedListNode
50 struct DoublyLinkedListNode *dlln_Next;
51 struct DoublyLinkedListNode *dlln_Previous;
52 } DoublyLinkedListNode;
54 typedef struct DoublyLinkedList
56 struct DoublyLinkedListNode *dll_First;
57 struct DoublyLinkedListNode *dll_Null;
58 struct DoublyLinkedListNode *dll_Last;
61 #define dllPreviousNode(n) ((n)->dlln_Previous)
62 #define dllNextNode(n) ((n)->dlln_Next)
64 void dllSetupList( DoublyLinkedList *dll )
66 dll->dll_First = (DoublyLinkedListNode *) &(dll->dll_Null);
67 dll->dll_Null = (DoublyLinkedListNode *) NULL;
68 dll->dll_Last = (DoublyLinkedListNode *) &(dll->dll_First);
71 void dllLinkNodes( DoublyLinkedListNode *Node0, DoublyLinkedListNode *Node1 )
73 Node0->dlln_Next = Node1;
74 Node1->dlln_Previous = Node0;
77 void dllInsertNodeBefore( DoublyLinkedListNode *NewNodePtr,
78 DoublyLinkedListNode *NodeInListPtr )
80 DoublyLinkedListNode *NodePreviousPtr = dllPreviousNode( NodeInListPtr );
81 dllLinkNodes( NodePreviousPtr, NewNodePtr );
82 dllLinkNodes( NewNodePtr, NodeInListPtr );
85 void dllInsertNodeAfter( DoublyLinkedListNode *NewNodePtr,
86 DoublyLinkedListNode *NodeInListPtr )
88 DoublyLinkedListNode *NodeNextPtr = dllNextNode( NodeInListPtr );
89 dllLinkNodes( NodeInListPtr, NewNodePtr );
90 dllLinkNodes( NewNodePtr, NodeNextPtr );
93 void dllDumpNode( DoublyLinkedListNode *NodePtr )
96 DBUG((" 0x%x -> (0x%x) -> 0x%x\n",
97 dllPreviousNode( NodePtr ), NodePtr,
98 dllNextNode( NodePtr ) ));
101 int32 dllCheckNode( DoublyLinkedListNode *NodePtr )
103 if( (NodePtr->dlln_Next->dlln_Previous != NodePtr) ||
104 (NodePtr->dlln_Previous->dlln_Next != NodePtr))
106 ERR("dllCheckNode: Bad Node!\n");
107 dllDumpNode( dllPreviousNode( NodePtr ) );
108 dllDumpNode( NodePtr );
109 dllDumpNode( dllNextNode( NodePtr ) );
117 void dllRemoveNode( DoublyLinkedListNode *NodePtr )
119 if( dllCheckNode( NodePtr ) == 0 )
121 dllLinkNodes( dllPreviousNode( NodePtr ), dllNextNode( NodePtr ) );
125 void dllAddNodeToHead( DoublyLinkedList *ListPtr, DoublyLinkedListNode *NewNodePtr )
127 dllInsertNodeBefore( NewNodePtr, ListPtr->dll_First );
130 void dllAddNodeToTail( DoublyLinkedList *ListPtr, DoublyLinkedListNode *NewNodePtr )
132 dllInsertNodeAfter( NewNodePtr, ListPtr->dll_Last );
135 #define dllIsNodeInList( n ) (!((n)->dlln_Next == NULL) )
136 #define dllIsLastNode( n ) ((n)->dlln_Next->dll_nNext == NULL )
137 #define dllIsListEmpty( l ) ((l)->dll_First == ((DoublyLinkedListNode *) &((l)->dll_Null)) )
138 #define dllFirstNode( l ) ((l)->dll_First)
140 static DoublyLinkedList gMemList;
141 static int32 gIfMemListInit;
143 typedef struct MemListNode
145 DoublyLinkedListNode mln_Node;
150 /***************************************************************
153 void maDumpList( void )
157 MSG("PForth MemList\n");
159 for( mln = (MemListNode *) dllFirstNode( &gMemList );
160 dllIsNodeInList( (DoublyLinkedListNode *) mln);
161 mln = (MemListNode *) dllNextNode( (DoublyLinkedListNode *) mln ) )
163 MSG(" Node at = 0x"); ffDotHex(mln);
164 MSG_NUM_H(", size = 0x", mln->mln_Size);
170 /***************************************************************
171 ** Free mem of any size.
173 static void pfFreeRawMem( char *Mem, int32 NumBytes )
175 MemListNode *mln, *FreeNode;
176 MemListNode *AdjacentLower = NULL;
177 MemListNode *AdjacentHigher = NULL;
178 MemListNode *NextBiggest = NULL;
180 /* Allocate in whole blocks of 16 bytes */
181 DBUG(("\npfFreeRawMem( 0x%x, 0x%x )\n", Mem, NumBytes ));
182 NumBytes = (NumBytes + PF_MEM_BLOCK_SIZE - 1) & ~(PF_MEM_BLOCK_SIZE - 1);
183 DBUG(("\npfFreeRawMem: Align NumBytes to 0x%x\n", NumBytes ));
185 /* Check memory alignment. */
186 if( ( ((int32)Mem) & (PF_MEM_BLOCK_SIZE - 1)) != 0)
188 MSG_NUM_H("pfFreeRawMem: misaligned Mem = 0x", (int32) Mem );
192 /* Scan list from low to high looking for various nodes. */
193 for( mln = (MemListNode *) dllFirstNode( &gMemList );
194 dllIsNodeInList( (DoublyLinkedListNode *) mln);
195 mln = (MemListNode *) dllNextNode( (DoublyLinkedListNode *) mln ) )
197 if( (((char *) mln) + mln->mln_Size) == Mem )
201 else if( ((char *) mln) == ( Mem + NumBytes ))
203 AdjacentHigher = mln;
205 /* is this the next biggest node. */
206 else if( (NextBiggest == NULL) && (mln->mln_Size >= NumBytes) )
212 /* Check to see if we can merge nodes. */
215 DBUG((" Merge (0x%x) -> 0x%x\n", Mem, AdjacentHigher ));
216 NumBytes += AdjacentHigher->mln_Size;
217 dllRemoveNode( (DoublyLinkedListNode *) AdjacentHigher );
221 DBUG((" Merge 0x%x -> (0x%x)\n", AdjacentLower, Mem ));
222 AdjacentLower->mln_Size += NumBytes;
226 DBUG((" Link before 0x%x\n", NextBiggest ));
227 FreeNode = (MemListNode *) Mem;
228 FreeNode->mln_Size = NumBytes;
229 if( NextBiggest == NULL )
231 /* Nothing bigger so add to end of list. */
232 dllAddNodeToTail( &gMemList, (DoublyLinkedListNode *) FreeNode );
236 /* Add this node before the next biggest one we found. */
237 dllInsertNodeBefore( (DoublyLinkedListNode *) FreeNode,
238 (DoublyLinkedListNode *) NextBiggest );
247 /***************************************************************
248 ** Setup memory list. Initialize allocator.
250 void pfInitMemAllocator( void *addr, uint32 poolSize )
257 gMemPoolSize = poolSize;
259 dllSetupList( &gMemList );
260 gIfMemListInit = TRUE;
262 /* Adjust to next highest aligned memory location. */
263 AlignedMemory = (char *) ((((int32)gMemPoolPtr) + PF_MEM_BLOCK_SIZE - 1) &
264 ~(PF_MEM_BLOCK_SIZE - 1));
266 /* Adjust size to reflect aligned memory. */
267 AlignedSize = gMemPoolSize - (AlignedMemory - gMemPoolPtr);
269 /* Align size of pool. */
270 AlignedSize = AlignedSize & ~(PF_MEM_BLOCK_SIZE - 1);
273 pfFreeRawMem( AlignedMemory, AlignedSize );
277 /***************************************************************
278 ** Allocate mem from list of free nodes.
280 static char *pfAllocRawMem( int32 NumBytes )
285 if( NumBytes <= 0 ) return NULL;
287 if( gIfMemListInit == 0 ) pfInitMemAllocator( PF_MALLOC_ADDRESS, PF_MEM_POOL_SIZE );
289 /* Allocate in whole blocks of 16 bytes */
290 NumBytes = (NumBytes + PF_MEM_BLOCK_SIZE - 1) & ~(PF_MEM_BLOCK_SIZE - 1);
292 DBUG(("\npfAllocRawMem( 0x%x )\n", NumBytes ));
294 /* Scan list from low to high until we find a node big enough. */
295 for( mln = (MemListNode *) dllFirstNode( &gMemList );
296 dllIsNodeInList( (DoublyLinkedListNode *) mln);
297 mln = (MemListNode *) dllNextNode( (DoublyLinkedListNode *) mln ) )
299 if( mln->mln_Size >= NumBytes )
305 /* Remove this node from list. */
306 dllRemoveNode( (DoublyLinkedListNode *) mln );
308 /* Is there enough left in block to make it worth splitting? */
309 RemSize = mln->mln_Size - NumBytes;
310 if( RemSize >= PF_MEM_BLOCK_SIZE )
312 pfFreeRawMem( (Mem + NumBytes), RemSize );
319 DBUG(("Allocate mem at 0x%x.\n", Mem ));
323 /***************************************************************
324 ** Keep mem size at first cell.
326 char *pfAllocMem( int32 NumBytes )
330 if( NumBytes <= 0 ) return NULL;
332 /* Allocate an extra cell for size. */
333 NumBytes += sizeof(int32);
335 IntMem = (int32 *)pfAllocRawMem( NumBytes );
337 if( IntMem != NULL ) *IntMem++ = NumBytes;
339 return (char *) IntMem;
342 /***************************************************************
343 ** Free mem with mem size at first cell.
345 void pfFreeMem( void *Mem )
350 if( Mem == NULL ) return;
352 /* Allocate an extra cell for size. */
353 IntMem = (int32 *) Mem;
357 pfFreeRawMem( (char *) IntMem, NumBytes );
361 #endif /* PF_NO_MALLOC */