/*
** ppc.library emulation
** (c)1998-2001 Frank Wille <frank@phoenix.owl.de>
**
** PowerUp kernel functions
**
** V0.9  (28.11.2001) phx
**       More debugging output.
**       Semaphore functions check for correct NT_SIGNALSEM type first.
**       Made PPCMsgPort more similar to a real exec MsgPort, like in
**       original PowerUp. Only the mp_MsgList will not be used!
** V0.8g (13.03.2001) phx
**       PPCGetTaskAttr() supports more tags: PPCTASKTAG_NAME, PRIORITY,
**       STACKSIZE, INPUTHANDLE, OUTPUTHANDLE, ERRORHANDLE, ELFOBJECT.
** 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.8e (16.12.2000) phx
**       Implemented PPCDoMethodA(), PPCCoerceMethodA() and
**       PPCDoSuperMethodA().
** 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() will allocate 32 bytes more than required to
**       be able to identify this block later on as PPC-allocated.
** V0.8b (01.08.2000) phx
**       PPCAllocVec() 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.8a (25.07.2000) phx
**       PPCMsgPort.pln is used for port list ports only, to allow
**       public ports to be in a port list too.
** V0.8  (29.06.2000) phx
**       Implemented Port List functions: PPCCreatePortList(),
**        PPCDeletePortList(), PPCAddPortList(), PPCRemPortList,
**        PPCGetPortListAttr(), PPCSetPortListAttr(), PPCWaitPortList().
** V0.7d (22.02.2000) phx
**       Returns value of HID1 for PPCINFOTAG_CPUPLL, as suggested
**       by Harry Sintonen. Although this is different from the same
**       function on the 68k-side, but this seems to be what original
**       PowerUp does!
** V0.7b (07.01.2000) phx
**       PPCINFOTAG_CPUPLL should return a special pll code and not
**       the calculated pll directly (reported by Sebastian Jedruszkiewicz).
** V0.7a (13.12.1999) phx
**       Fixed PPCFindTask(), which crashed when a task was not found!
** V0.7  (11.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.
**       Removed MEMDEBUG code, which is no longer of any use (the bug
**       which I tried to find was a temperature problem of my CSPPC/233).
** V0.6g (06.12.1999) phx
**       ELF-object loading/unloading is done by calling the appropriate
**       M68k functions: PPCLoadObject(), PPCLoadObjectTagList() and
**       PPCUnLoadObject().
**       PPCCreateTask() can start new tasks or new threads (if PC != 0).
**       Support for PPCprintf(), but the implementation is still hacky
**       and has some limitations.
** V0.6f (16.05.1999) phx
**       PPCAllocPooled == PPCAllocVecPooled and
**       PPCFreePooled == PPCFreeVecPooled.
** V0.6e (14.05.1999) phx
**       ppctaskattr() is called from kernPPCSetTaskAttr().
**       PPCTASKINFOTAG_NAME can't be set.
**       Set PPCTASKINFOTAG_PRIORITY changes NICE-value.
** V0.6d (11.05.1999) phx
**       MEMF_NOCACHEPPC,MEMF_NOCACHEM68K,MEMF_WRITETHROUGHPPC and
**       MEMF_WRITETHROUGHM68K are supported for PPCAllocVec(),
**       PPCAllocMem() and PPCCreatePool().
**       New functions: PPCReadByte,PPCReadWord,PPCReadLong,PPCWriteByte,
**       PPCWriteWord and PPCWriteLong.
**       PPCGetAttr() returns 0x57415250 ('WARP') for PPCINFOTAG_EMULATION
**       (=0x8001f0ff), which allows a program to distinguish the
**       emulation library from the original.
** V0.6a (14.04.1999) phx
**       PPCFindTask() returns PowerUp TaskObject instead WarpOS TaskPPC.
** V0.6  (11.04.1999) phx
**       Real pooled memory support by using WarpOS V4:
**        PPCAllocPooled(),PPCAllocVecPooled(),PPCCreatePool(),
**        PPCDeletePool(),PPCFreePooled(),PPCFreeVecPooled().
**       Misc: PPCRawDoFmt(), PPCFindName(), PPCFindNameSync().
**       Semaphore functions: PPCAttemptSemaphore, PPCAttemptSemaphoreByName,
**        PPCAttemptSemaphoreShared, PPCCreateSemaphore, PPCDeleteSemaphore,
**        PPCObtainSemaphore, PPCObtainSemaphoreByName,
**        PPCObtainSemaphoreShared, PPCReleaseSemaphore.
**       Cache support: PPCCacheFlush, PPCCacheFlushAll, PPCCacheInvalid.
** V0.5d (03.04.1999) phx
**       Ports and messages. Implemented functions of the PowerUp
**       Message System for now:
**        PPCCreatePort(), PPCDeletePort(), PPCObtainPort(), PPCReleasePort(),
**        PPCWaitPort(), PPCCreateMessage(), PPCDeleteMessage(),
**        PPCGetMessage(), PPCGetMessageAttr(), PPCReplyMessage(),
**        PPCSendMessage().
**       PPCGetTaskAttr(): new tags supported:
**        PPCTASKTAG_STARTUP_MSG, PPCTASKTAG_STARTUP_MSGDATA,
**        PPCTASKTAG_STARTUP_MSGLENGTH, PPCTASKTAG_STARTUP_MSGID and
**        PPCTASKTAG_MSGPORT.
**       Use MPSemaphore to access TaskObjects.
** V0.5b (12.02.1999) phx
**       Ext_UserData is stored in task now.
** V0.5a (26.01.1999) phx
**       PPCGetTaskAttr() supported tags: PPCTASKINFOTAG_NAME,
**        PPCTASKINFOTAG_PRIORITY, PPCTASKTAG_STACKSIZE,
**        PPCTASKTAG_EXTUSERDATA, PPCTASKINFOTAG_USEDSTACKSIZE,
**        PPCTASKINFOTAG_INPUTHANDLE, PPCTASKINFOTAG_OUTPUTHANDLE,
**        PPCTASKINFOTAG_ERRORHANDLE, PPCTASKINFOTAG_ELFOBJECT
**       New: PPCSetTaskAttr(). Supported tags: PPCTASKINFOTAG_NAME,
**        PPCTASKINFOTAG_PRIORITY, PPCTASKTAG_EXTUSERDATA,
**        PPCTASKINFOTAG_INPUTHANDLE, PPCTASKINFOTAG_OUTPUTHANDLE,
**        PPCTASKINFOTAG_ERRORHANDLE
** V0.4c (23.01.1999) phx
**       PPCGetAttr() - erroneously implemented as PPCGetAttrs() in 0.4.
**       Fixed PPCGetAttr (ULONG tag instead TagItem pointer).
**       Task support: PPCFindTask(), PPCFinishTask(),
**        PPCGetTaskAttr() - only PPCTASKTAG_STACKSIZE supported.
**       PowerUp ELF library support: PPCOpenLibrary, PPCCloseLibrary,
**        PPCGetLibSymbol.
** V0.4b (05.01.1999) phx
**       Timer Objects: PPCCreateTimerObject, PPCDeleteTimerObject,
**       PPCGetTimerObject, PPCSetTimerObject.
**       Made DPrintf() global.
**       New: PPCInput,PPCOutput.
** V0.4  (26.11.1998) phx
**       PPCGetAttrs.
**       __initbas replaced by __getbase. TOC is always available.
** 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.
**       Supported PowerUp kernel functions:
**        PPCAllocMem,PPCAllocPooled(dummy),PPCAllocVec,
**        PPCAllocVecPooled(dummy),PPCCallM68k,PPCCallOS,PPCClose,
**        PPCCreatePool(dummy),PPCDeletePool(dummy),PPCFreeMem,
**        PPCFreePooled(dummy),PPCFreeVec,PPCFreeVecPooled(dummy),
**        PPCOpen,PPCRead,PPCSeek,PPCWrite
**       Pooled memory functions currently use the normal Alloc/FreeVec
**       functions, because pooled memory support is still missing in WarpOS.
** V0.0  (14.11.1998) phx
**       created
*/

