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

#include "ppclibemu.h"
#include "supp.h"
#include "mpsema_protos.h"
#include <exec/memory.h>
#include <proto/exec.h>
#include <proto/powerpc.h>

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


#if 1
extern void PPCCacheClearE(__reg("a0")APTR,__reg("d0")ULONG,
                           __reg("d1")ULONG,__reg("a6")void *);
extern void PPCCacheInvalidE(__reg("a0")APTR,__reg("d0")ULONG,
                             __reg("d1")ULONG,__reg("a6")void *);
#else
#define PPCCacheClearE(p,l,f,b) __SetCache68K(CACHE_DCACHEFLUSH,p,l,(b)->PowerPCBase)
#define PPCCacheInvalidE(p,l,f,b) __SetCache68K(CACHE_DCACHEINV,p,l,(b)->PowerPCBase)
#endif



struct SignalSemaphore *createSemaphore68k(__reg("a0")struct PPCLibBase *ppcbase)
{
  struct SignalSemaphore *ss;

  if (ss = __AllocMem(sizeof(struct SignalSemaphore),
                      MEMF_CLEAR|MEMF_PUBLIC,ppcbase->SysBase)) {
    ss->ss_Link.ln_Type = NT_SIGNALSEM;
    __InitSemaphore(ss,ppcbase->SysBase);
  }
  return (ss);
}


void deleteSemaphore68k(__reg("a0")struct PPCLibBase *ppcbase,
                        __reg("a1")struct SignalSemaphore *ss)
{
  if (ss)
    __FreeMem(ss,sizeof(struct SignalSemaphore),ppcbase->SysBase);
}


BOOL initMPSemaphore68k(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->m68kSS = createSemaphore68k(ppcbase)) &&
      (mps->cpuSS = (struct CPUSemaphore *)
                    __AllocVec32(CPUSEMA_SIZE,
                                 MEMF_CLEAR|MEMF_CHIP|MEMF_PUBLIC,wosbase))) {
    PPCCacheClearE((APTR)mps->cpuSS,CPUSEMA_SIZE,CACRF_ClearD,ppcbase);
    _memset(&pa,sizeof(struct PPCArgs),0);
    pa.PP_Code = (APTR)createSemaphorePPC;
    pa.PP_Regs[PPREG_D0] = (ULONG)ppcbase;
    pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
    __RunPPC(&pa,wosbase);  /* let the PPC create its Semaphore */

    if (mps->ppcSS = (struct SignalSemaphorePPC *)pa.PP_Regs[PPREG_D0]) {
      PPCCacheClearE((APTR)data,length,CACRF_ClearD,ppcbase);
      return (TRUE);
    }
  }

  /* init failed */
  if (mps->cpuSS)
    free32(mps->cpuSS,ppcbase);
  deleteSemaphore68k(ppcbase,mps->m68kSS);
  return (FALSE);
}


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

  if (mps->ppcSS) {
    _memset(&pa,sizeof(struct PPCArgs),0);
    pa.PP_Code = (APTR)deleteSemaphorePPC;
    pa.PP_Regs[PPREG_D0] = (ULONG)ppcbase;
    pa.PP_Regs[PPREG_D1] = (ULONG)mps->ppcSS;
    pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
    __RunPPC(&pa,wosbase);
  }
  if (mps->cpuSS)
    free32(mps->cpuSS,ppcbase);
  deleteSemaphore68k(ppcbase,mps->m68kSS);
  _memset(mps,sizeof(struct MPSemaphore),0);
}


void obtainMPSemaphore68k(struct PPCLibBase *ppcbase,struct MPSemaphore *mps)
{
  __ObtainSemaphore(mps->m68kSS,ppcbase->SysBase);
  PPCCacheInvalidE((APTR)mps->cpuSS,CPUSEMA_SIZE,CACRF_ClearD,ppcbase);
  mps->cpuSS->interested[MPS_CPU68K] = TRUE;
  PPCCacheClearE((APTR)mps->cpuSS,CPUSEMA_SIZE,CACRF_ClearD,ppcbase);
  mps->cpuSS->cpuid = MPS_CPU68K;
  PPCCacheClearE((APTR)mps->cpuSS,CPUSEMA_SIZE,CACRF_ClearD,ppcbase);

  while (mps->cpuSS->cpuid==MPS_CPU68K &&   /* wait for PowerPC */
         mps->cpuSS->interested[MPS_CPUPPC])
    PPCCacheInvalidE((APTR)mps->cpuSS,CPUSEMA_SIZE,CACRF_ClearD,ppcbase);

  /* force a reread of the data structure */
  PPCCacheInvalidE((APTR)mps->data,mps->length,CACRF_ClearD,ppcbase);
}


void releaseMPSemaphore68k(struct PPCLibBase *ppcbase,struct MPSemaphore *mps)
{
  /* flush data structure and make changes visible for PPC */
  PPCCacheClearE((APTR)mps->data,mps->length,CACRF_ClearD,ppcbase);

  mps->cpuSS->interested[MPS_CPU68K] = FALSE;
  PPCCacheClearE((APTR)mps->cpuSS,CPUSEMA_SIZE,CACRF_ClearD,ppcbase);
  __ReleaseSemaphore(mps->m68kSS,ppcbase->SysBase);
}
