/*
** ppc.library emulation
** (c)1998-2001 Frank Wille <frank@phoenix.owl.de>
**
** M68k support functions
**
** V0.8f (10.03.2001) phx
**       New environment variable: IgnoreNOCACHEM68K.
** V0.8e (16.12.2000) phx
**       ReadENV() reads environment variables.
** V0.8  (29.06.2000) phx
**       SignalPPCor68k() sends a signal to a PPC or M68k task.
** V0.4c (21.01.1999) phx
**       Rewritten from scratch. Always use 32-byte aligned memory.
**       Old functions: outofmem(), Dprintf().
**       New: alloc32, alloc32c, newpool32, allocp32, allocpstr
**       As powerpc.library V14 doesn't support pooled memory, I'm
**       trying to simulate this until V15 is ready.
** V0.1  (15.11.1998) phx
**       First partially working ppc.library emulation. Synchronous PPC
**       tasks, started by runelf, which only use the basic PowerUp kernel
**       functions for I/O, memory and context-switch seem to work fine.
** V0.0  (02.11.1998) phx
**       created
*/

#include "ppclibemu.h"
#include "errors.h"
#include <exec/memory.h>
#include <exec/lists.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/powerpc.h>
#include "oscall.h"


static const char *ENVpath = "ENV:ppclibemu/";
#define ENVPATHLEN 14



/* @@@ V14 only! @@@ ---> */

struct Puddle
{
  struct MinNode n;
  char reserved[24];
};

/* <--- @@@ V14 only! @@@ */

APTR getSignalPPC(__reg("a0")struct PPCBase *) =
  "\tmove.l\t-484(a0),d0";



static void outofmem(struct PPCLibBase *ppcbase,ULONG bytes)
{
  error1(ppcbase,"While trying to allocate %ld bytes:\n"
         "Out of memory!",bytes);
}


void *alloc32(ULONG size,struct PPCLibBase *ppcbase)
{
  void *p;

  if (!(p = AllocVec32(size,MEMF_PUBLIC)))
    outofmem(ppcbase,size);
  return (p);
}


void *alloc32c(ULONG size,struct PPCLibBase *ppcbase)
{
  void *p;

  if (!(p = AllocVec32(size,MEMF_CLEAR|MEMF_PUBLIC)))
    outofmem(ppcbase,size);
  return (p);
}


void *newpool32(ULONG psize,struct PPCLibBase *ppcbase)
{
#if 0 /* powerpc.library V15 */
  void *p;

  if (!(p = CreatePool32(MEMF_ANY|MEMF_PUBLIC,psize,psize)))
    outofmem(ppcbase,psize);
  return (p);

#else
/* @@@ V14 only! @@@ ---> */

  struct MinList *pool;

  if (pool = alloc32(sizeof(struct MinList),ppcbase))
    _newlist((struct List *)pool);
  return ((void *)pool);

/* <--- @@@ V14 only! @@@ */
#endif
}


/* @@@ V14 only! @@@ ---> */
void freepool32(void *pool,struct PPCLibBase *ppcbase)
{
  if (pool) {
    struct Puddle *p;

    while (p = (struct Puddle *)_remhead((struct List *)pool)) {
      FreeVec32(p);
    }
    FreeVec32(pool);
  }
}
/* <--- @@@ V14 only! @@@ */


void *allocp32(void *pool,ULONG size,struct PPCLibBase *ppcbase)
{
#if 0 /* powerpc.library V15 */
  void *p;

  if (!(p = AllocPooled32(pool,size)))
    outofmem(ppcbase,size);
  return (p);

#else
/* @@@ V14 only! @@@ ---> */

  struct Puddle *p;

  if (p = alloc32(sizeof(struct Puddle)+size,ppcbase)) {
    _addhead((struct List *)pool,(struct Node *)&p->n);
    p++;
  }
  else
    outofmem(ppcbase,size);
  return ((void *)p);

/* <--- @@@ V14 only! @@@ */
#endif
}


char *allocpstr(void *pool,char *s,struct PPCLibBase *ppcbase)
{
#if 0 /* powerpc.library V15 */
  char *d;
  ULONG size = _strlen(s)+1;

  if (!(d = AllocPooled32(pool,size)))
    outofmem(ppcbase,size);
  _strcpy(d,s);
  return (d);

#else
/* @@@ V14 only! @@@ ---> */
  char *d;
  ULONG size = _strlen(s)+1;

  if (d = allocp32(pool,size,ppcbase))
    _strcpy(d,s);
  return (d);

/* <--- @@@ V14 only! @@@ */
#endif
}


void Dprintf(struct PPCLibBase *ppcbase,char *fmt,...)
{
  if (ppcbase->Flags & PPCLibF_Debug) {
    va_list vl;

    va_start(vl,fmt);
    SPrintF68K((STRPTR)fmt,(APTR)vl);
    va_end(vl);
  }
}


void SignalPPCor68k(struct Task *tsk,ULONG mask,struct PPCLibBase *ppcbase)
/* this function sends a signal to a PPC or M68k task */
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  struct PPCArgs pa;

  if (tsk->tc_Node.ln_Type == NT_PPCTASK) {
    _memset(&pa,sizeof(struct PPCArgs),0);
    pa.PP_Code = getSignalPPC(wosbase);  /* SignalPPC() func. ptr. */
    pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
    pa.PP_Regs[PPREG_D1] = (ULONG)tsk;
    pa.PP_Regs[PPREG_A0] = mask;
    RunPPC(&pa);
  }
  else
    Signal(tsk,mask);
}


static int readENVflag(struct PPCLibBase *ppcbase,char *name)
/* returns 1 if variable exists and is set to a value != 0, */
/* otherwise return 0 */
{
  char var[ENVPATHLEN+32];
  char buf[16];
  long val = 0;
  int rc = 0;

  _strcpy(var,ENVpath);
  _strncat(var,name,32);
  if (GetVar(var,buf,15,0) > 0) {
    StrToLong(buf,&val);
    if (val)
      rc = 1;
  }
  return rc;
}


void ReadENV(struct PPCLibBase *ppcbase)
/* read environment variables from ENV:ppclibemu/ */
{
  static const char *msg = "Active ENV-variable: ";

  if (readENVflag(ppcbase,"Debug")) {
    ppcbase->Flags |= PPCLibF_Debug;
    Dprintf(ppcbase,"%sDebug\n",msg);
  }
  if (readENVflag(ppcbase,"NoFreePatch")) {
    ppcbase->Flags |= PPCLibF_NoFreeMemPatch;
    Dprintf(ppcbase,"%sNoFreePatch\n",msg);
  }
  if (readENVflag(ppcbase,"NoELFPatch")) {
    ppcbase->Flags |= PPCLibF_NoLoadSegPatch;
    Dprintf(ppcbase,"%sNoELFPatch\n",msg);
  }
  if (readENVflag(ppcbase,"IgnoreNOCACHEM68K")) {
    ppcbase->Flags |= PPCLibF_IgnoreNoCacheM68k;
    Dprintf(ppcbase,"%sIgnoreNoCacheM68k\n",msg);
  }
}