#include "ppclibemu.h"
#include "timerobject.h"
#include "elflibraries.h"
#include "taskobject.h"
#include "msgsystem.h"
#include <intuition/classes.h>
#include <powerup/ppclib/ppc.h>
#include <powerup/ppclib/object.h>
#include <powerup/ppclib/tasks.h>
#include <powerup/ppclib/interface.h>
#include <powerup/ppclib/memory.h>
#include <powerup/ppclib/time.h>
#include <powerup/ppclib/message.h>
#include <powerup/ppclib/semaphore.h>
#include <powerpc/memoryPPC.h>
#include "mpsema_protos.h"
#include "warpos_protos.h"


#define PPCINFOTAG_EMULATION (TAG_USER + 0x1f0ff)

#ifdef KERNEL_DEBUG
static char *entering = "Kernel: entering -> %s()\n";
static char *leaving = "Kernel: leaving  <- %s()\n";
#define KDB(x) x
#define ENTER(f) DPrintf(ppcbase,entering,#f)
#define LEAVE(f) DPrintf(ppcbase,leaving,#f)
void _setr12(unsigned long) = "\tmr\tr12,r3";
#define ASMKERN(x) _setr12((unsigned long)ppcbase); x
#else
#define KDB(x)
#define ENTER(f)
#define LEAVE(f)
#define ASMKERN(x) x
#endif

static char *noname = "";


extern void kernPPCDivu64p(ULONG *,ULONG *);



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

    va_start(vl,fmt);
    __SPrintF(ppcbase->ppc_PowerPCBase,(STRPTR)fmt,(APTR)vl);
    va_end(vl);
  }
}



/****************
  Interface
 ****************/

ULONG kernPPCCallM68k(struct Caos *caos)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCCallM68k);
  _memset(&pa,sizeof(struct PPCArgs),0);
  _memcpy(pa.PP_Regs,&caos->d0,15*sizeof(ULONG));  /* copy d0-d7/a0-a6 */
  pa.PP_Code = caos->caos_Un.Function;

  /* @@@ Cache Flags are ignored! */
  /* @@@ Asynchronous calls made synchronous! */
  KDB(DPrintf(ppcbase,"\tat 0x%08lx\n",caos->caos_Un.Function));
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCCallM68k);
  return(pa.PP_Regs[PPREG_D0]);
}


ULONG kernPPCCallOS(struct Caos *caos)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCCallOS);
  _memset(&pa,sizeof(struct PPCArgs),0);
  _memcpy(pa.PP_Regs,&caos->d0,15*sizeof(ULONG));  /* copy d0-d7/a0-a6 */
  pa.PP_Code = (APTR)caos->a6;
  pa.PP_Offset = caos->caos_Un.Offset;

  /* @@@ Cache Flags are ignored! */
  /* @@@ Asynchronous calls made synchronous! */
  KDB(DPrintf(ppcbase,"\t%s(%ld)\n",
              ((struct Library *)caos->a6)->lib_Node.ln_Name,
              caos->caos_Un.Offset));
  KDB(DPrintf(ppcbase,"\td0-7: %08lx %08lx %08lx %08lx %08lx %08lx "
              "%08lx %08lx\n\ta0-5: %08lx %08lx %08lx %08lx %08lx %08lx\n",
              caos->d0,caos->d1,caos->d2,caos->d3,
              caos->d4,caos->d5,caos->d6,caos->d7,
              caos->a0,caos->a1,caos->a2,caos->a3,caos->a4,caos->a5));
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCCallOS);
  return(pa.PP_Regs[PPREG_D0]);
}


/****************
  PowerPC
 ****************/

ULONG kernPPCGetAttr(ULONG tag)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  double pll;
  ULONG ret = 0;

  ENTER(PPCGetAttr);

  switch (tag) {
    case PPCINFOTAG_CPU:
      ret = ppcbase->ppc_PVR >> 16;
      break;
    case PPCINFOTAG_CPUREV:
      ret = ppcbase->ppc_PVR & 0xffff;
      break;
    case PPCINFOTAG_CPUCOUNT:
      ret = 1;  /* 1 CPU is hard-coded */
      break;
    case PPCINFOTAG_CPUCLOCK:
      ret = (ppcbase->ppc_CPUClock+500000)/1000000;
      break;
    case PPCINFOTAG_CPUPLL:
      ret = ppcbase->ppc_HID1;
      break;
    case PPCINFOTAG_EMULATION:
      ret = 0x57415250;  /* return 'WARP', if in emulation mode */
      break;
    default:
      KDB(DPrintf(ppcbase,"\tUnknown\n"));
      break;
  }
  KDB(DPrintf(ppcbase,"\ttag=0x%08lx ret=0x%08lx\n",tag,ret));

  LEAVE(PPCGetAttr);
  return (ret);
}



/****************
  Memory
 ****************/

void *kernPPCAllocVec(ULONG size,ULONG attr)
{
  struct PPCLibBase *ppcbase = __getbase();
  ULONG wosattr = attr & ~(MEMF_NOCACHESYNCPPC|MEMF_NOCACHESYNCM68K|
                           MEMF_NOCACHEPPC|MEMF_NOCACHEM68K|
                           MEMF_WRITETHROUGHPPC|MEMF_WRITETHROUGHM68K);
  ULONG *p;

  ENTER(PPCAllocVec);
  if (size == 0) {
    KDB(DPrintf(ppcbase,"\tAllocation with size=0 converted to size=1!\n"));
    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 */
  }
  p = (ULONG *)__AllocVecPPC(ppcbase->ppc_PowerPCBase,(size+63)&~31,wosattr,32);
  p[0] = MEMMAGIC;
  p[1] = (ULONG)p;
  p += 8;
  KDB(DPrintf(ppcbase,"\t%ld bytes at 0x%08lx (0x%08lx)\n",size,p,attr));
  LEAVE(PPCAllocVec);
  return (p);
}


void kernPPCFreeVec(ULONG *p)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCFreeVec);
  KDB(DPrintf(ppcbase,"\tat 0x%08lx\n",p));
  if (p) {
    p -= 8;
    if (p[0]==MEMMAGIC && p[1]==(ULONG)p) {
      p[0] = p[1] = 0;
      KDB(DPrintf(ppcbase,"\tat 0x%08lx\n",p+8));
      __FreeVecPPC(ppcbase->ppc_PowerPCBase,p);
    }
    else {
      KDB(DPrintf(ppcbase,"\tIgnored! Memory block at 0x%08lx "
                  "was not allocated by PPCAllocVec()!\n",p+8));
    }
  }
  LEAVE(PPCFreeVec);
}


void *kernPPCCreatePool(ULONG attr,ULONG puddleSize,ULONG threshSize)
{
  struct PPCLibBase *ppcbase = __getbase();
  ULONG wosattr = attr & ~(MEMF_NOCACHESYNCPPC|MEMF_NOCACHESYNCM68K|
                           MEMF_NOCACHEPPC|MEMF_NOCACHEM68K|
                           MEMF_WRITETHROUGHPPC|MEMF_WRITETHROUGHM68K);
  void *p;

  ENTER(PPCCreatePool);
  KDB(DPrintf(ppcbase,"\tattr=0x%08lx, puddleSize=%lu, threshSize=%lu\n",
      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 */
  }
  p = __CreatePoolPPC(ppcbase->ppc_PowerPCBase,wosattr,puddleSize,threshSize);
  KDB(DPrintf(ppcbase,"\tpool=0x%08lx\n",p));
  LEAVE(PPCCreatePool);
  return (p);
}


void kernPPCDeletePool(void *pool)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCDeletePool);
  KDB(DPrintf(ppcbase,"\tat 0x%08lx\n",pool));
  __DeletePoolPPC(ppcbase->ppc_PowerPCBase,pool);
  LEAVE(PPCDeletePool);
}


void *kernPPCAllocVecPooled(void *pool,ULONG size)
{
  struct PPCLibBase *ppcbase = __getbase();
  void *p;

  ENTER(PPCAllocVecPooled);
  if (size == 0) {
    KDB(DPrintf(ppcbase,"\tAllocation with size=0 converted to size=1!\n"));
    size = 1;
  }
  p = __AllocPooledPPC(ppcbase->ppc_PowerPCBase,pool,size);
  KDB(DPrintf(ppcbase,"\t%ld bytes at 0x%08lx\n",size,p));
  LEAVE(PPCAllocVecPooled);
  return (p);
}


