/*
** ppc.library emulation
** (c)1998-2000 Frank Wille <frank@phoenix.owl.de>
**
** FreeMem/FreeVec patch, to avoid deallocation of PPC-allocated memory.
**
** V0.8c (21.10.2000) phx
**       Created.
*/

#include "supp.h"

#define	PATCHNAME   "FreeMem-Patch"
#define LVO_FreeMem ((void *)-210)
#define LVO_FreeVec ((void *)-690)


static const char *patchname = PATCHNAME;
APTR OldFreeMem,OldFreeVec;

/* exec.library - vbcc has some problems with a0=long */
APTR _SetFunction(__reg("a1")struct Library *,__reg("a0")void *,
                  __reg("d0")unsigned long (*)(),__reg("a6") void *) =
                  "\tjsr\t-420(a6)";

/* freemem.asm */
extern void NewFreeMem(__reg("a1")void *,__reg("d0")ULONG,
                       __reg("a6")struct ExecBase *);
extern void NewFreeVec(__reg("a1")void *,__reg("a6")struct ExecBase *);



BOOL SetFreeMemPatch(__reg("a0")struct PPCLibBase *ppcbase)
{
  struct ExecBase *sysbase = ppcbase->SysBase;
  struct SignalSemaphore *ss;
  char *ssname;

  /* arbitrate access to the semaphore list */
  __Forbid(sysbase);

  if (!__FindSemaphore((char *)patchname,sysbase)) {
    if ((ss = alloc32c(sizeof(struct SignalSemaphore),ppcbase))
        && (ssname = alloc32(_strlen((char *)patchname)+1,ppcbase))) {

      _strcpy(ssname,(char *)patchname);
      ss->ss_Link.ln_Name = ssname;
      ss->ss_Link.ln_Pri = -127;
      __InitSemaphore(ss,sysbase);
      __AddSemaphore(ss,sysbase);

      /* patch LVOs of FreeMem() and FreeVec() */
      OldFreeMem = _SetFunction((struct Library *)sysbase, LVO_FreeMem,
                                (unsigned long (*)())NewFreeMem,
                                sysbase);
      OldFreeVec = _SetFunction((struct Library *)sysbase, LVO_FreeVec,
                                (unsigned long (*)())NewFreeVec,
                                sysbase);
    }
    else {
      __Permit(sysbase);
      return (FALSE);  /* out of memory */
    }
  }
  /* patch already installed */
  __Permit(sysbase);
  return (TRUE);
}


BOOL RemFreeMemPatch(__reg("a0")struct PPCLibBase *ppcbase)
{
  struct ExecBase *sysbase = ppcbase->SysBase;
  struct SignalSemaphore *ss;
  APTR currFreeMem,currFreeVec;
  BOOL ret = FALSE;

  __Forbid(sysbase);
  if (ss = __FindSemaphore((char *)patchname,sysbase)) {
    currFreeMem = _SetFunction((struct Library *)sysbase,LVO_FreeMem,
                               OldFreeMem,sysbase);
    currFreeVec = _SetFunction((struct Library *)sysbase,LVO_FreeVec,
                               OldFreeVec,sysbase);
    if (currFreeMem==(APTR)NewFreeMem &&
        currFreeVec==(APTR)NewFreeVec) {
      /* successful! */
      __RemSemaphore(ss,sysbase);
      free32(ss->ss_Link.ln_Name,ppcbase);
      free32(ss,ppcbase);
      ret = TRUE;
    }
    else {
      /* patch removal failed - there's another patch active */
      _SetFunction((struct Library *)sysbase,LVO_FreeMem,currFreeMem,sysbase);
      _SetFunction((struct Library *)sysbase,LVO_FreeVec,currFreeVec,sysbase);
    }
  }
  __Permit(sysbase);
  return (ret);
}
