/*
** ppc.library emulation
** (c)1998-2001 Frank Wille <frank@phoenix.owl.de>
**
** Memory functions
**
** V0.8f (10.03.2001) phx
**       The NOCACHEM68K attributes in PPCAllocVec/Mem/Pooled are ignored
**       when the IgnoreNOCACHEM68K environment variable was set to "1".
**       This is extremely dangerous and unstable and will probably fail in
**       most situations, but in some it is the last chance to get a program
**       working (like e.g. CandyFactoryPro).
** V0.8d (28.11.2000) phx
**       Fixed problems with FreeMem/Vec(NULL), which were present since
**       the MEMMAGIC check in the last release.
** V0.8c (21.10.2000) phx
**       PPCAllocVec/Mem() will allocate 32 bytes more than required to
**       be able to identify this block later on as PPC-allocated.
**       PPCFree() frees PPC-allocated memory for exec.library/FreeMem()
**       or FreeVec() in this case.
** V0.8b (01.08.2000) phx
**       PPCAllocVec/Mem() functions no longer return NULL on allocation
**       of a memory area with size 0. Original PowerUp returns a valid
**       address in this case! So the size is increased to 1 to simulate
**       a similar effect. This makes it difficult to find bugs in pro-
**       grams, but we have to be compatible...
** V0.7  (12.12.1999) phx
**       PPCAllocVec/PPCAllocMem will get 32-byte aligned memory again.
**       Since V15 the AllocVecPPC function from WarpOS does only guarantee
**       8-byte alignment, because memory is always from a pool, so there
**       are no cache line problems - but maybe PowerUp software could
**       be annoyed by this fact.
** V0.6d (11.05.1999) phx
**       MEMF_NOCACHEPPC,MEMF_NOCACHEM68K,MEMF_WRITETHROUGHPPC and
**       MEMF_WRITETHROUGHM68K are supported for PPCAllocVec(),
**       PPCAllocMem() and PPCCreatePool().
** V0.6b (17.04.1999) phx
**       Rewrote mem68k.asm as ppcmem.c in C.
**       Alloc/Free functions call the appropriate PPC-functions now,
**       by doing a context-switch.
**       Support for pooled memory: PPCAllocPooled,PPCFreePooled,
**        PPCAllocVecPooled,PPCFreeVecPooled,PPCCreatePool,PPCDeletePool.
** V0.4  (22.11.1998) phx
**       Read/write data by PowerPC:
**         PPCReadByte,WriteByte,ReadWord,WriteWord,ReadLong,WriteLong,
**         WriteLongFlush.
** 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  (31.10.1998) phx
**       created
*/

#include "ppclibemu.h"
#include "errors.h"
#include "supp.h"
#include <powerup/ppclib/memory.h>
#include <powerpc/memoryPPC.h>
#include "oscall.h"


/* PPC functions to call: */
extern ULONG ppc_rb();
extern ULONG ppc_rw();
extern ULONG ppc_rl();
extern void ppc_wb();
extern void ppc_ww();
extern void ppc_wl();
extern void ppc_wli();

APTR getWarpLVO(__reg("a0")struct PPCBase *,__reg("d0")long) =
  "\tmove.l\t0(a0,d0.l),d0";



void *PPCAllocMem(__reg("d0")ULONG size,__reg("d1")ULONG attr,
                  __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCAllocMem(): ";
  struct PPCArgs pa;
  ULONG wosattr = attr & ~(MEMF_NOCACHESYNCPPC|MEMF_NOCACHESYNCM68K|
                           MEMF_NOCACHEPPC|MEMF_NOCACHEM68K|
                           MEMF_WRITETHROUGHPPC|MEMF_WRITETHROUGHM68K);
  ULONG *p;

  Dprintf(ppcbase,"%s%ld bytes, attr=0x%08lx\n",FN,size,attr);
  if (size == 0) {
    Dprintf(ppcbase,"%sAllocation with size=0 converted to size=1!\n",FN);
    size = 1;
  }
  if (attr & (MEMF_NOCACHEPPC|MEMF_NOCACHESYNCPPC|MEMF_WRITETHROUGHPPC))
    wosattr |= MEMF_CACHEOFF;
  if (!(ppcbase->Flags & PPCLibF_IgnoreNoCacheM68k)) {
    if (attr & (MEMF_NOCACHEM68K|MEMF_NOCACHESYNCM68K|MEMF_WRITETHROUGHM68K))
      wosattr |= MEMF_CHIP;  /* a bad solution, but better than failing */
  }
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = getWarpLVO(wosbase,-322); /* AllocVecPPC */
  pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
  pa.PP_Regs[PPREG_D1] = size+32;
  pa.PP_Regs[PPREG_A0] = wosattr;
  pa.PP_Regs[PPREG_A1] = 32;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
  p = (ULONG *)pa.PP_Regs[PPREG_D0];
  p[0] = MEMMAGIC;
  p[1] = (ULONG)p;
  p += 8;
  Dprintf(ppcbase,"%s-> 0x%08lx\n",FN,p);
  return (p);
}