void kernPPCFreeVecPooled(void *pool,APTR mem)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCFreeVecPooled);
  KDB(DPrintf(ppcbase,"\tat 0x%08lx\n",mem));
  __FreePooledPPC(ppcbase->ppc_PowerPCBase,pool,mem,0);
  LEAVE(PPCFreeVecPooled);
}


/****************
  I/O
 ****************/

BPTR kernPPCOpen(STRPTR name, LONG mode)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCOpen);
  KDB(DPrintf(ppcbase,"\tname=\"%s\",mode=%ld\n",name,mode));
  pa.PP_Code = (APTR)ppcbase->ppc_DOSBase;
  pa.PP_Offset = -30;  /* _LVOOpen */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_D1] = (ULONG)name;
  pa.PP_Regs[PPREG_D2] = (ULONG)mode;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase->ppc_DOSBase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  KDB(DPrintf(ppcbase,"\tfh=0x%08lx\n",pa.PP_Regs[PPREG_D0]));
  LEAVE(PPCOpen);
  return((BPTR)pa.PP_Regs[PPREG_D0]);
}


BOOL kernPPCClose(BPTR fh)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCClose);
  KDB(DPrintf(ppcbase,"\tfh=0x%08lx\n",fh));
  pa.PP_Code = (APTR)ppcbase->ppc_DOSBase;
  pa.PP_Offset = -36;  /* _LVOClose */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_D1] = (ULONG)fh;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase->ppc_DOSBase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCClose);
  return((BOOL)pa.PP_Regs[PPREG_D0]);
}


LONG kernPPCRead(BPTR fh,void *buf,LONG len)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCRead);
  KDB(DPrintf(ppcbase,"\tfh=0x%08lx: %ld bytes to 0x%08lx\n",fh,len,buf));
  pa.PP_Code = (APTR)ppcbase->ppc_DOSBase;
  pa.PP_Offset = -42;  /* _LVORead */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_D1] = (ULONG)fh;
  pa.PP_Regs[PPREG_D2] = (ULONG)buf;
  pa.PP_Regs[PPREG_D3] = (ULONG)len;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase->ppc_DOSBase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCRead);
  return((LONG)pa.PP_Regs[PPREG_D0]);
}


LONG kernPPCWrite(BPTR fh,void *buf,LONG len)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCWrite);
  KDB(DPrintf(ppcbase,"\tfh=0x%08lx: %ld bytes from 0x%08lx\n",fh,len,buf));
  pa.PP_Code = (APTR)ppcbase->ppc_DOSBase;
  pa.PP_Offset = -48;  /* _LVOWrite */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_D1] = (ULONG)fh;
  pa.PP_Regs[PPREG_D2] = (ULONG)buf;
  pa.PP_Regs[PPREG_D3] = (ULONG)len;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase->ppc_DOSBase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCWrite);
  return((LONG)pa.PP_Regs[PPREG_D0]);
}


LONG kernPPCSeek(BPTR fh,LONG pos,LONG mode)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCSeek);
  KDB(DPrintf(ppcbase,"\tfh=0x%08lx, pos=%ld, mode=%ld\n",fh,pos,mode));
  pa.PP_Code = (APTR)ppcbase->ppc_DOSBase;
  pa.PP_Offset = -66;  /* _LVOSeek */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_D1] = (ULONG)fh;
  pa.PP_Regs[PPREG_D2] = (ULONG)pos;
  pa.PP_Regs[PPREG_D3] = (ULONG)mode;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase->ppc_DOSBase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCSeek);
  return((LONG)pa.PP_Regs[PPREG_D0]);
}


BPTR kernPPCInput(void)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCInput);
  pa.PP_Code = (APTR)ppcbase->ppc_DOSBase;
  pa.PP_Offset = -54;  /* _LVOInput */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase->ppc_DOSBase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  KDB(DPrintf(ppcbase,"\t0x%08lx\n",pa.PP_Regs[PPREG_D0]));
  LEAVE(PPCInput);
  return((BPTR)pa.PP_Regs[PPREG_D0]);
}


BPTR kernPPCOutput(void)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCOutput);
  pa.PP_Code = (APTR)ppcbase->ppc_DOSBase;
  pa.PP_Offset = -60;  /* _LVOOutput */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase->ppc_DOSBase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  KDB(DPrintf(ppcbase,"\t0x%08lx\n",pa.PP_Regs[PPREG_D0]));
  LEAVE(PPCOutput);
  return((BPTR)pa.PP_Regs[PPREG_D0]);
}


APTR kernPPCRawDoFmt(STRPTR fmtString,APTR datStream,
                     void (*putChProc)(),APTR putChData)
{
  struct PPCLibBase *ppcbase = __getbase();
  APTR *next;

  ENTER(PPCRawDoFmt);
  KDB(DPrintf(ppcbase,"\tfmt=\"%s\" dat=0x%08lx\n",fmtString,datStream));
  next = __RawDoFmtPPC(ppcbase->ppc_PowerPCBase,fmtString,datStream,
                       putChProc,putChData);
  LEAVE(PPCRawDoFmt);
  return (next);
}


/*
 * @@@ WARNING! This is only an ugly hack to make PPCprintf()
 * work at all. The varargs are expected in PowerOpen format.
 * The varargs ABI-conversion is done by a stub routine in miscppc.s.
 * The resulting strings are limited to 1024 bytes!
 * The original PPCprintf() implementation doesn't seem to use
 * PPCRawDoFmt() as it also supports long long and float arguments.
 */
static __saveds char **putChBuf(char **bp,char c)
{
  char *cp = *bp;

  *cp++ = c;
  *bp = cp;
  return (bp);
}

int kernPPCprintfPO(const char *fmtString,void *valist)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  struct TaskPPC *task = __FindTaskPPC(wosbase,NULL);
  struct TaskObject *to;
  struct PPCArgs pa;
  char buf[1024];
  char *bufptr = buf;
  int len;

  ENTER(PPCprintf);
  __RawDoFmtPPC(wosbase,(STRPTR)fmtString,(APTR)valist,
                (void (*)())putChBuf,&bufptr);
  len = (int)(bufptr-buf);
  KDB(DPrintf(ppcbase,"\t%fmtString=\"%s\" extended to %lu bytes.\n",
              fmtString,len));
  to = (struct TaskObject *)(task->tp_Task.tc_UserData);
  pa.PP_Code = (APTR)ppcbase->ppc_DOSBase;
  pa.PP_Offset = -48;  /* _LVOWrite */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_D1] = (ULONG)to->outputhandle;
  pa.PP_Regs[PPREG_D2] = (ULONG)buf;
  pa.PP_Regs[PPREG_D3] = (ULONG)len;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase->ppc_DOSBase;
  __Run68K(wosbase,&pa);  /* write string to stdout */
  LEAVE(PPCprintf);
  return (len);
}


/****************
      Tasks
 ****************/

struct TaskObject *kernPPCCreateTask(struct ElfObject *elfobj, void *pc,
                                     struct TagItem *ti)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;
  struct PPCThread thrd = { 0,0,0 };

  ENTER(PPCCreateTask);
  if (pc) {
    /* spawn a new thread from current object */
    struct TaskPPC *task = __FindTaskPPC(ppcbase->ppc_PowerPCBase,NULL);

    KDB(DPrintf(ppcbase,"\tnew thread: pc=0x%08lx\n",pc));
    thrd.magicID = THREAD_MAGIC;
    thrd.parentTask = (struct TaskObject *)(task->tp_Task.tc_UserData);
    thrd.pc = (APTR)pc;
    elfobj = (struct ElfObject *)THREAD_MAGIC;
  }
  pa.PP_Code = (APTR)ppcbase;
  pa.PP_Offset = -84;  /* _LVOPPCCreateTask */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_A0] = (ULONG)elfobj;
  pa.PP_Regs[PPREG_A1] = (ULONG)ti;
  pa.PP_Regs[PPREG_A2] = (ULONG)&thrd;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCCreateTask);
  return ((struct TaskObject *)pa.PP_Regs[PPREG_D0]);
}


