/*
 * 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "kernel.h"
#include "swis.h"
#include "error.h"
#include "memory.h"

extern char *caller(void);

static char *wibble;

static int dynam, area, size, base;

typedef struct mem_rec_s *mem_rec_t;

struct mem_rec_s
{
    mem_rec_t link;
    int size;
    char name[32];
};

static mem_rec_t mem_list = 0;

static void init(void)
{
    _kernel_swi_regs r;

    r.r[0] = 0;
    r.r[1] = -1;
    r.r[2] = 1024;
    r.r[3] = -1;
    r.r[4] = 0x81;
    r.r[5] = 8*1024*1024;
    r.r[6] = 0;
    r.r[7] = 0;
    r.r[8] = (int) "Leak tested Malloc space";
    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);
}

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]) return (void *) r.r[2];
    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);
    return (void *) r.r[2];
}

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

    r.r[0] = 3;
    r.r[1] = base;
    r.r[2] = (int) mem;
    err = _kernel_swi(OS_Heap, &r, &r);
    if(err)
        printf("Error on free: %s %s\n", wibble, err->errmess);
}

void *Malloc0X(int i)
{
    static int uninit = 1;
    void *res;

    if(uninit)
    {
        init();
        uninit = 0;
    }
    res = dynam ? d_malloc(i) : malloc(i);
    return res;
}

static void *MallocX(int i)
{
    void *res;

    res = Malloc0X(i);
    if(i != 0 && res == NULL)
        ErrorFatal("Insufficient memory");
    return res;
}

void *Malloc(int i)
{
    mem_rec_t mr;

    mr = MallocX(sizeof(*mr) + i);
    strcpy(mr->name, caller());
    mr->size = i;
    mr->link = mem_list;
    mem_list = mr;
    return mr+1;
}

void *Malloc0(int i)
{
    mem_rec_t mr;

    mr = Malloc0X(sizeof(*mr) + i);
    if (mr == 0)
      return mr;
    strcpy(mr->name, caller());
    mr->size = i;
    mr->link = mem_list;
    mem_list = mr;
    return mr+1;
}

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

void Free(void *d)
{
    mem_rec_t mr, *p;

    if (d == 0)
      return;
    mr = d;
    mr--;
    p = &mem_list;
    while(*p)
    {
        if(*p == mr)
            *p = ((*p)->link);
        else
            p = &((*p)->link);
    }
    wibble = caller();
    FreeX(mr);
}

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

    res = malloc(i);
    if(i != 0 && res == NULL)
        ErrorFatal("Insufficient memory");
    return res;
}

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

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

    printf("MemMin\n");
    for(mr = mem_list; mr; mr = mr->link)
        printf("%s leaked %d\n", mr->name, mr->size);
    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;
    }
}

void MemKill(void)
{
    _kernel_swi_regs r;

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