void PPCFreeMem(__reg("a1")ULONG *mem,__reg("d0")ULONG size,
                __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCFreeMem(): ";
  struct PPCArgs pa;

  if (mem) {
    mem -= 8;
    if (mem[0]==MEMMAGIC && mem[1]==(ULONG)mem) {
      mem[0] = mem[1] = 0;
      Dprintf(ppcbase,"%s%ld bytes at 0x%08lx\n",FN,size,mem+8);
      _memset(&pa,sizeof(struct PPCArgs),0);
      pa.PP_Code = getWarpLVO(wosbase,-328); /* FreeVecPPC */
      pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
      pa.PP_Regs[PPREG_D1] = (ULONG)mem;
      pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
      RunPPC(&pa);
    }
    else {
      Dprintf(ppcbase,"%sIgnored! Memory block at 0x%08lx (%ld bytes) "
              "was not allocated by PPCAllocMem()!\n",FN,mem+8,size);
    }
  }
}


void *PPCAllocVec(__reg("d0")ULONG size,__reg("d1")ULONG attr,
                  __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCAllocVec(): ";
  struct PPCArgs pa;
  ULONG wosattr = attr & ~(MEMF_NOCACHESYNCPPC|MEMF_NOCACHESYNCM68K|
                           MEMF_NOCACHEPPC|MEMF_NOCACHEM68K|
                           MEMF_WRITETHROUGHPPC|MEMF_WRITETHROUGHM68K);
  ULONG *p;

  Dprintf(ppcbase,"%s%ld bytes, attr=0x%08lx\n",FN,size,attr);
  if (size == 0) {
    Dprintf(ppcbase,"%sAllocation with size=0 converted to size=1!\n",FN);
    size = 1;
  }
  if (attr & (MEMF_NOCACHEPPC|MEMF_NOCACHESYNCPPC|MEMF_WRITETHROUGHPPC))
    wosattr |= MEMF_CACHEOFF;
  if (!(ppcbase->Flags & PPCLibF_IgnoreNoCacheM68k)) {
    if (attr & (MEMF_NOCACHEM68K|MEMF_NOCACHESYNCM68K|MEMF_WRITETHROUGHM68K))
      wosattr |= MEMF_CHIP;  /* a bad solution, but better than failing */
  }
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = getWarpLVO(wosbase,-322); /* AllocVecPPC */
  pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
  pa.PP_Regs[PPREG_D1] = size+32;
  pa.PP_Regs[PPREG_A0] = wosattr;
  pa.PP_Regs[PPREG_A1] = 32;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
  p = (ULONG *)pa.PP_Regs[PPREG_D0];
  p[0] = MEMMAGIC;
  p[1] = (ULONG)p;
  p += 8;
  Dprintf(ppcbase,"%s-> 0x%08lx\n",FN,p);
  return (p);
}


void PPCFreeVec(__reg("a1")ULONG *mem,__reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCFreeVec(): ";
  struct PPCArgs pa;

  if (mem) {
    mem -= 8;
    if (mem[0]==MEMMAGIC && mem[1]==(ULONG)mem) {
      mem[0] = mem[1] = 0;
      Dprintf(ppcbase,"%sat 0x%08lx\n",FN,mem+8);
      _memset(&pa,sizeof(struct PPCArgs),0);
      pa.PP_Code = getWarpLVO(wosbase,-328); /* FreeVecPPC */
      pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
      pa.PP_Regs[PPREG_D1] = (ULONG)mem;
      pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
      RunPPC(&pa);
    }
    else {
      Dprintf(ppcbase,"%sIgnored! Memory block at 0x%08lx "
              "was not allocated by PPCAllocVec()!\n",FN,mem+8);
    }
  }
}