ULONG kernPPCGetTaskAttr(ULONG tag)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  struct TaskPPC *task;
  struct TaskObject *to;
  ULONG ret;

  ENTER(PPCGetTaskAttr);
  KDB(DPrintf(ppcbase,"\tTag = 0x%08lx\n",tag));
  task = __FindTaskPPC(wosbase,NULL);
  to = (struct TaskObject *)(task->tp_Task.tc_UserData);
  obtainMPSemaphorePPC(ppcbase,&to->mpSema);

  switch (tag) {
    case PPCTASKTAG_NAME:
    case PPCTASKINFOTAG_NAME:
      ret = (ULONG)to->n.ln_Name;
      break;

    case PPCTASKTAG_PRIORITY:
    case PPCTASKINFOTAG_PRIORITY:
      ret = (ULONG)((long)to->n.ln_Pri);
      break;

    case PPCTASKTAG_STACKSIZE:
    case PPCTASKINFOTAG_STACKSIZE:
      ret = task->tp_StackSize;
      break;

    case PPCTASKTAG_EXTUSERDATA:
      ret = to->Ext_UserData;
      break;

    case PPCTASKINFOTAG_USEDSTACKSIZE:
      ret = (ULONG)to->initialstack - (ULONG)__get_r1();
      break;

    case PPCTASKTAG_INPUTHANDLE:
    case PPCTASKINFOTAG_INPUTHANDLE:
      ret = (ULONG)to->inputhandle;
      break;

    case PPCTASKTAG_OUTPUTHANDLE:
    case PPCTASKINFOTAG_OUTPUTHANDLE:
      ret = (ULONG)to->outputhandle;
      break;

    case PPCTASKTAG_ERRORHANDLE:
    case PPCTASKINFOTAG_ERRORHANDLE:
      ret = (ULONG)to->errorhandle;
      break;

    case PPCTASKTAG_ELFOBJECT:
    case PPCTASKINFOTAG_ELFOBJECT:
      ret = (ULONG)to->elf;
      break;

    case PPCTASKTAG_STARTUP_MSG:
    case PPCTASKINFOTAG_STARTUP_MSG:
      ret = (ULONG)to->startup;
      break;

    case PPCTASKTAG_STARTUP_MSGDATA:
    case PPCTASKINFOTAG_STARTUP_MSGDATA:
      ret = (ULONG)to->msg_data;
      break;

    case PPCTASKTAG_STARTUP_MSGLENGTH:
    case PPCTASKINFOTAG_STARTUP_MSGLENGTH:
      ret = to->msg_length;
      break;

    case PPCTASKTAG_STARTUP_MSGID:
    case PPCTASKINFOTAG_STARTUP_MSGID:
      ret = to->msg_id;
      break;

    case PPCTASKTAG_MSGPORT:
    case PPCTASKINFOTAG_MSGPORT:
      ret = (ULONG)to->ppc_port;
      break;

    default:
      KDB(DPrintf(ppcbase,"\tTag 0x%08lx not implemented\n",tag));
      ret = 0;
      break;
  }

  releaseMPSemaphorePPC(ppcbase,&to->mpSema);
  LEAVE(PPCGetTaskAttr);
  return (ret);
}


void kernPPCSetTaskAttr(ULONG tag,ULONG data)
{
  extern BOOL ppctaskattr(struct PPCLibBase *,struct TaskObject *,
                          ULONG,ULONG,ULONG *,BOOL);
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCSetTaskAttr);
  KDB(DPrintf(ppcbase,"\tTag = 0x%08lx, Data = 0x%08lx\n",tag,data));
  ppctaskattr(ppcbase,(struct TaskObject *)
              (__FindTaskPPC(ppcbase->ppc_PowerPCBase,NULL)->tp_Task.tc_UserData),
              tag,data,NULL,TRUE);
  LEAVE(PPCSetTaskAttr);
}


struct TaskObject *kernPPCFindTask(char *name)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct TaskPPC *task;

  ENTER(PPCFindTask);
  KDB(DPrintf(ppcbase,"\tname=\"%s\"\n",name ? name : noname));
  task = __FindTaskPPC(ppcbase->ppc_PowerPCBase,name);
  LEAVE(PPCFindTask);
  return (task ? (struct TaskObject *)task->tp_Task.tc_UserData : NULL);
}


void kernPPCFinishTask(ULONG result)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct TaskPPC *task;
  struct TaskObject *to;
  void (*exitfunc)(ULONG);
  void *p;

  ENTER(PPCFinishTask);
  task = __FindTaskPPC(ppcbase->ppc_PowerPCBase,NULL);
  to = (struct TaskObject *)(task->tp_Task.tc_UserData);
  obtainMPSemaphorePPC(ppcbase,&to->mpSema);
  exitfunc = to->exit_addr;
  releaseMPSemaphorePPC(ppcbase,&to->mpSema);
  KDB(DPrintf(ppcbase,"\tJumping to exit at 0x%08lx\n",exitfunc));
  __set_r2(to);
  exitfunc(result);
}


/****************
  Timer Objects
 ****************/

struct PPCTimerObject *kernPPCCreateTimerObject(struct TagItem *ti)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  struct TagItem *tag;
  struct PPCTimerObject *to = NULL;
  ULONG *err;

  ENTER(PPCCreateTimerObject);
  err = (ULONG *)__GetTagDataPPC(wosbase,PPCTIMERTAG_ERROR,0,ti);

  if (tag = __FindTagItemPPC(wosbase,PPCTIMERTAG_50HZ,ti)) {
    /* create a 50Hz notification timer */
    KDB(DPrintf(ppcbase,"\t50Hz notification timer\n"));
    if (to = CreateTimObj(wosbase,tag->ti_Data,err)) {
      to->NotifMask = __GetTagDataPPC(wosbase,PPCTIMERTAG_SIGNALMASK,0,ti);
      if (__GetTagDataPPC(wosbase,PPCTIMERTAG_AUTOREMOVE,FALSE,ti))
        to->to_Flags |= TOF_AutoRemove;

      /* tell TimerObject server to enqueue the new object */
      if (!SendTOMsg(wosbase,to,TOCMD_ADD)) {
        DeleteTimObj(wosbase,to);
        to = NULL;
      }
    }
  }

  else if (tag = __FindTagItemPPC(wosbase,PPCTIMERTAG_CPU,ti)) {
    KDB(DPrintf(ppcbase,"\tCPU timer\n"));
    if (tag->ti_Data)
      to = CreateTimObj(wosbase,0,err);
  }

  KDB(DPrintf(ppcbase,"\ttimobj=0x%08lx\n",to));
  LEAVE(PPCCreateTimerObject);
  return (to);
}


void kernPPCDeleteTimerObject(struct PPCTimerObject *to)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCDeleteTimerObject);
  KDB(DPrintf(ppcbase,"\ttimobj=0x%08lx\n",to));
  DeleteTimObj(ppcbase->ppc_PowerPCBase,to);
  LEAVE(PPCDeleteTimerObject);
}


void kernPPCSetTimerObject(struct PPCTimerObject *to,ULONG tag,ULONG *tim)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;

  ENTER(PPCSetTimerObject);
  KDB(DPrintf(ppcbase,"\ttimobj=0x%08lx tag=0x%08lx\n",to,tag));

  if (to->to_Flags & TOF_50Hz) {
    /* 50Hz notification timer */
    switch (tag) {
      case PPCTIMERTAG_START:
        SendTOMsg(wosbase,to,TOCMD_ADD);  /* reactivate timer job */
        break;
      case PPCTIMERTAG_STOP:
        SendTOMsg(wosbase,to,TOCMD_REM);  /* remove timer job */
        break;
    }
  }

  else {
    /* synchronous CPU timer */
    switch (tag) {
      case PPCTIMERTAG_START:
        GetPPCTime(wosbase,to->StartTime);
        break;
      case PPCTIMERTAG_STOP:
        GetPPCTime(wosbase,to->StopTime);
        break;
    }
  }

  LEAVE(PPCSetTimerObject);
}


