/****************************************************************************/
/* Copyright 1990 MBARI                                                     */
/****************************************************************************/
/****************************************************************************/
/* Summary  : C malloc utility                                              */
/* Filename : malloc.c                                                      */
/* Author   : Andrew Pearce                                                 */
/* Project  : ROV 1.5 Microcontroller Board Rev 2.0                         */
/* Version  : 1.0                                                           */
/* Created  : 11/21/90                                                      */
/* Modified : 04/22/92                                                      */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/* $Header: /usr/tiburon/.cvsroot/micro/lib/malloc.c,v 1.4 1997/07/17 15:25:12 pean Exp $
 * $Log: malloc.c,v $
 * Revision 1.4  1997/07/17 15:25:12  pean
 * Second attempt to fix problem with mem_size alignment.
 *
 * Revision 1.3  1997/07/10 15:33:17  pean
 * Fixed problem claused when mem_start is not word aligned.
 *
 * Revision 1.2  1997/05/07 15:52:49  pean
 * Cleaned up Include files and makefile for new directory structure.
 *
 * Revision 1.1.1.1  1997/05/02 17:15:59  pean
 * Initial release of the microcontroller software after Tiburon
 * Moolpool Dive to test IView, Lapboxes, modified Power can
 * GF/5V using bus capacitance mode.
 *
 * Revision 1.1  92/05/14  09:14:28  09:14:28  pean (Andrew Pearce 408-647-3746)
 * Initial revision
 *
*/
/****************************************************************************/
                                        /* Generate code for the 80C196KB   */
#pragma model(196)
#pragma nosignedchar                    /* Use unsigned char (Char)         */

#include "C:/C96/INCLUDE/80C196.h"      /* Standard 80C186 Definitions      */
#include "C:/C96/INCLUDE/stddef.h"      /* C compiler standard definitions  */
#include "C:/C96/INCLUDE/string.h"      /* memset definition needed         */
#include "types.h"                      /* MBARI style guide declarations   */
#include "const.h"                      /* Misc. constants - TRUE/FALSE     */
#include "malloc.h"                     /* Malloc suppport routines         */

#define MIN_SEG_SIZE            8       /* Minimum fragment size            */

#define set_alloc_bit(x)     x->size |= 0x0001
#define clear_alloc_bit(x)   x->size &= 0xfffe
#define test_alloc_bit(x)    ((Boolean) (x->size & 0x0001))

#define set_size(x, s)       x->size = (s & 0xfffe) | (x->size & 0x0001)
#define get_size(x)         (Word) (x->size & 0xfffe)

extern  const Word  mem_start;          /* start of free memory pool        */
extern  const Word  mem_size;           /* size of free memory pool         */

struct malloc_hdr
{
    struct  malloc_hdr *next_hdr_ptr;   /* Link to next header              */
    Word    size;                       /* Size of this segment             */
};                                      /* LSB is allocated bit             */

typedef struct malloc_hdr malloc_hdr;
typedef malloc_hdr    *malloc_hdr_ptr;

MLocal malloc_hdr_ptr  malloc_head; /* Pointer to head of malloc list       */
MLocal malloc_hdr_ptr  malloc_tail; /* Pointer to tail of malloc list       */

MLocal malloc_hdr_ptr  pmalloc_head;/* Points to head of private malloc list*/
MLocal malloc_hdr_ptr  pmalloc_tail;/* Points to tail of private malloc list*/

MLocal Byte    *mallocFromPool(Nat16 size, malloc_hdr_ptr malloc_header);
MLocal Boolean checkLink(malloc_hdr_ptr  malloc_header);

Int16 memAvailable;

/****************************************************************************/
/* Function    : initMalloc                                                 */
/* Purpose     : Initializes malloc library functions and memory pool       */
/* Inputs      : None                                                       */
/* Outputs     : None                                                       */
/****************************************************************************/
    Void
initMalloc( Void )
{
    Word  memStart = mem_start;
    Word  memSize  = mem_size;
	
    if (memStart & 0x0001)          /* Ensure memory pool is word aligned   */
         memStart++, memSize--;

                                    /* Locate head header at start of       */
                                    /* available memory space               */
    malloc_head = (malloc_hdr_ptr) memStart;
                                    /* Segment size if total memory -       */
                                    /* head and tail header space           */
    set_size(malloc_head,
        memSize - sizeof(malloc_hdr) - sizeof(malloc_hdr));
                                    /* Mark this segment as unallocated     */
    clear_alloc_bit(malloc_head);
                                    /* Set tail link to NULL                */
    malloc_head->next_hdr_ptr = NULL;

                                    /* Locate tail ptr at end of space      */
    malloc_tail = (malloc_hdr_ptr) (memStart + memSize
                    - sizeof(malloc_hdr));
                                    /* Last segment size is always 0        */
    set_size(malloc_tail, 0);
                                    /* and is never allocated               */
    clear_alloc_bit(malloc_tail);
                                    /* Terminate end of header list         */
    malloc_tail->next_hdr_ptr   = NULL;

    malloc_head->next_hdr_ptr = malloc_tail; /* join head to tail           */

    memAvailable = memSize;
} /* initMalloc() */

