/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "Licence").
 * You may not use this file except in compliance with the Licence.
 *
 * You can obtain a copy of the licence at RISC OS path @.^.LICENCE
 * or  http://www.riscosdev.com/lanman98/LICENCE.CDDL
 * See the Licence for the specific language governing permissions
 * and limitations under the Licence.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the Licence file. If applicable, add the
 * following below this CDDL HEADER, with the fields enclosed by
 * brackets "[]" replaced with your own identifying information:
 * Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
 
/*
 *   Copyright 1996 Warm Silence Software Ltd.  All rights reserved.
 *   Use is subject to license terms.
 */

/*   PHBG 7/10/96: Initial version
 */

#include "memcheckcompat.h"
#include "kernel.h"
#include "swis.h"
#include <stdlib.h>
#include "error.h"
#include "memory.h"

extern char *caller(void);

static int initialised = 0;
static int dynam, area, size, base;

static void *d_malloc(int i)
{
    _kernel_swi_regs r;

    r.r[0] = 2;
    r.r[1] = base;
    r.r[3] = i;
    _kernel_swi(OS_Heap, &r, &r);
    if(r.r[2] == 0)
    {
        r.r[0] = area;
        r.r[1] = i + 1024;
        _kernel_swi(OS_ChangeDynamicArea, &r, &r);
        size += r.r[1];
        r.r[3] = r.r[1];
        r.r[0] = 5;
        r.r[1] = base;
        _kernel_swi(OS_Heap, &r, &r);
        r.r[0] = 2;
        r.r[1] = base;
        r.r[3] = i;
        _kernel_swi(OS_Heap, &r, &r);
    }
#ifdef MemCheck_MEMCHECK
    if(r.r[2])
    {
        if(r.r[2] < base || r.r[2] + i > base + size)
            MemCheck_Printf("Strange malloc 0x%08x, %d\n", r.r[2], i);
        MemCheck_RegisterMiscBlock((void *) r.r[2], i);
    }
#endif
    return (void *) r.r[2];
}

static void d_free(void *mem)
{
    _kernel_swi_regs r;

    MemCheck_UnRegisterMiscBlock(mem);
    r.r[0] = 3;
    r.r[1] = base;
    r.r[2] = (int) mem;
    _kernel_swi(OS_Heap, &r, &r);
}


void MemInitialise(char *name)
{
    _kernel_swi_regs r;

    initialised = 1;
    r.r[0] = 0;
    r.r[1] = -1;
    r.r[2] = 1024;
    r.r[3] = -1;
    r.r[4] = 0x81;
    r.r[5] = 2*1024*1024;
    r.r[6] = 0;
    r.r[7] = 0;
    r.r[8] = (int) name;
    dynam = (_kernel_swi(OS_DynamicArea, &r, &r) == NULL);
    if(!dynam) return;
    area = r.r[1];
    r.r[0] = 2;
    _kernel_swi(OS_DynamicArea, &r, &r);
    size = r.r[2];
    base = r.r[3];
    r.r[0] = 0;
    r.r[1] = base;
    r.r[3] = size;
    _kernel_swi(OS_Heap, &r, &r);
}

void MemFinalise(void)
{
    _kernel_swi_regs r;

    if(dynam)
    {
        r.r[0] = 1;
        r.r[1] = area;
        _kernel_swi(OS_DynamicArea, &r, &r);
    }
}

void *Malloc(int i)
{
    void *res;

    res = Malloc0(i);
    if(i != 0 && res == NULL)
        ErrorFatal("Insufficient memory: %s", caller());
    return res;
}

void *Malloc0(int i)
{
    void *res;

    if(!initialised)
        Error("Memory not initialised");
    res = dynam ? d_malloc(i) : malloc(i);
    return res;
}

void Free(void *d)
{
    if(d)
    {
        if(dynam)
            d_free(d);
        else
            free(d);
    }
}

void *MallocRMA(int i)
{
    return malloc(i);
}

void FreeRMA(void *d)
{
    free(d);
}

void MemMinimalise(void)
{
    static struct {char tag[4]; int f, g;} crud = {"goz", 0, 0};
    _kernel_swi_regs r;

    if(dynam)
    {
        r.r[0] = 5;
        r.r[1] = base;
        r.r[3] = -size;
        _kernel_swi(OS_Heap, &r, &r);
        /* Remove 4 lines to make DA shrink */
//        r.r[0] = 5;
//        r.r[1] = base;
//        _kernel_swi(OS_Heap, &r, &r);
//        return;
        /* */
        r.r[0] = area;
        r.r[1] = -r.r[3];
        _kernel_swi(OS_ChangeDynamicArea, &r, &r);
        size -= r.r[1];
        r.r[3] -= r.r[1];
        r.r[0] = 5;
        r.r[1] = base;
        _kernel_swi(OS_Heap, &r, &r);
        r.r[0] = 1;
        r.r[1] = base;
        _kernel_swi(OS_Heap, &r, &r);
        crud.f = size - r.r[3];
        crud.g = size;
    }
}
