/*
** ppc.library emulation
** (c)1998-2001 Frank Wille <frank@phoenix.owl.de>
**
** FreeMem/FreeVec patch, to avoid deallocation of PPC-allocated memory.
**
** V0.8e (16.12.2000) phx
**       Patch can be disabled by setting PPCLibF_NoFreeMemPatch.
**       Added some debugging output.
** V0.8c (21.10.2000) phx
**       Created.
*/

#include "supp.h"
#include "oscall.h"

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


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

/* 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->ppc_SysBase;
  struct SignalSemaphore *ss;
  char *ssname;
  BOOL already_installed = TRUE;

  if (ppcbase->Flags & PPCLibF_NoFreeMemPatch)
    return (TRUE);

  /* arbitrate access to the semaphore list */
  Forbid();

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

      already_installed = FALSE;
      _strcpy(ssname,(char *)patchname);
      InitSemaphore(ss);
      ss->ss_Link.ln_Name = ssname;
      ss->ss_Link.ln_Pri = -127;
      AddSemaphore(ss);

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

  if (!already_installed)
    Dprintf(ppcbase,"FreeMem/Vec-patch installed\n");
  else
    Dprintf(ppcbase,"FreeMem/Vec-patch still active\n");
  return (TRUE);
}


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

  if (ppcbase->Flags & PPCLibF_NoFreeMemPatch)
    return (TRUE);

  Forbid();
  if (ss = FindSemaphore((char *)patchname)) {
    currFreeMem = SetFunction((struct Library *)sysbase,LVO_FreeMem,
                              OldFreeMem);
    currFreeVec = SetFunction((struct Library *)sysbase,LVO_FreeVec,
                              OldFreeVec);
    if (currFreeMem==(APTR)NewFreeMem &&
        currFreeVec==(APTR)NewFreeVec) {
      /* successful! */
      RemSemaphore(ss);
      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);
      SetFunction((struct Library *)sysbase,LVO_FreeVec,currFreeVec);
    }
  }
  Permit();

  if (ret)
    Dprintf(ppcbase,"FreeMem/Vec-patch removed\n");
  else
    Dprintf(ppcbase,"Couldn't remove FreeMem/Vec-patch\n");
  return (ret);
}
