/*->c.flex */




#ifndef BOOL
#define BOOL int
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>

/* from clib */
#include "kernel.h"


/* from osLib: */
#include "OSLib:os.h"
#include "OSLib:toolbox.h"
#include "OSLib:wimp.h"

// from oslib support
#include "OSLibSupport:event.h"



#ifndef H__MESSAGE
#include "message.h"
#endif

#include "etc.h"
#include "flex.h"


#define FLEXEX

#define OS_CHANGE_DYNAMIC_AREA          0x00002a
#define OS_DYNAMIC_AREA                 0x000066


/* There are two alternative implementations in this file. */


typedef struct
{
  flex_ptr    anchor;    /* *anchor should point back to here.    */
  int         size;      /* in bytes. Exact size of logical area. */

#ifdef FLEXEX

  flex_lockfn lock;      /* fn to call when block is moved        */
  int         handle;    /* user handle                           */
                         /* then the actual store follows.        */
#endif


} flex__rec;


static int null_events = 0;

static BOOL null_events_enabled;


/* From base upwards, it's divided into store blocks of
  a flex__rec
  the space
  align up to next word.
*/


static osbool flex_demon ( wimp_event_no event_code,
                           wimp_block *block,
                           toolbox_block *id_block,
                           void *handle);


static int roundup (int i)
{
 return (0xfffffffc & (i + 3));
}


static flexblockstr flexbaseblock;




static int freemem (void)
{
 int        cur;
 int        next;
 int        free;
 int        new_cur = -1;
 int        new_next = -1;

 wimp_slot_size (new_cur, new_next, &cur, &next, &free);

 return (free);
}


#define APPSLOTMAX (32*1024*1024)

int flex_freemem(void)
{
/* dprintf(4,"free=%d os=%d delta=%d",flex__freespace,freemem(),
                                    flex__lim-flex__freep); */
 char * limit;
 int    free;

 limit = flexbaseblock.flex__lim + freemem();
 if (limit > (char*)(APPSLOTMAX)) limit = (char*)(APPSLOTMAX);
 free = (limit - flexbaseblock.flex__freep);
 free -= 2 * sizeof (flexblockstr);
 if (free < 0) free = 0;

 return (free);
}



/* read/write the top of available memory. *top == -1 -> just read. */

static void flex__wimpslot (char **top)
{
 int        cur;
 int        next;
 int        free;
 int        new_cur = ((int) *top);
 int        new_next = -1;

/////// int dud = -1;
/////// int slot = ((int) *top);

 if (new_cur != -1) new_cur -= 0x8000;
 wimp_slot_size (new_cur, new_next, &cur, &next, &free);
//////// wimp_slot_size (&slot,&dud,&dud)
 *top = (char*)cur + 0x8000;
}


/* ignore returned errors */
static void flex__slot (int delta, flexblockstr * fb)
{
/////// os_regset   rx;
  _kernel_oserror *err;
  _kernel_swi_regs in, out;


 if (delta)
 {
  if (fb->areano)
  {
   in.r[0] = fb->areano;
   in.r[1] = delta;

///////   os_swix(OS_ChangeDynamicArea,&rx);
   err = _kernel_swi (OS_CHANGE_DYNAMIC_AREA, &in, &out);
   if (delta > 0) fb->flex__lim += out.r[1];
   else        fb->flex__lim -= out.r[1];
  }
  else
  {
   fb->flex__lim += delta;
   flex__wimpslot (&fb->flex__lim);
  }
 }
}


 /* Tries to get at least n more bytes, raising flex__lim and
  returning TRUE if it can. */

static osbool flex__more (int n, flexblockstr * fb)
{
////// os_error * err;
 char     * prev;

////// err = NULL;

 prev = fb->flex__lim;

 flex__slot (n, fb);

 if(fb->flex__lim < prev + n) /* restore state and lose extra memory */
 {
  flex__slot(prev-fb->flex__lim, fb);
  CJL_MsgDBF ("Flex Out of memory");
  return (FALSE);
////////  err=geterror(EMEM);
 }

 return TRUE;
/////// return (err);
}


static void remzeroevent (flexblockstr * fb)

{
  wimp_poll_flags mask;

  event_deregister_wimp_handler ( event_ANY,
                                  wimp_NULL_REASON_CODE,
                                  flex_demon,
                                  (void *)fb);
  if (!null_events_enabled)
  {
    event_get_mask (&mask);
    event_set_mask (mask | wimp_MASK_NULL);
  }
  return;
}


#define CLEAR_NULL_MASK (0xffffffff ^ wimp_MASK_NULL)

static osbool addzeroevent (flexblockstr * fb)