/****************************************************************************/
/* Function    : malloc                                                     */
/* Purpose     : Allocate an area of memory of specified size from pool     */
/* Inputs      : size of desired memory area in bytes                       */
/* Outputs     : Returns pointer to memory or NULL if not enough memory     */
/* Note        : This function may NOT be called from interrupt context     */
/****************************************************************************/
    Byte*
malloc(size)
    Nat16   size;
{
    return(mallocFromPool(size, malloc_head));
} /* malloc */

/****************************************************************************/
/* Function    : free                                                       */
/* Purpose     : Releases memory previously allocated with malloc           */
/* Inputs      : pointer to memory from prior malloc                        */
/* Outputs     : None                                                       */
/* Note        : This function may be called from interrupt context         */
/****************************************************************************/
    Void
free(ptr)
    Byte *ptr;
{
    malloc_hdr_ptr  malloc_header;

    malloc_header = (malloc_hdr_ptr) (ptr - sizeof(malloc_hdr));

    if test_alloc_bit(malloc_header)
    {
        clear_alloc_bit(malloc_header);
        memAvailable += (get_size(malloc_header) + sizeof(malloc_hdr));
    }
} /* free() */

/****************************************************************************/
/* Function    : mallocFromPool                                             */
/* Purpose     : Allocates memory from the specified memory pool            */
/* Inputs      : size of memory area and memory pool header                 */
/* Outputs     : Returns pointer to memory or NULL if not enough memory     */
/****************************************************************************/
    MLocal Byte*
mallocFromPool(size, malloc_header)
    Nat16   size;                   /* Size of segment needed               */
    malloc_hdr_ptr malloc_header;   /* Start at head of linked list         */
{
    malloc_hdr_ptr next_malloc_header;
    malloc_hdr_ptr new_malloc_header;

    if (size & 0x0001) size++;      /* Make size an even number of bytes    */

    while (malloc_header->next_hdr_ptr != NULL)     /* End of List          */
    {
       if (checkLink(malloc_header) == FALSE)
           return(NULL);

       if test_alloc_bit(malloc_header)/* Is this segment allocated ?      */
           malloc_header = malloc_header->next_hdr_ptr;/* Walk linked list */
       else
       {                            /* Segment is free so                   */
                                    /* Is this segment big enough ?         */
          if (get_size(malloc_header) >=
                (size + sizeof(malloc_hdr) + MIN_SEG_SIZE))
          {                         /* Yes - so split this segment          */
                                    /* Calculate address of new header      */
              new_malloc_header = (malloc_hdr_ptr)
                    ((Byte*) (malloc_header) + sizeof(malloc_hdr) + size);
                                    /* Fill in size of this segment         */
              set_size(new_malloc_header, get_size(malloc_header)
                - size - sizeof(malloc_hdr));
                                    /* Mark this segment as unallocated     */
              clear_alloc_bit(new_malloc_header);
                                    /* Insert this node into the list by    */
                                    /* Joining to the next node             */
              new_malloc_header->next_hdr_ptr = malloc_header->next_hdr_ptr;
                                    /* And joining to the previous node     */
              malloc_header->next_hdr_ptr = new_malloc_header;
              set_size(malloc_header, size);  /* Set the size of segment    */
              set_alloc_bit(malloc_header);   /* Set segment as allocated   */
                                              /* Zero out allocated memory  */
              memset(((Byte *) malloc_header) + sizeof(malloc_hdr),0, size);

              memAvailable -= (size + sizeof(malloc_hdr));

              return((Byte *)       /* Return valid pointer to calller      */
                    (((Byte *) malloc_header) + sizeof(malloc_hdr)));
         } /* if */

         else
         {                          /* segment isn't large enough           */
                                    /* If the next segment is free then     */
           if (test_alloc_bit(malloc_header->next_hdr_ptr) == 0)
           {                        /* coalesce the two segments by         */
                                    /* adding sizes and ptrs                */
              next_malloc_header = malloc_header->next_hdr_ptr;
                     set_size(malloc_header, malloc_header->size +
                     sizeof(malloc_hdr) + get_size(next_malloc_header));
              malloc_header->next_hdr_ptr = next_malloc_header->next_hdr_ptr;
           } /* if */
           else                     /* Segment not free so walk link        */
             malloc_header = malloc_header->next_hdr_ptr;
          }  /* else */
       } /* else */
     } /* while */

     return(NULL);                  /* We're out of memory - return NULL    */
} /* mallocFromPool() */

/****************************************************************************/
/* Function    : checkLink                                                  */
/* Purpose     : Internal routine which checks integrty of malloc pool      */
/* Inputs      : memory pool header                                         */
/* Outputs     : Returns TRUE if link is OK, else FALSE                     */
/****************************************************************************/
    MLocal Boolean
checkLink(malloc_header)
    malloc_hdr_ptr  malloc_header;
{
    if (malloc_header->next_hdr_ptr == (malloc_hdr_ptr)
        ((Byte*) (malloc_header) + sizeof(malloc_hdr)
                     + get_size(malloc_header)))
            return(TRUE);
    return(FALSE);
} /* checkLink() */

/****************************************************************************/

    Void
bzero(Byte *buffer, Nat16 len)
{
    while (len-- > 0)
        *buffer++ = 0;
} /* bzero() */