void PPCFree(__reg("a1")ULONG *mem,__reg("a6")struct ExecBase *sysbase)
/* This function is called by the patched exec.library FreeMem() and */
/* FreeVec() LVOs, when trying to free a block of memory which was */
/* previously allocated by PPCAllocMem/Vec(). */
{
  struct PPCLibBase *ppcbase;
  struct PPCArgs pa;

  if (ppcbase = (struct PPCLibBase *)
                 __OpenLibrary(sysbase,"ppc.library",0)) {
#if 0 /* @@@ */
    Dprintf(ppcbase,"PPCFree(): Deallocating PPC-allocated memory "
            "for FreeMem/Vec() at 0x%08lx\n",mem);
    mem -= 8;
    mem[0] = mem[1] = 0;
    _memset(&pa,sizeof(struct PPCArgs),0);
    pa.PP_Code = getWarpLVO(ppcbase->ppc_PowerPCBase,-328); /* FreeVecPPC */
    pa.PP_Regs[PPREG_D0] = (ULONG)ppcbase->ppc_PowerPCBase;
    pa.PP_Regs[PPREG_D1] = (ULONG)mem;
    pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
    RunPPC(&pa);
#else
    /* Don't know why, but it still causes crashes, freeing this memory */
    /* (especially with "TurboPrint"). So we just leave it alone... */
    Dprintf(ppcbase,"PPCFree(): Warning! Program tried to free PPC-"
            "allocated memory by FreeMem/Vec() at 0x%08lx. Ignored!\n",mem);
#endif
    CloseLibrary((struct Library *)ppcbase);
  }
}


void *PPCCreatePool(__reg("d0")ULONG attr,__reg("d1")ULONG puddleSize,
                    __reg("d2")ULONG threshSize,
                    __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCCreatePool(): ";
  struct PPCArgs pa;
  ULONG wosattr = attr & ~(MEMF_NOCACHESYNCPPC|MEMF_NOCACHESYNCM68K|
                           MEMF_NOCACHEPPC|MEMF_NOCACHEM68K|
                           MEMF_WRITETHROUGHPPC|MEMF_WRITETHROUGHM68K);
  void *p;

  Dprintf(ppcbase,"%sattr=0x%08lx, puddle=%ld thresh=%ld\n",
          FN,attr,puddleSize,threshSize);
  if (attr & (MEMF_NOCACHEPPC|MEMF_NOCACHESYNCPPC|MEMF_WRITETHROUGHPPC))
    wosattr |= MEMF_CACHEOFF;
  if (!(ppcbase->Flags & PPCLibF_IgnoreNoCacheM68k)) {
    if (attr & (MEMF_NOCACHEM68K|MEMF_NOCACHESYNCM68K|MEMF_WRITETHROUGHM68K))
      wosattr |= MEMF_CHIP;  /* a bad solution, but better than failing */
  }
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = getWarpLVO(wosbase,-814); /* CreatePoolPPC */
  pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
  pa.PP_Regs[PPREG_D1] = wosattr;
  pa.PP_Regs[PPREG_A0] = puddleSize;
  pa.PP_Regs[PPREG_A1] = threshSize;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
  p = (void *)pa.PP_Regs[PPREG_D0];
  Dprintf(ppcbase,"%s-> 0x%08lx\n",FN,p);
  return (p);
}


void PPCDeletePool(__reg("a0")void *p,__reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCDeletePool(): ";
  struct PPCArgs pa;

  Dprintf(ppcbase,"%sat 0x%08lx\n",FN,p);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = getWarpLVO(wosbase,-820); /* DeletePoolPPC */
  pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
  pa.PP_Regs[PPREG_D1] = (ULONG)p;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
}


void *PPCAllocPooled(__reg("a0")void *pool,__reg("d0")ULONG size,
                     __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCAllocPooled(): ";
  struct PPCArgs pa;
  void *p;

  Dprintf(ppcbase,"%s%ld bytes from pool 0x%08lx\n",FN,size,pool);
  if (size == 0) {
    Dprintf(ppcbase,"%sAllocation with size=0 converted to size=1!\n",FN);
    size = 1;
  }
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = getWarpLVO(wosbase,-826); /* AllocPooledPPC */
  pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
  pa.PP_Regs[PPREG_D1] = (ULONG)pool;
  pa.PP_Regs[PPREG_A0] = size;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
  p = (void *)pa.PP_Regs[PPREG_D0];
  Dprintf(ppcbase,"%s-> 0x%08lx\n",FN,p);
  return (p);
}