{
  wimp_poll_flags mask;
  osbool ok = TRUE;

  event_get_mask (&mask);
  null_events_enabled = (mask & 1) == 0;
  if (!null_events_enabled) event_set_mask (mask & CLEAR_NULL_MASK);
  event_register_wimp_handler ( event_ANY,
                                wimp_NULL_REASON_CODE,
                                flex_demon,
                                (void *)fb);
  return (ok);
}



static osbool flex_demon ( wimp_event_no event_code,
                           wimp_block *block,
                           toolbox_block *id_block,
                           void *handle)
{
 int free;
 flexblockstr * fb;

 fb = (flexblockstr *)handle;
 free = fb->flex__lim - fb->flex__freep;
 free -= fb->overhead;

 if (free > 0) flex__slot (-free, fb);

 remzeroevent(fb);

 fb->demon = 0;

 return(TRUE);
}


/* there is a problem because addzeroevent() may be called before */
/* events are initialised */

static osbool flex__give (flexblockstr * fb)
{
 int free;

 if (!fb->demon)
 {
  free = fb->flex__lim - fb->flex__freep;
  if(free > fb->overhead)
  {
   if( addzeroevent (fb)) fb->demon = 1;
  }
 }

 return (TRUE);
}


static osbool flex__ensure (int n, flexblockstr * fb)
{
 n -= fb->flex__lim - fb->flex__freep;

 if (n <= 0) return (TRUE);
 else     return (flex__more (n, fb));
}



osbool flex_alloca (flex_ptr anchor, int n, flexblockstr * fb)
{
//// os_error  * err;
 flex__rec * p;
 osbool ok = TRUE;

//// err = NULL;


 if (n < 0)
 {
  *anchor = 0;
 }
 else
 {
  ok = flex__ensure(sizeof(flex__rec) + roundup (n), fb);
  if (ok)
  {
   p = (flex__rec*)fb->flex__freep;
   fb->flex__freep += sizeof(flex__rec) + roundup (n);

   p->anchor = anchor;
   p->size = n;

#ifdef FLEXEX
   p->lock=NULL;
#endif

   *anchor = p + 1; /* sizeof(flex__rec), that is */
  }
 }
 return (ok);
}



static osbool flex__reanchor (flex__rec *p, int by, flexblockstr * fb)
{
////// os_error * err;
 osbool ok = TRUE;

 /* Move all the anchors from p upwards. This is in anticipation
    of that block of the heap being shifted. */

////// err = NULL;

 while (1)
 {
  if ((int)p >= (int)fb->flex__freep) break;
  if (*(p->anchor) != p + 1)
  {
    CJL_MsgDBF ("Bad memory move");
    ok = FALSE;
///////////   err = geterror(EMEMF);
   break;
  }

#ifdef FLEXEX
  if(p->lock) p->lock(p->handle, by);
#endif

  *(p->anchor) = ((char*)(p + 1)) + by;
  p = (flex__rec*)(((char*)(p + 1)) + roundup(p->size));
 }

 return (ok);
}



osbool flex_freea (flex_ptr anchor, flexblockstr * fb)
{
//// os_error  * err;
 osbool ok = TRUE;

 if (*anchor)
 {
  flex__rec * p = ((flex__rec*) *anchor) - 1;
  int roundsize = roundup (p->size);
  flex__rec * next = (flex__rec*)(((char*)(p + 1)) + roundsize);

  if(p->anchor != anchor)
  {
    CJL_MsgDBF ( "Bad memory free");
    ok = FALSE;
  }
//////////    err=geterror(EMEMF);
  else
  {
   ok = flex__reanchor (next, -(sizeof(flex__rec) + roundsize), fb);

   if (ok)
   {
    memmove (p, next, fb->flex__freep - (char*)next);
    fb->flex__freep -= sizeof (flex__rec) + roundsize;
    ok = flex__give(fb);
    *anchor = 0;
   }
  }
 } else ok = TRUE;

 return (ok);
}