void kernPPCGetTimerObject(struct PPCTimerObject *to,ULONG tag,ULONG *tim)
{
  struct PPCLibBase *ppcbase = __getbase();
  ULONG d[2];

  ENTER(PPCGetTimerObject);
  KDB(DPrintf(ppcbase,"\ttimobj=0x%08lx tag=0x%08lx\n",to,tag));

  switch(tag) {

	  case PPCTIMERTAG_TICKSPERSEC:
      tim[0] = 0;
      tim[1] = TO_1S;
      break;

	  case PPCTIMERTAG_START:
      tim[0] = to->StartTime[0];
      tim[1] = to->StartTime[1];
      break;

	  case PPCTIMERTAG_STOP:
      tim[0] = to->StopTime[0];
      tim[1] = to->StopTime[1];
      break;

	  case PPCTIMERTAG_CURRENTTICKS:
      GetPPCTime(ppcbase->ppc_PowerPCBase,tim);
      break;

	  case PPCTIMERTAG_DIFFTICKS:
	  case PPCTIMERTAG_DIFFMICRO:
      GetDiffTime(to,tim);
      break;

	  case PPCTIMERTAG_DIFFSECS:
      GetDiffTime(to,tim);
      d[0] = 0;
      d[1] = TO_1S;
      ASMKERN(kernPPCDivu64p(tim,d));
      break;

    case PPCTIMERTAG_DIFFMINS:
      GetDiffTime(to,tim);
      d[0] = 0;
      d[1] = TO_1S;
      ASMKERN(kernPPCDivu64p(tim,d));
      d[1] = 60;
      ASMKERN(kernPPCDivu64p(tim,d));
      break;

	  case PPCTIMERTAG_DIFFHOURS:
      GetDiffTime(to,tim);
      d[0] = 0;
      d[1] = TO_1S;
      ASMKERN(kernPPCDivu64p(tim,d));
      d[1] = 60*60;
      ASMKERN(kernPPCDivu64p(tim,d));
      break;

	  case PPCTIMERTAG_DIFFDAYS:
      GetDiffTime(to,tim);
      d[0] = 0;
      d[1] = TO_1S;
      ASMKERN(kernPPCDivu64p(tim,d));
      d[1] = 60*60*24;
      ASMKERN(kernPPCDivu64p(tim,d));
      break;
  }

  LEAVE(PPCGetTimerObject);
}


/****************
  ELF Objects
 ****************/

struct ElfObject *kernPPCLoadObjectTagList(struct TagItem *ti)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCLoadObjectTagList);
  KDB(DPrintf(ppcbase,"\ttags=0x%08lx\n",ti));
  pa.PP_Code = (APTR)ppcbase;
  pa.PP_Offset = -408;  /* _LVOPPCLoadObjectTagList */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_A0] = (ULONG)ti;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCLoadObjectTagList);
  return ((struct ElfObject *)pa.PP_Regs[PPREG_D0]);
}


struct ElfObject *kernPPCLoadObject(char *name)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCLoadObject);
  KDB(DPrintf(ppcbase,"\tname=\"%s\"\n",name));
  pa.PP_Code = (APTR)ppcbase;
  pa.PP_Offset = -30;  /* _LVOPPCLoadObject */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_A0] = (ULONG)name;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCLoadObject);
  return ((struct ElfObject *)pa.PP_Regs[PPREG_D0]);
}


void kernPPCUnLoadObject(struct ElfObject *elfobj)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCUnLoadObject);
  pa.PP_Code = (APTR)ppcbase;
  pa.PP_Offset = -36;  /* _LVOPPCUnLoadObject */
  pa.PP_Flags = pa.PP_StackSize = 0;
  pa.PP_Stack = NULL;
  pa.PP_Regs[PPREG_A0] = (ULONG)elfobj;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCUnLoadObject);
}


/****************
  ELF Libraries
 ****************/

struct ElfLibrary *kernPPCOpenLibrary(char *libname,struct TagItem *ti)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  struct ElfLibrary *lib;

  ENTER(PPCOpenLibrary);

  if (!(lib = FindELFLib(ppcbase,libname)))
    lib = LoadELFLib(ppcbase,libname);

  if (lib) {
    struct TagItem *tag;
    struct ElfLibrary *(*libopen)(struct ElfLibrary *);

    if (tag = __FindTagItemPPC(wosbase,PPCELFLOADTAG_LIBVERSION,ti))
      if ((ULONG)lib->version < tag->ti_Data)
        return (NULL);
    if (tag = __FindTagItemPPC(wosbase,PPCELFLOADTAG_LIBREVISION,ti))
      if ((ULONG)lib->revision < tag->ti_Data)
        return (NULL);
    if (tag = __FindTagItemPPC(wosbase,PPCELFLOADTAG_LIBEXACTVERSION,ti))
      if ((ULONG)lib->version != tag->ti_Data)
        return (NULL);
    if (tag = __FindTagItemPPC(wosbase,PPCELFLOADTAG_LIBEXACTREVISION,ti))
      if ((ULONG)lib->revision != tag->ti_Data)
        return (NULL);

    if (libopen = GetELFSym(lib->elf,"__LIB_Open"))
      if (libopen(lib) != lib)
        return (NULL);

    lib->open_cnt++;  /* All checks passed, library opened */

    KDB(DPrintf(ppcbase,"\tOpened ELF-library %s (0x%08lx)\n",libname,lib));
  }

  LEAVE(PPCOpenLibrary);
  return (lib);
}


void kernPPCCloseLibrary(struct ElfLibrary *lib)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct ElfLibrary *(*libclose)(struct ElfLibrary *);

  ENTER(PPCCloseLibrary);
  KDB(DPrintf(ppcbase,"\t0x%08lx\n",lib));
  if (lib) {
    if (libclose = GetELFSym(lib->elf,"__LIB_Close"))
      if (libclose(lib) != lib)
        return;
    if (--lib->open_cnt == 0) {
      KDB(DPrintf(ppcbase,"\tExpunging ELF-library %s\n",lib->n.ln_Name));
      RemoveELFLib(ppcbase,lib);
    }
  }
  LEAVE(PPCCloseLibrary);
}


void *kernPPCGetLibSymbol(struct ElfLibrary *lib,char *symname)
{
  struct PPCLibBase *ppcbase = __getbase();
  void *p;

  ENTER(PPCGetLibSymbol);
  p = GetELFSym(lib->elf,symname);
  KDB(DPrintf(ppcbase,"\t%s = 0x%08lx\n",symname,p));
  LEAVE(PPCGetLibSymbol);
  return (p);
}


/****************
  Ports
 ****************/

struct PPCMsgPort *kernPPCCreatePort(struct TagItem *ti)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCMsgPort *mp;

  ENTER(PPCCreatePort);
  mp = createPPCMsgPort(ppcbase,
                        (char *)__GetTagDataPPC(ppcbase->ppc_PowerPCBase,
                                                PPCPORTTAG_NAME,NULL,ti),
                        (ULONG *)__GetTagDataPPC(ppcbase->ppc_PowerPCBase,
                                                 PPCPORTTAG_ERROR,NULL,ti));
  KDB(DPrintf(ppcbase,"\t0x%08lx (%s)\n",(unsigned long)mp,
              mp ? (mp->mp.mp_Node.ln_Name?mp->mp.mp_Node.ln_Name:noname) : noname));
  LEAVE(PPCCreatePort);
  return (mp);
}


BOOL kernPPCDeletePort(struct PPCMsgPort *mp)
{
  struct PPCLibBase *ppcbase = __getbase();
  BOOL rc;

  ENTER(PPCDeletePort);
  rc = deletePPCMsgPort(ppcbase,mp);
  KDB(DPrintf(ppcbase,"\t%s\n",rc?"successful":"failed"));
  LEAVE(PPCDeletePort);
  return (rc);
}


struct PPCMsgPort *kernPPCObtainPort(struct TagItem *ti)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCMsgPort *mp;
  char *name;
  ULONG *errptr;

  ENTER(PPCObtainPort);
  name = (char *)__GetTagDataPPC(ppcbase->ppc_PowerPCBase,
                                 PPCPORTTAG_NAME,NULL,ti);
  errptr = (ULONG *)__GetTagDataPPC(ppcbase->ppc_PowerPCBase,
                                    PPCPORTTAG_ERROR,NULL,ti);
  KDB(DPrintf(ppcbase,"\tname = \"%s\"\n",name?name:noname));
  mp = obtainPPCMsgPort(ppcbase,name);
  if (errptr)
    *errptr = mp ? PPCPORTERROR_OK : PPCPORTERROR_NOTFOUND;
  KDB(DPrintf(ppcbase,"\tport = 0x%08lx\n",(unsigned long)mp));
  LEAVE(PPCObtainPort);
  return (mp);
}