void PPCFreePooled(__reg("a0")void *pool,__reg("a1")APTR mem,
                   __reg("d0")ULONG size,
                   __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCFreePooled(): ";
  struct PPCArgs pa;

  Dprintf(ppcbase,"%s%ld bytes at 0x%08lx (pool 0x%08lx)\n",FN,size,mem,pool);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = getWarpLVO(wosbase,-832); /* FreePooledPPC */
  pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
  pa.PP_Regs[PPREG_D1] = (ULONG)pool;
  pa.PP_Regs[PPREG_A0] = (ULONG)mem;
  pa.PP_Regs[PPREG_A1] = size;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
}


void *PPCAllocVecPooled(__reg("a0")void *pool,__reg("d0")ULONG size,
                        __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCAllocVecPooled(): ";
  struct PPCArgs pa;
  void *p;

  Dprintf(ppcbase,"%s%ld bytes from pool 0x%08lx\n",FN,size,pool);
  if (size == 0) {
    Dprintf(ppcbase,"%sAllocation with size=0 converted to size=1!\n",FN);
    size = 1;
  }
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = getWarpLVO(wosbase,-826); /* AllocPooledPPC */
  pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
  pa.PP_Regs[PPREG_D1] = (ULONG)pool;
  pa.PP_Regs[PPREG_A0] = size;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
  p = (void *)pa.PP_Regs[PPREG_D0];
  Dprintf(ppcbase,"%s-> 0x%08lx\n",FN,p);
  return (p);
}


void PPCFreeVecPooled(__reg("a0")void *pool,__reg("a1")APTR mem,
                      __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  static const char *FN = "PPCFreeVecPooled(): ";
  struct PPCArgs pa;

  Dprintf(ppcbase,"%sat 0x%08lx (pool 0x%08lx)\n",FN,mem,pool);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = getWarpLVO(wosbase,-832); /* FreePooledPPC */
  pa.PP_Regs[PPREG_D0] = (ULONG)wosbase;
  pa.PP_Regs[PPREG_D1] = (ULONG)pool;
  pa.PP_Regs[PPREG_A0] = (ULONG)mem;
  pa.PP_Regs[PPREG_A1] = 0;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
}


UBYTE PPCReadByte(__reg("a0")UBYTE *p,__reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCArgs pa;

  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)ppc_rb;
  pa.PP_Regs[PPREG_D0] = (ULONG)p;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
  return ((UBYTE)pa.PP_Regs[PPREG_D0]);
}


UWORD PPCReadWord(__reg("a0")UWORD *p,__reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCArgs pa;

  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)ppc_rw;
  pa.PP_Regs[PPREG_D0] = (ULONG)p;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
  return ((UWORD)pa.PP_Regs[PPREG_D0]);
}


ULONG PPCReadLong(__reg("a0")ULONG *p,__reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCArgs pa;

  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)ppc_rl;
  pa.PP_Regs[PPREG_D0] = (ULONG)p;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
  return ((ULONG)pa.PP_Regs[PPREG_D0]);
}


void PPCWriteByte(__reg("a0")UBYTE *p,__reg("d0")ULONG val,
                  __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCArgs pa;

  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)ppc_wb;
  pa.PP_Regs[PPREG_D0] = (ULONG)p;
  pa.PP_Regs[PPREG_D1] = val;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
}


void PPCWriteWord(__reg("a0")UWORD *p,__reg("d0")ULONG val,
                  __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCArgs pa;

  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)ppc_ww;
  pa.PP_Regs[PPREG_D0] = (ULONG)p;
  pa.PP_Regs[PPREG_D1] = val;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
}


void PPCWriteLong(__reg("a0")ULONG *p,__reg("d0")ULONG val,
                  __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCArgs pa;

  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)ppc_wl;
  pa.PP_Regs[PPREG_D0] = (ULONG)p;
  pa.PP_Regs[PPREG_D1] = val;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
}


void PPCWriteLongFlush(__reg("a0")ULONG *p,__reg("d0")ULONG val,
                       __reg("a6")struct PPCLibBase *ppcbase)
{
  struct PPCArgs pa;

  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)ppc_wli;
  pa.PP_Regs[PPREG_D0] = (ULONG)p;
  pa.PP_Regs[PPREG_D1] = val;
  pa.PP_Regs[PPREG_A4] = (ULONG)ppcbase->WOSLinkerDB;
  RunPPC(&pa);
}
