Memory Management Routines -------------------------- The stdlib memory management routines let you dynamically allocate storage on the heap. These routines are somewhat similar to those provided by the "C" programming language. However, these routines do not perform garbage collection, as this would introduce too many restrictions and have a very adverse effect on speed. The following paragraph gives a description of how the memory management routines work. These routines may be updated in future revisions, however, so you should never make assumptions about the structure of the memory management record (described below) or else your code may not work on the next revision. The allocation/deallocation routines should be fairly fast. Malloc and free use a modified first/next fit algorithm which lets the system quickly find a memory block of the desired size without undue fragmentation problems (average case). The memory manager data structure has an overhead of eight bytes (meaning each malloc operation requires at least eight more bytes than you ask for) and a granularity of 16 bytes. The overhead (eight bytes) per allocated block may seem rather high, but that is part of the price to pay for faster malloc and free routines. All pointers are far pointers and each new item is allocated on a paragraph boundary. The current memory manager routines always allocate (n+8) bytes, rounding up to the next multiple of 16 if the result is not evenly divisible by sixteen. The first eight bytes of the structure are used by the memory management routines, the remaining bytes are available for use by the caller (malloc, et. al., return a pointer to the first byte beyond the memory management overhead structure). NOTE: There was a major change in the way this package works starting with version 30 of the library. Prior to version 30, MemInit required a parameter in the DX register to determine where to allocate the heap and how much storage to allocate. Furthermore, the older versions called DOS to deallocate memory then reallocate it for the heap. Finally, the older versions required that you set up a global variable "PSP" containing the program segment prefix value. As of version 30, MemInit was split into two routines: MemInit and MemInit2. MemInit allocates all of available memory (like the standard version of the earlier MemInit) whereas MemInit2 lets you specify the location and size of the heap. The new version calls DOS to get the PSP (so you don't need to declare the PSP variable just for MemInit). The new version does not reallocate memory blocks with DOS calls (which created some problems, especially with debugger programs). Finally the new versions work fine with ".EXE" files which do not get all leftover memory allocated to them. Most older STDLIB programs will work just fine with the new MemInit routine. If you relied on MemInit to reallocate memory for you, or if you specified the location of the heap, you will need to modify your program to use these new versions of the MemInit routine. Routine: MemInit ----------------- Category: Memory Management Routine Registers on Entry: Nothing Globals Affected: zzzzzzseg - segment name of the last segment in your program Registers on return: CX - number of paragraphs actually reserved by MemInit Flags affected: None Example of Usage: ; Don't forget to set up ; zzzzzzseg before calling ; MemInit MemInit Description: This routine initializes the memory manager system. You must call it before using any routines which call any of the memory manager procedures (since a good number of the stdlib routines call the memory manager, you should get in the habit of always calling this routine.) The system will "die a horrible death" if you call a memory manager routine (like malloc) without first calling MemInit. This routine expects you to define (and set up) a global names: zzzzzzseg. "zzzzzzseg" is a dummy segment which must be the name of the very last segment defined in your program. MemInit uses the name of this segment to determine the address of the last byte in your program. If you do not declare this segment last, the memory manager will overwrite anything which follows zzzzzzseg. The "shell.asm" file provides you with a template for your programs which properly defines this segment. On return from MemInit, the CX register contains the number of paragraphs actually allocated. Include: stdlib.a or memory.a Routine: MemInit2 ------------------ Category: Memory Management Routine Registers on Entry: ES- segment address of the start of the heap. CX- Number of paragraphs to allocate for the heap. Registers on return: None Flags affected: None Example of Usage: mov cx, seg HeapSeg mov es, cx mov cx, HeapSize ;In paragraphs! MemInit2 Description: This routine initializes the memory manager system. You must call it before using any routines which call any of the memory manager procedures (since a good number of the stdlib routines call the memory manager, you should get in the habit of always calling this routine.) The system will "die a horrible death" if you call a memory manager routine (like malloc) without first calling MemInit2 (or MemInit). This routine lets you decide where the heap lies in memory (as opposed to MemInit which uses all available bytes from the end of your program to the end of memory). Note: you should only call MemInit or MemInit2 once in your program. Include: stdlib.a or memory.a Routine: Malloc ---------------- Category: Memory Management Routine Registers on Entry: CX - number of bytes to reserve Registers on return: CX - number of bytes actually reserved by Malloc ES:DI - ptr to 1st byte of memory allocated by Malloc Flags affected: Carry=0 if no error. Carry=1 if insufficient memory. Example of Usage: mov cx, 256 Malloc jnc GoodMalloc print db "Insufficient memory to continue.",cr,lf,0 jmp Quit GoodMalloc: mov es:[di], 0 ;Init string to NULL Description: Malloc is the workhorse routine you use to allocate a block of memory. You give it the number of bytes you need and if it finds a block large enough, it will allocate the requested amount and return a pointer to that block. Most memory managers require a small amount of overhead for each block they allocate. Stdlib's (current) memory manager requires an overhead of eight bytes. Furthermore, the grainularity is 16 bytes. This means that Malloc always allocates blocks of memory in paragraph multiples. Therefore, Malloc may actually reserve more storage than you specify. Therefore, the value returned in CX may be somewhat greater than the requested value. By setting the minimum allocation size to a paragraph, however, the overhead is reduced and the speed of Malloc is improved by a considerable amount. Stdlib's memory management does not do any garbage collection. Doing so would place too many demands on Malloc's users. Therefore, it is quite possible for you to fragment memory with multiple calls to maloc, realloc, and free. You could wind up in a situation where there is enough free memory to satisfy your request, but there isn't a single contiguous block large enough for the request. Malloc treats this as an insufficient memory error and returns with the carry flag set. If Malloc cannot allocate a block of the requested size, it returns with the carry flag set. In this situation, the contents of ES:DI is undefined. Attempting to dereference this pointer will produce erratic and, perhaps, disasterous results. Include: stdlib.a or memory.a Routine: Realloc ----------------- Category: Memory Management Routine Registers on Entry: CX - number of bytes to reserve ES:DI - pointer to block to reallocate. Registers on return: CX - number of bytes actually reserved by Realloc. ES:DI - pointer to first byte of memory allocated by Realloc. Flags affected: Carry = 0 if no error. Carry = 1 if insufficient memory. Example of Usage: mov cx, 1024 ;Change block size to 1K les di, CurPtr ;Get address of block into ES:DI realloc jc BadRealloc mov word ptr CurPtr, di mov word ptr CurPtr+2, es Description: Realloc lets you change the size of an allocated block in the heap. It allows you to make the block larger or smaller. If you make the block smaller, Realloc simply frees (returns to the heap) any leftover bytes at the end of the block. If you make the block larger, Realloc goes out and allocates a block of the requested size, copies the bytes form the old block to the beginning of the new block (leaving the bytes at the end of the new block uninitialized)), and then frees the old block. Include: stdlib.a or memory.a Routine: Free -------------- Category: Memory Management Routine Registers on Entry: ES:DI - pointer to block to deallocate Registers on return: None Flags affected: Carry = 0 if no error. Carry = 1 if ES:DI doesn't point at a Free block. Example of Usage: les di, HeapPtr Free Description: Free (possibly) deallocates storage allocated on the heap by malloc or Realloc. Free returns this storage to heap so other code can reuse it later. Note, however, that Free doesn't always return storage to the heap. The memory manager data structure keeps track of the number of pointers currently pointing at a block on the heap (see DupPtr, below). If you've set up several pointers such that they point at the same block, Free will not deallocate the storage until you've freed all of the pointers which point at the block. Free usually returns an error code (carry flag = 1) if you attempt to Free a block which is not currently allocated or if you pass it a memory address which was not returned by malloc (or Realloc). By no means is this routine totally robust. If you start calling free with arbitrary pointers in es:di (which happen to be pointing into the heap) it is possible, under certain circumstances, to confuse Free and it will attempt to free a block it really should not. This problem could be solved by adding a large amount of extra code to the free routine, but it would slow it down considerably. Therefore, a little safety has been sacrificed for a lot of speed. Just make sure your code is correct and everything will be fine! Include: stdlib.a or memory.a Routine: DupPtr ---------------- Category: Memory Manager Routine Registers on Entry: ES:DI - pointer to block Registers on return: None Flags affected: Carry = 0 if no error. Carry = 1 if es:di doesn't point at a free block. Example of Usage: les di, Ptr DupPtr Description: DupPtr increments the pointer count for the block at the specifiied address. Malloc sets this counter to one. Free decrements it by one. If free decrements the value and it becomes zero, free will release the storage to the heap for other use. By using DupPtr you can tell the memory manager that you have several pointers pointing at the same block and that it shouldn't deallocate the storage until you free all of those pointers. Include: stdlib.a or memory.a Routine: IsInHeap ------------------ Category: Memory Management Routine Registers on Entry: ES:DI - pointer to a block Registers on return: None Flags affected: Carry = 0 if ES:DI is a valid pointer. Carry = 1 if not. Example of Usage: les di, MemPtr IsInHeap jc NotInHeap Description: This routine lets you know if es:di contains the address of a byte in the heap somewhere. It does not tell you if es:di contains a valid pointer returned by malloc (see IsPtr, below). For example, if es:di contains the address of some particular element of an array (not necessarily the first element) allocated on the heap, IsInHeap will return with the carry clear denoting that the es:di points somewhere in the heap. Keep in mind that calling this routine does not validate the pointer; it could be pointing at a byte which is part of the memory manager data structure rather than at actual data (since the memory manager maintains that informatnion within the bounds of the heap). This routine is mainly useful for seeing if something is allocated on the heap as opposed to somewhere else (like your code, data, or stack segment). Include: stdlib.a or memory.a Routine: IsPtr --------------- Category: Memory Management Routine Registers on Entry: ES:DI - pointer to block Registers on return: None Flags affected: Carry = 0 if es:di is a valid pointer. Carry = 1 if not. Example of Usage: les di, MemPtr IsPtr jc NotAPtr Description: IsPtr is much more specific than IsInHeap. This routine returns the carry flag clear if and only if es:di contains the address of a properly allocated (and currently allocated) block on the heap. This pointer must be a value returned by Malloc, Realloc, or DupPtr and that block must be currently allocated for IsPtr to return the carry flag clear. Include: stdlib.a or memory.a Routine: BlockSize ------------------- Category: Memory Management Routine Registers on Entry: ES:DI - pointer to block Registers on return: CX- Size of specifed block (in bytes). Returns zero if ES:DI does not point at a legal block. Flags affected: None Example of Usage: les di, MemPtr BlockSize Description: BlockSize returns the size (in bytes) of a block allocated on the heap. If the block is not in the heap, this code returns zero in CX. This routine does NOT verify that the block was actually allocated and is still allocated. It just makes sure that the pointer points at a valid location somewhere in the heap and returns the block size from the data structure at the specified address. You are responsible for ensuring that you do not use a deallocated memory block. Include: stdlib.a or memory.a Routine: MemAvail ------------------ Category: Memory Management Routine Registers on Entry: None Registers on return: CX- Size of largest free block on the heap Flags affected: None Example of Usage: MemAvail Description: MemAvail returns the size (in paragraphs) of the largest free block on the heap. You can use this call to determine if there is sufficient storage for an object on the heap. Include: stdlib.a or memory.a Routine: MemFree ----------------- Category: Memory Management Routine Registers on Entry: None Registers on return: CX- Size of all free blocks on the heap. Flags affected: None Example of Usage: MemFree Description: MemFree returns the size (in paragraphs) of the the free storage on the heap. Note that this storage might be fragmented and not all of it may be available for use by Malloc. To determine the largest free block available use MemAvail. Include: stdlib.a or memory.a