BOOL kernPPCReleasePort(struct PPCMsgPort *mp)
{
  struct PPCLibBase *ppcbase = __getbase();
  BOOL rc;

  ENTER(PPCReleasePort);
  rc = releasePPCMsgPort(ppcbase,mp);
  LEAVE(PPCReleasePort);
  return (rc);
}


struct PPCMessage *kernPPCWaitPort(struct PPCMsgPort *mp)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCMessage *m;

  ENTER(PPCWaitPort);
  if (mp) {
    KDB(DPrintf(ppcbase,"\twaiting for port 0x%08lx (%s)...\n",
                (unsigned long)mp,
                mp->mp.mp_Node.ln_Name?mp->mp.mp_Node.ln_Name:noname));
    m = waitPPCMessage(ppcbase,mp);
    KDB(DPrintf(ppcbase,"\tmsg = 0x%08lx\n",(unsigned long)m));
  }
  else {
    KDB(DPrintf(ppcbase,"\tMsgPort to wait for = NULL!\n"));
    m = NULL;
  }
  LEAVE(PPCWaitPort);
  return (m);
}


/****************
  Messages
 ****************/

struct PPCMessage *kernPPCCreateMessage(struct PPCMsgPort *reply,ULONG len)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCMessage *m;

  ENTER(PPCCreateMessage);
  if (m = __AllocVecPPC(ppcbase->ppc_PowerPCBase,
                        (sizeof(struct PPCMessage)+31)&~31,
                        MEMF_CLEAR|MEMF_CHIP|MEMF_PUBLIC,32)) {
    m->n.ln_Type = NT_FREEMSG;
    m->replyPort = reply;
    m->maxdatasize = len;
  }
  KDB(DPrintf(ppcbase,"\tmsg = 0x%08lx\n",(unsigned long)m));
  LEAVE(PPCCreateMessage);
  return (m);
}


void kernPPCDeleteMessage(struct PPCMessage *m)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCDeleteMessage);
  /* only delete if valid pointer and message is replied or free */
  if (m) {
    if (m->n.ln_Type != NT_MESSAGE) {
      m->n.ln_Type = (UBYTE)-1;
      m->replyPort = (struct PPCMsgPort *)-1;
      __FreeVecPPC(ppcbase->ppc_PowerPCBase,m);
      KDB(DPrintf(ppcbase,"\tdeleted\n"));
    }
  }
  LEAVE(PPCDeleteMessage);
}


ULONG kernPPCGetMessageAttr(struct PPCMessage *m,ULONG tag)
{
  struct PPCLibBase *ppcbase = __getbase();
  ULONG attr;

  ENTER(PPCGetMessageAttr);
  switch (tag) {
    case PPCMSGTAG_DATA:
      attr = (ULONG)m->data;
      break;
    case PPCMSGTAG_DATALENGTH:
      attr = m->datasize;
      break;
    case PPCMSGTAG_MSGID:
      attr = m->msgid;
      break;
    default:
      KDB(DPrintf(ppcbase,"\tillegal tag: 0x%08lx\n",tag));
      attr = 0;
      break;
  }
  LEAVE(PPCGetMessageAttr);
  return (attr);
}


struct PPCMessage *kernPPCGetMessage(struct PPCMsgPort *mp)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCMessage *m;

  ENTER(PPCGetMessage);
  if (mp) {
    m = getPPCMessage(ppcbase,mp);
    KDB(DPrintf(ppcbase,"\tmsg = 0x%08lx\n",(unsigned long)m));
  }
  else {
    m = NULL;
    KDB(DPrintf(ppcbase,"\tCan't get! MsgPort = NULL!\n"));
  }
  LEAVE(PPCGetMessage);
  return (m);
}


BOOL kernPPCSendMessage(struct PPCMsgPort *mp,struct PPCMessage *m,
                        void *data,ULONG datasize,ULONG msgid)
{
  struct PPCLibBase *ppcbase = __getbase();
  BOOL rc = FALSE;

  ENTER(PPCSendMessage);
  if (mp!=NULL && m!=NULL) {
    KDB(DPrintf(ppcbase,"\tSending msg=0x%08lx (data=0x%08lx, len=%lu "
                        "id=0x%08lx) to MsgPort=0x%08lx (%s)\n",
                (unsigned long)m,data,datasize,msgid,
                (unsigned long)mp,mp->mp.mp_Node.ln_Name?mp->mp.mp_Node.ln_Name:noname));
    m->data = data;
    m->datasize = datasize;
    m->msgid = msgid;
    rc = putPPCMessage(ppcbase,mp,m);
  }
  LEAVE(PPCSendMessage);
  return (rc);
}


BOOL kernPPCReplyMessage(struct PPCMessage *m)
{
  struct PPCLibBase *ppcbase = __getbase();
  BOOL rc = FALSE;

  ENTER(PPCReplyMessage);
  if (m) {
    KDB(DPrintf(ppcbase,"\treplying msg=0x%08lx (reply port=0x%08lx)\n",
                (unsigned long)m,(unsigned long)m->replyPort));
    rc = replyPPCMessage(ppcbase,m);
  }
  LEAVE(PPCReplyMessage);
  return (rc);
}


/****************
  MsgPort Lists
 ****************/

struct PPCPortList *kernPPCCreatePortList(struct PPCMsgPort **ports,
                                          ULONG extsigmask)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCMsgPort *p;
  struct PPCPortList *mplist;
  ULONG cnt = 0;

  ENTER(PPCCreatePortList);
  if (mplist  = __AllocVecPPC(ppcbase->ppc_PowerPCBase,
                              (sizeof(struct PPCPortList)+31)&~31,
                              MEMF_CLEAR|MEMF_PUBLIC,32)) {
    KDB(DPrintf(ppcbase,"\tinitializing new PortList\n"));
    _newlist(&mplist->portlist);
    mplist->signal_mask = mplist->extended_signals = extsigmask;

    while (p = *ports++) {
      cnt++;
      p->pln.ln_Name = (char *)p; /* ln_Name points to port's base addr */
      _addtail(&mplist->portlist,&p->pln);
      mplist->signal_mask |= (1L<<(ULONG)p->mp.mp_SigBit);
    }
    KDB(DPrintf(ppcbase,"\tadded %ld PPCMsgPorts\n",cnt));
  }

  KDB(DPrintf(ppcbase,"\tPortList=0x%08lx\n",(ULONG)mplist));
  LEAVE(PPCCreatePortList);
  return (mplist);
}


BOOL kernPPCDeletePortList(struct PPCPortList *plist)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct Node *n;
  struct PPCMsgPort *p;

  ENTER(PPCDeletePortList);
  KDB(DPrintf(ppcbase,"\t0x%08lx\n",(ULONG)plist));
  if (plist) {
    while (n = _remhead(&plist->portlist)) {
      p = (struct PPCMsgPort *)n->ln_Name;
      n->ln_Pred = n->ln_Succ = (struct Node *)-1;
    }
    plist->portlist.lh_Head = plist->portlist.lh_Tail =
                              plist->portlist.lh_TailPred = (struct Node *)-1;
    __FreeVecPPC(ppcbase->ppc_PowerPCBase,plist);
    return (TRUE);
  }
  LEAVE(PPCDeletePortList);
  return (FALSE);
}


BOOL kernPPCAddPortList(struct PPCPortList *plist,struct PPCMsgPort *mp)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCAddPortList);
  KDB(DPrintf(ppcbase,"\tport=0x%08lx to list=0x%08lx\n",
              (ULONG)mp,(ULONG)plist));
  if (plist) {
    mp->pln.ln_Name = (char *)mp; /* ln_Name points to port's base addr */
    _addtail(&plist->portlist,&mp->pln);
    plist->signal_mask |= (1L<<(ULONG)mp->mp.mp_SigBit);
    return (TRUE);
  }
  LEAVE(PPCAddPortList);
  return (FALSE);
}


void kernPPCRemPortList(struct PPCPortList *plist,struct PPCMsgPort *mp)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCRemPortList);
  KDB(DPrintf(ppcbase,"\tport=0x%08lx from list=0x%08lx\n",
              (ULONG)mp,(ULONG)plist));
  _remove(&mp->pln);
  mp->pln.ln_Pred = mp->pln.ln_Succ = (struct Node *)-1;
  plist->signal_mask &= ~(1L<<(ULONG)mp->mp.mp_SigBit);
  LEAVE(PPCRemPortList);
}


