/*
** vbcc-PowerOpen/WarpOS version of malloc.c
** utilizes the powerpc.library AllocVecPPC()/FreeAllMem() memory functions
**
** v0.3 19.12.98 mikerek
**      FreeVecPPC bug repaired (fingers crossed)
** v0.2 02.08.98 phx
**      Unfortunately, there is still no Pooled Memory support in WarpOS!
**      Because AllocVecPPC()'s slowness is inacceptable, I have to
**      implement vbcc's obsolete pooled memory routines.
**      There is a bug in FreeVecPPC(), which trashes r24 and r25 if
**      memory chunks >= 400k are freed, therefore we don't use it for
**      the moment.
** v0.1 06.03.98 phx
*/

#define DEBUG
#ifdef DEBUG
#include <stdio.h>  /* only for printing "memory list corrup" to stderr */
#endif
#include <stdlib.h>
#include <string.h>
#include <powerpc/powerpc.h>
#include <clib/powerpc_protos.h>


/* @@@ delete this, if pooled memory works: */
static union _mheader _base;
static union _mheader *_freep=0;

size_t _nalloc=1023;
union _mheader *_first_mlist=0,*_last_mlist=0;

extern LONG MYFreeVecPPC(APTR);


union _mheader *_morecore(size_t anu)
{
    union _mheader *up;size_t nu;
    if(anu<_nalloc) nu=_nalloc; else nu=anu;
    up=(union _mheader *)AllocVecPPC((nu+1)*sizeof(union _mheader),0,0);
    if(!up) return(0);
    up->s.size=nu+1; up->s.ptr=0; /*  Merken fuer Freigabe am Schluss */
    if(_last_mlist){
        _last_mlist->s.ptr=up;_last_mlist=up;
    }else{
        _last_mlist=_first_mlist=up;
    }
    up++;
    up->s.size=nu;
    if(anu>_nalloc) return((void *)up);
    free((void *)(up+1));
    return(_freep);
}


void *malloc(size_t nbytes)
{
    union _mheader *p,*prevp;
    size_t nunits;
    /*  aufrunden   */
    nunits=(nbytes+sizeof(union _mheader)-1)/sizeof(union _mheader)+1;
    if(nunits>_nalloc){
        if(!(p=_morecore(nunits))) return(0);
        p->s.size=nunits;p->s.ptr=0;
        return(p+1);
    }
    if((prevp=_freep)==0){   /*  noch keine base */
        _base.s.ptr=_freep=prevp=&_base;
        _base.s.size=0;
    }
    for(p=prevp->s.ptr;;prevp=p,p=p->s.ptr){
        if(p->s.size>=nunits){  /*  Block passt */
            if(p->s.size==nunits){  /*  genau   */
                prevp->s.ptr=p->s.ptr;
            }else{  /*  ist kleiner */
                p->s.size-=nunits;p+=p->s.size;p->s.size=nunits;
            }
            _freep=prevp;
            return((void *)(p+1));
        }
        /*  mehr Speicher anfordern     */
        if(p==_freep) if(!(p=_morecore(nunits))) return(0);
    }
}


void free(void *ap)
{
    union _mheader *bp,*p,*last;
    bp=(union _mheader *)ap;
    if(!bp) return;
    bp--;
    if(bp->s.size>_nalloc){
    /*  grosse Bloecke gleich freigeben     */
        bp--;   /*  Startadresse der mlist  */
        p=_first_mlist;
        while(p){
            if(p==bp){  /*  Block gefunden  */
                if(p==_first_mlist) _first_mlist=p->s.ptr;
                    else last->s.ptr=p->s.ptr;
                if(p==_last_mlist) _last_mlist=last;
                MYFreeVecPPC(p);  /* @@@ workaround */
                return;
            }
            last=p;p=p->s.ptr;
        }
#ifdef DEBUG
        /*  wenn man hierherkommt, koennte man einen Fehler melden  */
        fprintf(stderr,"Memory list corrupt!\n");
        return;
#endif
    }
    for(p=_freep;!(bp>p&&bp<p->s.ptr);p=p->s.ptr)
        if(p>=p->s.ptr&&(bp>p||bp<p->s.ptr))
            break;

    if(bp+bp->s.size==p->s.ptr){
        bp->s.size+=p->s.ptr->s.size;
        bp->s.ptr=p->s.ptr->s.ptr;
    }else{
        bp->s.ptr=p->s.ptr;
    }
    if(p+p->s.size==bp){
        p->s.size+=bp->s.size;
        p->s.ptr=bp->s.ptr;
    }else{
        p->s.ptr=bp;
    }
    _freep=p;
}
/* end delete @@@ */


void _freemem(void)
{
  FreeAllMem();
}


#if 0 /*@@@@ stable, but slow */
void *malloc(size_t nbytes)
{
  return ((void *)AllocVecPPC(nbytes,0,0));
}


void free(void *ap)
{
  FreeVecPPC(ap);
}
#endif /*@@@@*/
