/*
 * 'wwheap'  (c) 2000 die@zonze.nu
 */


#include "wwheap.h"

#include <sys/bios.h>
#include <stdio.h>


/* PCBより */
extern WORD _heap;


/* malloc で使うヒープ領域 */
static BYTE *heap_top = NULL;


/* ヒープ情報 */
typedef struct _heap_block_t {
    BYTE flags;
    BYTE reserve;
    struct _heap_block_t *next;
    size_t size;
} heap_block_t;


#define BLOCKFLAG_FREEAREA  0x01    /* free された領域 */


#define align2(p)   ((((WORD)p) + 1) & 0xFFFE)
#define align16(p)  ((((WORD)p) + 15) & 0xFFF0)



#ifdef _DEBUG

static void debug_out(char far *message)
{
    comm_open();
    comm_send_string(message);
}

static void debug_newline(void)
{
    debug_out("\r\n");
}

#else

#define debug_out(m) (void)0
#define debug_newline() (void)0

#endif





/* ヒープ初期化 */
static void init_heap(void)
{
    heap_block_t *p;
    heap_top = (BYTE*)align16(_heap);
    p = (heap_block_t*)heap_top;
    p->next = NULL;
    p->size = sizeof (heap_block_t);
    p->flags = 0;
}


/* 資源の再利用 */
static BOOL check_reuse(heap_block_t *p, size_t size)
{
    size_t check, need;
    heap_block_t *np;
    heap_block_t *dp;

    if ((p->flags & BLOCKFLAG_FREEAREA) == 0) {
        return FALSE;
    }

    check = p->size;
    need = align2(size + sizeof (heap_block_t));
    if (check >= need) {
        if (check >= need + sizeof (heap_block_t)) {
            np = p->next;
            dp = (heap_block_t*)((BYTE*)p + need);
            p->next = dp;
            p->size = need;
            p->flags = 0;
            dp->next = np;
            dp->size = check - need;
            dp->flags = BLOCKFLAG_FREEAREA;
        } else {
            p->flags = 0;
        }
        return TRUE;
    }

    return FALSE;
}




void *malloc(size_t size)
{
    register heap_block_t *p;
    register heap_block_t *np;
    size_t need;

    if (heap_top == NULL) {
        init_heap();
    }

    p = (heap_block_t*)heap_top;
    np = p->next;
    while (np != NULL) {
        if (check_reuse(np, size)) {
            return (BYTE*)np + sizeof (heap_block_t);
        }
        p = np;
        np = p->next;
    }

    p->next = (heap_block_t*)((BYTE*)p + p->size);
    np = p->next;
    need = align2(size + sizeof (heap_block_t));
    np->size = need;
    np->flags = 0;
    np->next = NULL;
    return (BYTE*)np + sizeof (heap_block_t);
}




void free(void *p)
{
    register heap_block_t *bp;
    register heap_block_t *np;

    if (p == NULL) {
        return;
    }

    bp = (heap_block_t*)((BYTE*)p - sizeof (heap_block_t));
    bp->flags |= BLOCKFLAG_FREEAREA;

    bp = ((heap_block_t*)heap_top)->next;
    np = bp->next;
    while (np != NULL) {
        if ((bp->flags & np->flags & BLOCKFLAG_FREEAREA) != 0) {
            bp->size += np->size;
            bp->next = np->next;
        } else {
            bp = np;
        }

        np = bp->next;
    }
}




/* デバッグ用 */
void heap_walk(void)
{
    heap_block_t *p;
    char s[128];

    debug_out("##heap_walk##");
    debug_newline();
    p = (heap_block_t*)heap_top;
    while (p != NULL) {
        sprintf(s, "%04x: next=%04x, size=%04x, flags=%02x",
                p, p->next, p->size, p->flags);
        debug_out(s);
        debug_newline();
        p = p->next;
    }
    debug_out("##end##");
    debug_newline();
}