ULONG kernPPCGetPortListAttr(struct PPCPortList *plist,ULONG attr)
{
  struct PPCLibBase *ppcbase = __getbase();
  ULONG m = 0;

  ENTER(PPCGetPortListAttr);
  if (plist) {
    switch (attr) {
      case PPCPORTLISTTAG_EXTENDEDSIGNALS:
        m = plist->extended_signals;
        break;
      case PPCPORTLISTTAG_RECEIVEDSIGNALS:
        m = plist->received_signals;
        break;
      default:
        KDB(DPrintf(ppcbase,"\tinvalid attr 0x%08lx!\n",attr));
        break;
    }
    KDB(DPrintf(ppcbase,"\tattr=0x%08lx -> 0x%08lx\n",attr,m));
  }
  else
    KDB(DPrintf(ppcbase,"\tPortList=NULL!\n"));
  LEAVE(PPCGetPortListAttr);
  return (m);
}


void kernPPCSetPortListAttr(struct PPCPortList *plist,ULONG attr,ULONG val)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCSetPortListAttr);
  if (plist) {
    if (attr == PPCPORTLISTTAG_EXTENDEDSIGNALS) {
      plist->signal_mask &= ~plist->extended_signals;
      plist->extended_signals = val;
      plist->signal_mask |= val;
      KDB(DPrintf(ppcbase,"\t0x%08lx,0x%08lx\n",attr,val));
    }
    else
      KDB(DPrintf(ppcbase,"\tinvalid attr 0x%08lx!\n",attr));
  }
  else
    KDB(DPrintf(ppcbase,"\tPortList=NULL!\n"));
  LEAVE(PPCSetPortListAttr);
}


struct PPCMsgPort *kernPPCWaitPortList(struct PPCPortList *plist)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCMsgPort *mp_rdy = NULL;

  ENTER(PPCWaitPortList);
  if (plist) {
    KDB(DPrintf(ppcbase,"\twaiting for port list 0x%08lx...\n",(ULONG)plist));
    mp_rdy = waitPortList(ppcbase,plist);
  }
  else
    KDB(DPrintf(ppcbase,"\tPortList=NULL!\n"));

  LEAVE(PPCWaitPortList);
  return (mp_rdy);
}


/****************
  Semaphores
 ****************/

struct SignalSemaphorePPC *kernPPCCreateSemaphore(struct TagItem *ti)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCBase *wosbase = ppcbase->ppc_PowerPCBase;
  struct SignalSemaphorePPC *ss;
  char *name;
  ULONG *errptr;

  ENTER(PPCCreateSemaphore);
  name = (char *)__GetTagDataPPC(wosbase,PPCSEMAPHORETAG_NAME,NULL,ti);
  if (errptr = (ULONG *)__GetTagDataPPC(wosbase,PPCSEMAPHORETAG_ERROR,
                                        NULL,ti))
    *errptr = PPCSEMAPHOREERROR_MEMORY;

  if (ss = __AllocVecPPC(wosbase,sizeof(struct SignalSemaphorePPC),
                         MEMF_CLEAR|MEMF_PUBLIC,0)) {
    LONG status = SSPPC_NOMEM;

    ss->ssppc_SS.ss_Link.ln_Type = NT_SIGNALSEM;
    if (ss->ssppc_SS.ss_Link.ln_Name = name) {
      if (__FindSemaphorePPC(wosbase,(STRPTR)name)) {
        if (errptr)
          *errptr = PPCSEMAPHOREERROR_ALREDYEXISTS;
      }
      else
        status = __AddSemaphorePPC(wosbase,ss);
    }
    else
      status = __InitSemaphorePPC(wosbase,ss);

    if (status == SSPPC_SUCCESS) {
      KDB(DPrintf(ppcbase,"\t0x%08lx (%s)\n",ss,name?name:noname));
      if (errptr)
        *errptr = PPCSEMAPHOREERROR_OK;
    }
    else {
      __FreeVecPPC(wosbase,ss);
      ss = NULL;
    }
  }

  LEAVE(PPCCreateSemaphore);
  return (ss);
}


void kernPPCDeleteSemaphore(struct SignalSemaphorePPC *ss)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCDeleteSemaphore);
  if (ss->ssppc_SS.ss_Link.ln_Type == NT_SIGNALSEM) {
    KDB(DPrintf(ppcbase,"\t0x%08lx (%s)\n",ss,
        ss->ssppc_SS.ss_Link.ln_Name ? ss->ssppc_SS.ss_Link.ln_Name : noname));
    if (ss->ssppc_SS.ss_Link.ln_Name)
      __RemSemaphorePPC(ppcbase->ppc_PowerPCBase,ss);
    else
      __FreeSemaphorePPC(ppcbase->ppc_PowerPCBase,ss);
  }
  else {
    KDB(DPrintf(ppcbase,"\t0x%08lx is NO SignalSemaphore!\n",ss));
  }
  LEAVE(PPCDeleteSemaphore);
}


LONG kernPPCAttemptSemaphore(struct SignalSemaphorePPC *ss)
{
  struct PPCLibBase *ppcbase = __getbase();
  LONG ret;

  ENTER(PPCAttemptSemaphore);
  if (ss->ssppc_SS.ss_Link.ln_Type == NT_SIGNALSEM) {
    ret = __AttemptSemaphorePPC(ppcbase->ppc_PowerPCBase,ss) == ATTEMPT_SUCCESS;
    KDB(DPrintf(ppcbase,"\t0x%08lx rc=%d\n",ss,ret));
  }
  else {
    KDB(DPrintf(ppcbase,"\t0x%08lx is NO SignalSemaphore!\n",ss));
  }
  LEAVE(PPCAttemptSemaphore);
  return (ret);
}


LONG kernPPCAttemptSemaphoreByName(char *name,ULONG *errptr)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct SignalSemaphorePPC *ss;
  LONG ret = FALSE;

  ENTER(PPCAttemptSemaphoreByName);
  if (name) {
    KDB(DPrintf(ppcbase,"\tname=\"%s\"\n",name));
    if (ss = __FindSemaphorePPC(ppcbase->ppc_PowerPCBase,(STRPTR)name)) {
      if (__AttemptSemaphorePPC(ppcbase->ppc_PowerPCBase,ss) == ATTEMPT_SUCCESS) {
        ret = TRUE;
      }
      else {
        if (errptr)
          *errptr = PPCSEMAPHOREERROR_OBTAINED;
      }
    }
    else {
      if (errptr)
        *errptr = PPCSEMAPHOREERROR_NOTFOUND;
    }
  }
  else {
    if (errptr)
      *errptr = PPCSEMAPHOREERROR_NONAME;
  }
  LEAVE(PPCAttemptSemaphoreByName);
  return (ret);
}


LONG kernPPCAttemptSemaphoreShared(struct SignalSemaphorePPC *ss)
{
  struct PPCLibBase *ppcbase = __getbase();
  LONG ret;

  ENTER(PPCAttemptSemaphoreShared);
  if (ss->ssppc_SS.ss_Link.ln_Type == NT_SIGNALSEM) {
    ret = __AttemptSemaphoreSharedPPC(ppcbase->ppc_PowerPCBase,ss) == ATTEMPT_SUCCESS;
    KDB(DPrintf(ppcbase,"\t0x%08lx rc=%d\n",ss,ret));
  }
  else {
    KDB(DPrintf(ppcbase,"\t0x%08lx is NO SignalSemaphore!\n",ss));
  }
  LEAVE(PPCAttemptSemaphoreShared);
  return (ret);
}


void kernPPCObtainSemaphore(struct SignalSemaphorePPC *ss)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCObtainSemaphore);
  if (ss->ssppc_SS.ss_Link.ln_Type == NT_SIGNALSEM) {
    KDB(DPrintf(ppcbase,"\t0x%08lx\n",ss));
    __ObtainSemaphorePPC(ppcbase->ppc_PowerPCBase,ss);
  }
  else {
    KDB(DPrintf(ppcbase,"\t0x%08lx is NO SignalSemaphore!\n",ss));
  }
  LEAVE(PPCObtainSemaphore);
}