osbool flex_midextenda (flex_ptr anchor, int at, int by, flexblockstr * fb)
{
//// os_error  * err;
 osbool ok = TRUE;
 flex__rec * p;
 flex__rec * next;

//// err = NULL;

 p = ((flex__rec*)*anchor) - 1;

 if (p->anchor != anchor)
 {
   CJL_MsgDBF ( "Bad memory free");
   ok = FALSE;
 }
 else
 if (at > p->size)
 {
   CJL_MsgDBF ("Bad memory free");
   ok = FALSE;
 }
 else
 if (by < 0 && (-by) > at)
 {
   CJL_MsgDBF ("Bad memory free");
   ok = FALSE;
 }
 else
 if (by == 0)
 {
  /* do nothing */
 }
 else
 if (by > 0)
 {                                        /* extend */
  int growth = roundup(p->size + by) - roundup(p->size);
  /* Amount by which the block will actually grow. */

  ok = flex__ensure (growth,fb);
  if (ok)
  {
   next = (flex__rec*)(((char*)(p + 1)) + roundup (p->size));

   /* The move has to happen in two parts because the moving
      of objects above is word-aligned, while the extension within
      the object may not be. */

   ok = flex__reanchor (next, growth, fb);
   if (ok)
   {
    memmove (((char*)next) + roundup(growth), next, fb->flex__freep - (char*) next);

    fb->flex__freep += growth;

    memmove (((char*)(p + 1)) + at + by, ((char*)(p + 1)) + at, p->size - at);
    p->size += by;
   }
  }
 }
 else
 { /* The block shrinks. */
  int shrinkage;

  next = (flex__rec*)(((char*)(p + 1)) + roundup (p->size));

  by = -by; /* a positive value now */
  shrinkage = roundup (p->size) - roundup (p->size - by);
      /* a positive value */

  memmove (((char*)(p + 1)) + at - by, ((char*)(p + 1)) + at, p->size - at);
  p->size -= by;

  ok = flex__reanchor (next, -shrinkage, fb);
  if (ok)
  {
   memmove (((char*)next) - shrinkage, next, fb->flex__freep - (char*) next);
   fb->flex__freep -= shrinkage;
   ok = flex__give (fb);
  }
 }
 return (ok);
}




osbool flex_extenda (flex_ptr anchor, int newsize, flexblockstr * fb)
{
 flex__rec *p = ((flex__rec*) *anchor) - 1;
 return (flex_midextenda (anchor, p->size, newsize - p->size, fb));
}





/* called when we propose to change amount of stuff in flex block */

osbool flex_chunka (flex_ptr anchor, int size, int chunksize,
                                                          flexblockstr * fb)
{
 osbool ok = TRUE;
//////// os_error * err;
 flex__rec *p = ((flex__rec*) *anchor) - 1;

////// err = NULL;

 if((size >= p->size) || (p->size > (size + (3 * chunksize) / 2)))
 {
  while (chunksize)
  {
   ok = flex_extenda (anchor, (size / chunksize + 1) * chunksize, fb);
   if (ok) break;
   chunksize /= 2;
  }
 }

 return (ok);
}




osbool flex_overheada (int overhead, flexblockstr * fb)
{
////// os_error * err;
 int        delta;
 osbool ok = TRUE;

////// err = NULL;

 overhead = roundup (overhead);

 delta = fb->flex__lim - fb->flex__freep;
 delta = overhead - delta;

 if (delta > 0)
 {
  flex__slot (delta, fb);
 }
 else
 if (delta < 0)
 {
  ok = flex__give (fb);
 }

 if (ok)
 {
  fb->overhead = overhead;
 }

 return (ok);
}


int flex_getoverheada (flexblockstr * fb)
{
 return (fb->overhead);
}


/**************************************************************************/

osbool flex_createarea(flexblockstr ** fbp, int bits, int max, char * name)
{
//// os_error     * err;
//// os_regset      rx;
 flexblockstr * fb;
 flexblockstr * next;
  _kernel_oserror *err;
  _kernel_swi_regs rx, out;


 rx.r[0] = 0;
 rx.r[1] = -1;
 rx.r[2] = 0;
 rx.r[3] = -1;
 rx.r[4] = bits;
 rx.r[5] = max;
 rx.r[6] = 0;
 rx.r[7] = 0;
 rx.r[8] = (int)name;

   err = _kernel_swi (OS_DynamicArea, &rx, &out);
////// err = os_swix(OS_DynamicArea, &rx);
 if (!err)
 {
  fb = *fbp;

  fb->areano = rx.r[1];
  fb->uses = 0;
  fb->demon = 0;
  fb->flex__freep = fb->flex__lim = fb->flex__base = (char*)rx.r[3];

  next = flexbaseblock.next;
  fb->prev = &flexbaseblock;
  fb->next = next;
  flexbaseblock.next = fb;
  if(next) next->prev = fb;
 }
 else
 {
  *fbp = &flexbaseblock;
  err = NULL;
 }

 return (TRUE);
}


