/*
** ppc.library emulation
** (c)1998-99 Frank Wille <frank@phoenix.owl.de>
**
** PowerPC semaphore support
**
** V0.5d (03.04.1999) phx
**       created
*/

#include "ppclibemu.h"
#include <powerpc/memoryPPC.h>
#include "mpsema_protos.h"
#include "warpos_protos.h"

#define CPUSEMA_SIZE ((sizeof(struct CPUSemaphore)+31)&~31)

static struct SignalSemaphore *(*creatSema68k)() = createSemaphore68k;
static void (*delSema68k)() = deleteSemaphore68k;



struct SignalSemaphorePPC *createSemaphorePPC(struct PPCLibBase *ppcbase)
{
  struct SignalSemaphorePPC *ss;

  if (ss = __AllocVecPPC(ppcbase->PowerPCBase,
                         sizeof(struct SignalSemaphorePPC),
                         MEMF_CLEAR|MEMF_PUBLIC,0)) {
    if (__InitSemaphorePPC(ppcbase->PowerPCBase,ss) != SSPPC_SUCCESS) {
      __FreeVecPPC(ppcbase->PowerPCBase,ss);
      ss = NULL;
    }
  }
  return (ss);
}


void deleteSemaphorePPC(struct PPCLibBase *ppcbase,
                        struct SignalSemaphorePPC *ss)
{
  if (ss) {
    __FreeSemaphorePPC(ppcbase->PowerPCBase,ss);
    __FreeVecPPC(ppcbase->PowerPCBase,ss);
  }
}


BOOL initMPSemaphorePPC(struct PPCLibBase *ppcbase,struct MPSemaphore *mps,
                        void *data,ULONG length)
/* initialize a multi-processor semaphore, which can be used to */
/* protect data against access of both CPUs */
{
  struct PPCBase *wosbase = ppcbase->PowerPCBase;
  struct PPCArgs pa;

  _memset(mps,sizeof(struct MPSemaphore),0);
  mps->data = data;
  mps->length = length;

  if ((mps->ppcSS = createSemaphorePPC(ppcbase)) &&
      (mps->cpuSS = __AllocVecPPC(wosbase,CPUSEMA_SIZE,
                                  MEMF_CLEAR|MEMF_CHIP|MEMF_PUBLIC,32))) {
    __SetCache(wosbase,CACHE_DCACHEFLUSH,mps->cpuSS,CPUSEMA_SIZE);
    _memset(&pa,sizeof(struct PPCArgs),0);
    pa.PP_Code = (APTR)creatSema68k;
    pa.PP_Regs[PPREG_A0] = (ULONG)ppcbase;
    __Run68K(wosbase,&pa);  /* let the M68k create its Semaphore */

    if (mps->m68kSS = (struct SignalSemaphore *)pa.PP_Regs[PPREG_D0]) {
      __SetCache(wosbase,CACHE_DCACHEFLUSH,data,length);
      return (TRUE);
    }
  }

  /* init failed */
  if (mps->cpuSS)
    __FreeVecPPC(wosbase,mps->cpuSS);
  deleteSemaphorePPC(ppcbase,mps->ppcSS);
  return (FALSE);
}


void freeMPSemaphorePPC(struct PPCLibBase *ppcbase,struct MPSemaphore *mps)
{
  struct PPCBase *wosbase = ppcbase->PowerPCBase;
  struct PPCArgs pa;

  if (mps->m68kSS) {
    _memset(&pa,sizeof(struct PPCArgs),0);
    pa.PP_Code = (APTR)delSema68k;
    pa.PP_Regs[PPREG_A0] = (ULONG)ppcbase;
    pa.PP_Regs[PPREG_A1] = (ULONG)mps->m68kSS;
    __Run68K(wosbase,&pa);
  }
  if (mps->cpuSS)
    __FreeVecPPC(wosbase,mps->cpuSS);
  deleteSemaphorePPC(ppcbase,mps->ppcSS);
  _memset(mps,sizeof(struct MPSemaphore),0);
}


void obtainMPSemaphorePPC(struct PPCLibBase *ppcbase,struct MPSemaphore *mps)
{
  struct PPCBase *wosbase = ppcbase->PowerPCBase;

  __ObtainSemaphorePPC(wosbase,mps->ppcSS);
  __SetCache(wosbase,CACHE_DCACHEINV,mps->cpuSS,CPUSEMA_SIZE);
  mps->cpuSS->interested[MPS_CPUPPC] = TRUE;
  __SetCache(wosbase,CACHE_DCACHEFLUSH,mps->cpuSS,CPUSEMA_SIZE);
  mps->cpuSS->cpuid = MPS_CPUPPC;
  __SetCache(wosbase,CACHE_DCACHEFLUSH,mps->cpuSS,CPUSEMA_SIZE);

  while (mps->cpuSS->cpuid==MPS_CPUPPC &&   /* wait for M68k */
         mps->cpuSS->interested[MPS_CPU68K])
    __SetCache(wosbase,CACHE_DCACHEINV,mps->cpuSS,CPUSEMA_SIZE);

  /* force a reread of the data structure */
  __SetCache(wosbase,CACHE_DCACHEINV,mps->data,mps->length);
}


void releaseMPSemaphorePPC(struct PPCLibBase *ppcbase,struct MPSemaphore *mps)
{
  struct PPCBase *wosbase = ppcbase->PowerPCBase;

  /* flush data structure and make changes visible for M68k */
  __SetCache(wosbase,CACHE_DCACHEFLUSH,mps->data,mps->length);

  mps->cpuSS->interested[MPS_CPUPPC] = FALSE;
  __SetCache(wosbase,CACHE_DCACHEFLUSH,mps->cpuSS,CPUSEMA_SIZE);
  __ReleaseSemaphorePPC(wosbase,mps->ppcSS);
}