struct SignalSemaphorePPC *kernPPCObtainSemaphoreByName(char *name)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct SignalSemaphorePPC *ss;

  ENTER(PPCObtainSemaphoreByName);
  KDB(DPrintf(ppcbase,"\tname=\"%s\"\n",name?name:noname));
  if (ss = __FindSemaphorePPC(ppcbase->ppc_PowerPCBase,(STRPTR)name))
    __ObtainSemaphorePPC(ppcbase->ppc_PowerPCBase,ss);
  KDB(DPrintf(ppcbase,"\trc=0x%08lx\n",ss));
  LEAVE(PPCObtainSemaphoreByName);
  return (ss);
}


void kernPPCObtainSemaphoreShared(struct SignalSemaphorePPC *ss)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCObtainSemaphoreShared);
  if (ss->ssppc_SS.ss_Link.ln_Type == NT_SIGNALSEM) {
    KDB(DPrintf(ppcbase,"\t0x%08lx\n",ss));
    __ObtainSemaphoreSharedPPC(ppcbase->ppc_PowerPCBase,ss);
  }
  else {
    KDB(DPrintf(ppcbase,"\t0x%08lx is NO SignalSemaphore!\n",ss));
  }
  LEAVE(PPCObtainSemaphoreShared);
}


void kernPPCReleaseSemaphore(struct SignalSemaphorePPC *ss)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCReleaseSemaphore);
  if (ss->ssppc_SS.ss_Link.ln_Type == NT_SIGNALSEM) {
    KDB(DPrintf(ppcbase,"\t0x%08lx\n",ss));
    __ReleaseSemaphorePPC(ppcbase->ppc_PowerPCBase,ss);
  }
  else {
    KDB(DPrintf(ppcbase,"\t0x%08lx is NO SignalSemaphore!\n",ss));
  }
  LEAVE(PPCReleaseSemaphore);
}


/****************
  Caches
 ****************/

void kernPPCCacheFlush(APTR start,ULONG length)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCCacheFlush);
  KDB(DPrintf(ppcbase,"\tstart=0x%08lx len=0x%08lx\n",start,length));
  __SetCache(ppcbase->ppc_PowerPCBase,CACHE_DCACHEFLUSH,start,length);
  __SetCache(ppcbase->ppc_PowerPCBase,CACHE_ICACHEINV,start,length);
  LEAVE(PPCCacheFlush);
}


void kernPPCCacheInvalid(APTR start,ULONG length)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCCacheInvalid);
  KDB(DPrintf(ppcbase,"\tstart=0x%08lx len=0x%08lx\n",start,length));
  __SetCache(ppcbase->ppc_PowerPCBase,CACHE_DCACHEINV,start,length);
  __SetCache(ppcbase->ppc_PowerPCBase,CACHE_ICACHEINV,start,length);
  LEAVE(PPCCacheInvalid);
}


void kernPPCCacheFlushAll(void)
{
  struct PPCLibBase *ppcbase = __getbase();

  ENTER(PPCCacheFlushAll);
  __SetCache(ppcbase->ppc_PowerPCBase,CACHE_DCACHEFLUSH,0,0);
  __SetCache(ppcbase->ppc_PowerPCBase,CACHE_ICACHEINV,0,0);
  LEAVE(PPCCacheFlushAll);
}


/****************
  Miscellaneous
 ****************/

struct Node *kernPPCFindName(struct List *list,STRPTR name)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct Node *n;

  ENTER(PPCFindName);
  n = __FindNamePPC(ppcbase->ppc_PowerPCBase,list,name);
  LEAVE(PPCFindName);
  return (n);
}


struct Node *kernPPCFindNameSync(struct List *list,STRPTR name)
{
  struct PPCLibBase *ppcbase = __getbase();
  ULONG skey;
  struct Node *n;

  ENTER(PPCFindNameSync);
  skey = __Super(ppcbase->ppc_PowerPCBase);
  n = __FindNamePPC(ppcbase->ppc_PowerPCBase,list,name);
  __User(ppcbase->ppc_PowerPCBase,skey);
  LEAVE(PPCFindNameSync);
  return (n);
}


ULONG kernPPCDoMethodA(Object *obj,Msg msg)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;
  ULONG *addr = (ULONG *)(*(obj-1));

  ENTER(PPCDoMethodA);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)(addr[2]);
  pa.PP_Regs[PPREG_A0] = (ULONG)addr;
  pa.PP_Regs[PPREG_A1] = (ULONG)msg;
  pa.PP_Regs[PPREG_A2] = (ULONG)obj;
  pa.PP_Regs[PPREG_D0] = (ULONG)obj;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCDoMethodA);
  return (pa.PP_Regs[PPREG_D0]);
}


ULONG kernPPCDoSuperMethodA(struct IClass *class,Object *obj,Msg msg)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCDoSuperMethodA);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)class->cl_Super->cl_Dispatcher.h_Entry;
  pa.PP_Regs[PPREG_A0] = (ULONG)class->cl_Super;
  pa.PP_Regs[PPREG_A1] = (ULONG)msg;
  pa.PP_Regs[PPREG_A2] = (ULONG)obj;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCDoSuperMethodA);
  return(pa.PP_Regs[PPREG_D0]);
}


ULONG kernPPCCoerceMethodA(struct IClass *class,Object *obj,Msg msg)
{
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;

  ENTER(PPCCoerceMethodA);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)class->cl_Dispatcher.h_Entry;
  pa.PP_Regs[PPREG_A0] = (ULONG)class;
  pa.PP_Regs[PPREG_A1] = (ULONG)msg;
  pa.PP_Regs[PPREG_A2] = (ULONG)obj;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCCoerceMethodA);
  return(pa.PP_Regs[PPREG_D0]);
}


/****************
  Read/Write 68k
 ****************/

UBYTE kernPPCReadByte(UBYTE *p)
{
  extern void m68k_rb();
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;
  static void (*rbfunc)() = m68k_rb;

  ENTER(PPCReadByte);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)rbfunc;
  pa.PP_Regs[PPREG_A0] = (ULONG)p;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCReadByte);
  return ((UBYTE)pa.PP_Regs[PPREG_D0]);
}


UWORD kernPPCReadWord(UWORD *p)
{
  extern void m68k_rw();
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;
  static void (*rwfunc)() = m68k_rw;

  ENTER(PPCReadWord);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)rwfunc;
  pa.PP_Regs[PPREG_A0] = (ULONG)p;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCReadWord);
  return ((UWORD)pa.PP_Regs[PPREG_D0]);
}


ULONG kernPPCReadLong(ULONG *p)
{
  extern void m68k_rl();
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;
  static void (*rlfunc)() = m68k_rl;

  ENTER(PPCReadLong);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)rlfunc;
  pa.PP_Regs[PPREG_A0] = (ULONG)p;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCReadLong);
  return ((ULONG)pa.PP_Regs[PPREG_D0]);
}


void kernPPCWriteByte(UBYTE *p,ULONG val)
{
  extern void m68k_wb();
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;
  static void (*wbfunc)() = m68k_wb;

  ENTER(PPCWriteByte);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)wbfunc;
  pa.PP_Regs[PPREG_A0] = (ULONG)p;
  pa.PP_Regs[PPREG_D0] = val;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCWriteByte);
}


void kernPPCWriteWord(UWORD *p,ULONG val)
{
  extern void m68k_ww();
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;
  static void (*wwfunc)() = m68k_ww;

  ENTER(PPCWriteWord);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)wwfunc;
  pa.PP_Regs[PPREG_A0] = (ULONG)p;
  pa.PP_Regs[PPREG_D0] = val;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCWriteWord);
}


void kernPPCWriteLong(ULONG *p,ULONG val)
{
  extern void m68k_wl();
  struct PPCLibBase *ppcbase = __getbase();
  struct PPCArgs pa;
  static void (*wlfunc)() = m68k_wl;

  ENTER(PPCWriteLong);
  _memset(&pa,sizeof(struct PPCArgs),0);
  pa.PP_Code = (APTR)wlfunc;
  pa.PP_Regs[PPREG_A0] = (ULONG)p;
  pa.PP_Regs[PPREG_D0] = val;
  pa.PP_Regs[PPREG_A6] = (ULONG)ppcbase;
  __Run68K(ppcbase->ppc_PowerPCBase,&pa);
  LEAVE(PPCWriteLong);
}