osbool flex_deletearea (flexblockstr * fb)
{
//// os_regset      rx;
//// os_error     * err;

 osbool ok = TRUE;
 flexblockstr * next;
 flexblockstr * prev;
  _kernel_oserror *err;
  _kernel_swi_regs rx, out;


 err = NULL;

 if(fb->areano && fb->uses-- == 0)
 {
  rx.r[0] = 1;
  rx.r[1] = fb->areano;
   err = _kernel_swi (OS_DynamicArea, &rx, &out);
////  err = os_swix (OS_DynamicArea, &rx);
   if (err)
   {
     CJL_MsgDBF (err->errmess);
     ok = FALSE;
   }

  next = fb->next;
  prev = fb->prev;

  if(next) next->prev = prev;
  if(prev) prev->next = next;
 }

/**************** need tyo report error correctly here **************/
 return (ok);
}



osbool flex_finit (void)
{
 while (flexbaseblock.next) flex_deletearea (flexbaseblock.next);
 return (TRUE);
}


/**************************************************************************/

osbool flex_alloc (flex_ptr anchor,int n)
{
 return (flex_alloca(anchor, n, &flexbaseblock));
}

/* cj added */
osbool flex_salloc (flex_ptr anchor, int n)
{
  osbool ok;

  ok = flex_alloca (anchor, n, &flexbaseblock);
  if (ok)
    memset (*anchor, 0, n);
  return (ok);
}

osbool flex_free (flex_ptr anchor)
{
 return (flex_freea (anchor, &flexbaseblock));
}

osbool flex_extend (flex_ptr anchor, int newsize)
{
 return (flex_extenda (anchor, newsize, &flexbaseblock));
}

osbool flex_midextend (flex_ptr anchor, int at, int by)
{
 return (flex_midextenda (anchor, at, by, &flexbaseblock));
}

osbool flex_chunk (flex_ptr anchor, int size, int chunksize)
{
 return (flex_chunka (anchor, size, chunksize, &flexbaseblock));
}

osbool flex_overhead (int overhead)
{
 return (flex_overheada (overhead, &flexbaseblock));
}

int flex_getoverhead (void)
{
 return (flex_getoverheada (&flexbaseblock));
}

/****************************************************************************/


#ifdef FLEXEX
void flex_setlock (flex_ptr anchor, flex_lockfn lockfn, int handle)
{
 flex__rec *p = ((flex__rec*) *anchor) - 1;
 p->lock = lockfn;
 p->handle = handle;
 return;
}
#endif



osbool flex_size (flex_ptr anchor, int * size)
{
  osbool ok = TRUE;
////// os_error  * err;
 flex__rec * p = ((flex__rec*)*anchor) - 1;

 if (p->anchor != anchor) CJL_MsgDBF ("Flex memory error");

 *size = p->size;

 return (ok);
}



void flex_swop (flex_ptr anchor1, flex_ptr anchor2)
{
 flex__rec * p1 = ((flex__rec*)*anchor1) - 1;
 flex__rec * p2 = ((flex__rec*)*anchor2) - 1;

 p1->anchor = anchor2;
 p2->anchor = anchor1;

 *anchor1 = p2 + 1;
 *anchor2 = p1 + 1;

 return;
}





osbool flex_init(void)
{
////// os_error * err;
 osbool ok = TRUE;
 void     * a;

 flexbaseblock.flex__lim = (char*)-1;
 flex__wimpslot (&flexbaseblock.flex__lim);

 flexbaseblock.flex__base = flexbaseblock.flex__freep = flexbaseblock.flex__lim;

 /* Check that we're in the Wimp environment. */

 ok = flex_alloc (&a, 1);
 if (ok)
   ok = flex_free (&a);
 else
   CJL_MsgDBF ("Can only use flex in the wimp");
 return (ok);
}


/**************************************************************************/


#ifdef NEVER

#ifndef PROD

void flex_debug(flex_ptr anchor,int line)
{
 flex__rec *p = ((flex__rec*) *anchor)-1;

 dprintf(line,"anchor=%x p=%x p->anchor=%x",anchor,p,p->anchor);
}



int flex_checka(flexblockstr * fb)
{
 flex__rec * p;
 char      * q;

 p=(flex__rec *)fb->flex__base;

 while(((int)p)<((int)fb->flex__freep))
 {
  q=*(p->anchor);

  if(q<((char*)fb->flex__base) || q>((char*)fb->flex__freep))
  {
   dprintf(0,"flex pointer at %x points to illegal address.",(int)p);
   return(1);
  }

  if(*(p->anchor)!=(((char*)(p+1))))
  {
   dprintf(0,"bad flex blockat %x.",(int)p);
   return(1);
  }

  dprintf(0,"block %x len=%d.",p,p->size);

  p=(flex__rec*)(((char*)(p + 1))+roundup(p->size));
 }

 return(0);
}


int flex_check(void)
{
 return(flex_checka(&flexbaseblock));
}

#endif

#endif



