/* * mem * Store handling for Steel * * version 1.00 6 September 1993 * * © 1993-1998 Straylight */ /*----- Licensing note ----------------------------------------------------* * * This file is part of Straylight's Steel library. * * Steel is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * Steel is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Steel. If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "flex.h" #include "heap.h" #include "wimpt.h" #include "mem.h" #include "kernel.h" #include "bbc.h" #include #include #include #ifndef BOOL #define BOOL int #define TRUE 1 #define FALSE 0 #endif #define mem__MALLOC 1 #define mem__HEAP 2 #define mem__RMA 3 #define mem__USER 4 /* #define mem__DEBUG_MALLOC */ /* Uncomment previous line for memDebug */ #define mem__MAGIC 0x45464153 /* Says 'SAFE' */ #define mem__SENTINEL 0x59524442 /* Says 'BDRY' */ #define OS_Module 0x2001E typedef struct mem__rmaListstr { struct mem__rmaListstr *next; struct mem__rmaListstr *prev; #ifdef mem__DEBUG_MALLOC int length; int check; int *sentinel; #endif } mem__rmaListstr; static BOOL mem__initedFlex; static BOOL mem__initedHeap; static int mem__currentAlloc=mem__MALLOC; static mem__rmaListstr *mem__rmaList; #ifdef mem__DEBUG_MALLOC static mem__rmaListstr *mem__mallocList; #endif static BOOL mem__usedRMA; static mem_allocProc mem__userAlloc; static mem_freeProc mem__userFree; /* * void mem_flexdInit(const char *name) * * Use * Initialises flex system. If flex is already initialised, nothing * happens. This call should be used in preference to flex_init(). */ void mem_flexdInit(const char *name, long max) { if (!mem__initedFlex) { flex_dinit(name, max); atexit(flex_die); mem__initedFlex=TRUE; } } #ifdef _DLL void (mem_flexInit)(void) { mem_flexInit(); } #endif /* * void mem_heapInit(void) * * Use * Initialises heap system. If heap is already initialised, nothing * happens. If flex is not initialised, it is initialised for you. This * call should be used in preference to heap_init(). Note that unlike * earlier versioms, this call will NOT set up ArmenLib to use heap by * default. This would be duplicating the functionality of mem_useHeap(). */ void mem_heapInit(void) { if (!mem__initedHeap) { mem_flexInit(); heap_init(); mem__initedHeap=TRUE; } } /* * void mem_useMalloc(void) * * Use * Makes ArmenLib use malloc() etc. for memory allocation. */ void mem_useMalloc(void) { mem__currentAlloc=mem__MALLOC; } /* * void mem_useHeap(void) * * Use * Makes ArmenLib use heap_alloc() etc. for memory allocation. It is * initialised if necessary. */ void mem_useHeap(void) { mem_heapInit(); mem__currentAlloc=mem__HEAP; } /* * void mem_useUser(mem_allocProc alloc,mem_freeProc free) * * Use * Makes ArmenLib use your own user-defined memory allocation procedures. * * Parameters * mem_allocProc alloc == a routine to allocate a block of memory. If a * block of sufficient size cannot be allocated, return 0. Problems * about allocating blocks of 0 size are handled before your routine is * called. * mem_freeProc free == a routine to free a block of memory. A valid * pointer will be passed to your routine. */ void mem_useUser(mem_allocProc alloc,mem_freeProc free) { mem__currentAlloc=mem__USER; mem__userAlloc=alloc; mem__userFree=free; } /* * void mem_useRMA(void) * * Use * Makes ArmenLib use the RMA for memory allocation. */ void mem_useRMA(void) { mem__currentAlloc=mem__RMA; } /* * void mem__tidyRMA(void) * * Use * Frees any blocks allocated from the RMA at the end of the program, to * avoid any problems with blocks being non-deallocatable. */ static void mem__tidyRMA(void) { mem__rmaListstr *l=mem__rmaList; mem__rmaListstr *m; while (l!=0) { m=l; l=m->next; mem_RMAfree(m+1); } } /* * void *mem_RMAalloc(size_t size) * * Use * Allocates a block of memory from the relocatable module area. * * Parameters * size_t size == the size of the block * * Returns * A pointer to the block (or 0) */ void *mem_RMAalloc(size_t size) { _kernel_swi_regs r; mem__rmaListstr *l; if (mem__usedRMA==FALSE) { atexit(mem__tidyRMA); mem__usedRMA=TRUE; } r.r[0]=6; r.r[3]=size+sizeof(mem__rmaListstr); if (_kernel_swi(OS_Module,&r,&r)) return (0); else { l=(mem__rmaListstr *)r.r[2]; if (mem__rmaList) mem__rmaList->prev=l; l->next=mem__rmaList; l->prev=0; mem__rmaList=l; return ((void *)(l+1)); } } /* * void mem_RMAfree(void *ptr) * * Use * Frees a block allocated using RMAalloc. * * Parameters * void *ptr == a pointer to the block. */ void mem_RMAfree(void *ptr) { _kernel_swi_regs r; mem__rmaListstr *l=((mem__rmaListstr *)(ptr))-1; if (l->prev!=0) l->prev->next=l->next; else mem__rmaList=l->next; if (l->next!=0) l->next->prev=l->prev; r.r[0]=7; r.r[2]=(int)l; wimpt_noerr((os_error *)_kernel_swi(OS_Module,&r,&r)); } #ifdef mem__DEBUG_MALLOC #define mem__PASS_TEST 1 #define mem__PASS_DONE 0 #define mem__PASS_BAD 2 #define mem__PASS_DUMP 3 #define mem__walkmsg(x) \ { \ if (pass==mem__PASS_DUMP) \ printf(x); \ else \ pass=mem__PASS_BAD; \ } /* * static void mem__walkMalloc(char *op) * * Use * Part of checked malloc routines. Checks that all allocated blocks are * behaving well. * * Parameters * char *op == are we allocating or deallocating? */ static void mem__walkMalloc(char *op) { mem__rmaListstr *l; int pass=mem__PASS_TEST; while (pass!=mem__PASS_DONE) { l=mem__mallocList; while (l) { if (pass==mem__PASS_DUMP) printf("\n%p %-8i ",(l+1),l->length); if (l->check!=mem__MAGIC) mem__walkmsg("Bad block header "); if (*(l->sentinel)!=mem__SENTINEL) mem__walkmsg("Block overflow "); if ((int)l->next<0x8000 && l->next!=0) mem__walkmsg("Bad forward link "); if (l->next) { if (l->next->prev!=l) mem__walkmsg("Link corrupted "); } if (l) l=l->next; } switch (pass) { case mem__PASS_BAD: pass=mem__PASS_DUMP; bbc_vdu(26); bbc_vdu(4); bbc_vdu(12); bbc_vdu(14); bbc_vdu(7); printf("Error found in malloc chain during %s\n",op); printf(" malloc block dump follows\n\n"); printf("Address Length Faults"); /* 12345678 12345678 */ break; case mem__PASS_DUMP: printf ( "\n" "\n" "Please report this error to Straylight.\n" "\n" "If debugging, press SHIFT-F12 for backtrace, otherwise, press\n" "any key to quit program.\n" ); bbc_get(); wimpt_forceredraw(); exit(1); break; case mem__PASS_TEST: pass=mem__PASS_DONE; break; } } } /* * void *mem__debugAlloc(size_t size) * * Use * malloc debugging allocation routine. * * Parameters * size_t size == the size of the block * * Returns * A pointer to the block (or 0) */ static void *mem__debugAlloc(size_t size) { mem__rmaListstr *l; int sent; mem__walkMalloc("alloc"); if (l=malloc(size+sizeof(mem__rmaListstr)+4),!l) return (0); else { if (mem__mallocList) mem__mallocList->prev=l; l->next=mem__mallocList; l->prev=0; l->check=mem__MAGIC; mem__mallocList=l; sent=(int)(l+1)+size; if (sent%4) sent+=4-sent%4; l->sentinel=(int *)sent; l->length=size; *(l->sentinel)=mem__SENTINEL; return ((void *)(l+1)); } } /* * void mem__debugFree(void *ptr) * * Use * Frees a block allocated using mem__debugAlloc * * Parameters * void *ptr == a pointer to the block. */ static void mem__debugFree(void *ptr) { mem__rmaListstr *l=((mem__rmaListstr *)(ptr))-1; mem__walkMalloc("free"); if (l->prev!=0) l->prev->next=l->next; else mem__mallocList=l->next; if (l->next!=0) l->next->prev=l->prev; free(l); } #endif /* * void *mem_alloc(size_t size) * * Use * Allocates a block of memory using malloc (or heap if initialised). If * size is zero, or allocation fails then a NULL pointer is returned. * * Parameters * size_t size == how big you want the block. * * Returns * A pointer to the block allocated. */ void *mem_alloc(size_t size) { int *ptr=0; if (size==0) return (0); size+=2*sizeof(int); switch (mem__currentAlloc) { case mem__HEAP: ptr=heap_alloc(size); break; case mem__MALLOC: #ifdef mem__DEBUG_MALLOC ptr=mem__debugAlloc(size); #else ptr=malloc(size); #endif break; case mem__RMA: ptr=mem_RMAalloc(size); break; case mem__USER: ptr=mem__userAlloc(size); break; } if (ptr==0) return (0); ptr[0]=mem__currentAlloc; ptr[1]=size; return ((void *)(ptr+2)); } /* * void mem_free(void *ptr) * * Purpose * Frees a block of memory. It must have been allocated using mem_alloc(). * If ptr is NULL, then mem_free() does nothing. * * Parameters * void *ptr == the pointer returned by mem_alloc() */ void mem_free(void *ptr) { int *i=((int *)ptr)-2; if (ptr==0) return; switch (i[0]) { case mem__MALLOC: #ifdef mem__DEBUG_MALLOC mem__debugFree(i); #else free(i); #endif break; case mem__HEAP: heap_free(i); break; case mem__RMA: mem_RMAfree(i); break; case mem__USER: mem__userFree(i); break; } } /* * size_t mem_sizeOfBlock(void *ptr) * * Use * Returns the allocated size of the block. * * Parameters * void *ptr == the pointer to the block * * Returns * The size of the block. */ size_t mem_sizeOfBlock(void *ptr) { int *i=((int *)ptr)-2; if (ptr==0) return (0); else return (i[1]); } /* * void *mem_reAlloc(void *ptr,size_t newSize) * * Use * Alters the size of the block given. If the block could not be resized, * its contents are unchanged. Note that the block may move as a result of * this call. * * Parameters * void *ptr == a pointer to the block. This may be NULL, in which case, * this call behaves exactly like mem_alloc(). * size_t newSize == the new size to make the block. This may be zero, in * which case the block is freed. * * Returns * A pointer to the block. */ void *mem_reAlloc(void *ptr,size_t newSize) { void *newPtr; int size=mem_sizeOfBlock(ptr); if (ptr==0) return (mem_alloc(newSize)); if (newPtr=mem_alloc(newSize),newSize==0) return (0); if (newSize