/*
 * kicktags.c
 * Based on "KickTags.c" by Ralph Babel 1988.
 * vbcc-apdation, written by Frank Wille 1998.
 *
 */

#include <exec/types.h>
#include <exec/execbase.h>
#include <proto/exec.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "kicktags.h"

#define LIBRARY_VERSION_2_0 36  /* Kickstart 2.0 */
#define RANGER_LOWER ((APTR)0xc00000)
#define RANGER_UPPER ((APTR)0xdc0000)



void *AllocKickMem(struct ExecBase *eb,ULONG bytes,
                   ULONG align,LONG adjust)
/* allocate MEMF_KICKTAGS from the end of the free memory pool */
{
  void *result;
  struct MemHeader *mh;
  struct MemChunk *mc;
  char *last=NULL,*temp;

  bytes = (bytes + (MEM_BLOCKSIZE-1)) & ~MEM_BLOCKMASK;
  Forbid();

  for (mh = (struct MemHeader *)eb->MemList.lh_Head; mh->mh_Node.ln_Succ;
       mh = (struct MemHeader *)mh->mh_Node.ln_Succ) {
    if (mh->mh_Free >= bytes
        && ((mh->mh_Attributes & (MEMF_CHIP|MEMF_LOCAL))
        || mh->mh_Lower >= RANGER_LOWER && mh->mh_Upper >= RANGER_UPPER)) {
      for (mc = mh->mh_First; mc != NULL; mc = mc->mc_Next) {
        if (mc->mc_Bytes >= bytes) {
          temp = (char *)((ULONG)((char *)mc + mc->mc_Bytes - (bytes+adjust))
                 & ~(align-1)) + adjust;
          if (temp >= (char *)mc)
            last = temp;
        }
      }
      if (last)
        break;
    }
  }

  result = last ? AllocAbs(bytes,last) : (void *)NULL;
  Permit();

  return (result==NULL ? (void *)NULL : memset(result,0,(size_t)bytes));
}


static struct KickTagHeader *FindKickTag0(struct ExecBase *eb,
                                          const char *name,
                                          ULONG bytes,
                                          struct Resident ****ppprt,
                                          struct MemList ***ppml,
                                          ULONG *pKickTagMask)
/* find a KickTag */
{
  struct KickTagHeader *kth;
  struct Resident ***pprt,**prt,*rt;
  struct MemList **pml,*ml;
  ULONG kickTagMask;

  Forbid();

  if (SumKickData() == (ULONG)eb->KickCheckSum) {
    if (eb->KickTagPtr) {
      kickTagMask = ~(1L<<31);
      pprt = (struct Resident ***)&eb->KickTagPtr;
      prt = *pprt;

      while (rt = *prt++) {
        if ((LONG)rt < 0) {
          kickTagMask = ~0U;
          pprt = (struct Resident ***)prt;
          prt = (struct Resident **)((ULONG)rt & ~(1L<<31));
          continue;
        }

        if (!strcmp(rt->rt_Name,name)) {
          kth = (struct KickTagHeader *)((char *)rt -
                (offsetof(struct KickTagHeader,Resident)));

          if (eb->KickMemPtr) {
            for (pml = (struct MemList **)&eb->KickMemPtr; (ml=*pml)!=NULL;
                 pml = (struct MemList **)&ml->ml_Node.ln_Succ) {
              if (ml->ml_ME[0].me_Addr == kth &&
                  ml->ml_ME[0].me_Length == bytes) {
                *ppprt = pprt;
                *ppml = pml;
                *pKickTagMask = kickTagMask;
                goto quit;
              }
            }
          }
        }
      }
    }
  }
  kth = NULL;

quit:
  Permit();
  return (kth);
}


struct KickTagHeader *FindKickTag(struct ExecBase *eb,const char *name,
                                  ULONG bytes)
{
  struct Resident ***pprt;
  struct MemList **pml;
  ULONG kickTagMask;

  return(FindKickTag0(eb,name,bytes,&pprt,&pml,&kickTagMask));
}


void AddKickTag(struct ExecBase *eb,struct KickTagHeader *kth,
                ULONG bytes,UWORD entries)
/* add a KickTag */
{
  APTR kickMemPtr,kickTagPtr;

  kth->MemList.ml_NumEntries = entries+1;
  kth->MemList.ml_ME[0].me_Addr = kth;
  kth->MemList.ml_ME[0].me_Length = bytes;

  kth->KickTagArray[0] = &kth->Resident;

  kth->Resident.rt_MatchWord = RTC_MATCHWORD;
  kth->Resident.rt_MatchTag = &kth->Resident;
  kth->Resident.rt_EndSkip = ((char *)kth + bytes);

  kickMemPtr = kickTagPtr = NULL;

  Forbid();

  if (SumKickData() == (ULONG)eb->KickCheckSum) {
    kickMemPtr = eb->KickMemPtr;
    kickTagPtr = eb->KickTagPtr;
  }

  kth->MemList.ml_Node.ln_Type = NT_UNKNOWN;
  kth->MemList.ml_Node.ln_Succ = (struct Node *)kickMemPtr;
  kth->MemList.ml_Node.ln_Pred = (struct Node *)0;

  kth->KickTagArray[1] = kickTagPtr ? 
                         (APTR)((ULONG)kickTagPtr|(1L<<31)) : (void *)NULL;

  eb->KickMemPtr = &kth->MemList;
  eb->KickTagPtr = kth->KickTagArray;
  eb->KickCheckSum = (APTR)SumKickData();

  /* push data cache after writing to KickCheckSum */
  clearCache(eb);

  Permit();
}


struct KickTagHeader *RemKickTag(struct ExecBase *eb,const char *name,
                                 ULONG bytes)
/* remove a KickTag */
{
  struct KickTagHeader *kth;
  struct Resident ***pprt;
  struct MemList **pml;
  ULONG kickTagMask;

  Forbid();

  if (kth = FindKickTag0(eb,name,bytes,&pprt,&pml,&kickTagMask)) {
    *pprt = (struct Resident **)((ULONG)kth->KickTagArray[1] &
                                 kickTagMask);
    *pml = (struct MemList *)kth->MemList.ml_Node.ln_Succ;
    eb->KickCheckSum = (APTR)SumKickData();

    /* push data cache after writing to KickCheckSum */
    clearCache(eb);
  }

  Permit();
  return (kth);
}


void clearCache(struct ExecBase *eb)
/* push and clear all data caches */
{
  if (SysBase->LibNode.lib_Version >= LIBRARY_VERSION_2_0)
    CacheClearU();
}
