/*
 * ACME - a crossassembler for producing 6502/65c02/65816 code.
 * Copyright (C) 1998 Marco Baye
 * Have a look at "acme.c" for further info
 */

/*
 * List item stuff
 */

#include "item.h"

/*
 * Compute "big hash" by merging "len" bytes from "base.type". Write output to
 * "base.hash", reduce "big hash" to character and return that.
 */
static unsigned char FN_Struct_Hash(ListItem *p, int len) {
  char *pb;
  int   a,
        Hash = 0;

  pb = &p->Type;
  for(a = 0; a < len; a++) {
    Hash = ((Hash << 7) | (Hash >> (8 * sizeof(int) - 7))) ^ pb[a];
  }
  p->Hash = Hash;
  PLATFORM_INT2CHAR(Hash);
  return((unsigned char) Hash);
}

/*
 * Compute the hash of the given string and then use that to try to find a
 * list item that matches the given data (big hash, type, name). Return pointer
 * to list item found.
 * If no matching list item can be found and size is not zero, allocate "size"
 * bytes of memory, link it to the correct list, copy comparison data into
 * this new list item and return its pointer (and set the global "fMadeItem"
 * flag).
 * If no matching list item can be found and size is zero, return NULL.
 */
static ListItem *FN_Struct_Search(ListItem *pConst, char type, int len, int Size) {
  ListItem      *pCheck,
                *pNew;
  char          *p1,
                *p2,
                 byte1,
                 byte2;
  unsigned char  Hash;

  fMadeItem = FALSE;
  pConst->Type = type;
  Hash = FN_Struct_Hash(pConst, len);
  pCheck = (ListItem *) &pPointerTable[Hash];/* point to first pointer */
  while(pCheck->Next) {
    if((pCheck->Next)->Hash > pConst->Hash) break;
#ifdef FDEBUG
    printf("{Check}");
#endif
    pCheck = pCheck->Next;
    /* Compare item at pCheck with pConst item */
    /* Compare big hash, type and first two characters (no terminator check) */
    if(pCheck->Hash == pConst->Hash) {
#ifdef FDEBUG
      printf("{=B}");
#endif
      p1 = &pCheck->Type;
      p2 = &pConst->Type;
      if(*(p1++) == *(p2++)) {/* check type */
        /* check first two characters */
        if((*(p1++) == *(p2++)) && (*(p1++) == *(p2++))) {
          /* check rest of name up to terminator */
          do {
            byte1 = *(p1++);
            byte2 = *(p2++);
          } while((byte1 == byte2) && byte1);
          if(byte1 == byte2) {
#ifdef FDEBUG
            printf("{found}");
#endif
            return(pCheck);/* return pointer to item */
          }
        }
      }
    }
  }
  if(Size == 0) {
#ifdef FDEBUG
    printf("{failed}");
#endif
    return(NULL);/* indicate failure */
  }
  /* create new item and link into list */
  pNew = (ListItem *) malloc(Size);
  if(pNew == NULL) FN_Message(pseNoMemLeft, ESERIOUS);
  pNew->Next = pCheck->Next;
  pCheck->Next = pNew;
  pNew->Hash = pConst->Hash;
  p1 = &pNew->Type;
  p2 = &pConst->Type;
  *(p1++) = *(p2++);/* copy type */
  *(p1++) = *(p2++);/* copy first char */
  *(p1++) = *(p2++);/* copy second char */
  do {
    /* copy rest of name */
    byte2 = *(p2++);
    *(p1++) = byte2;
  } while(byte2);
  fMadeItem = TRUE;
#ifdef FDEBUG
  printf("{created}");
#endif
  return(pNew);/* return pointer to new item */
}

/*
 * Looks out for (and creates, if necessary) label/macro and returns pointer.
 * Stringbuffer contains name and "Zone" contains desired zone, "len" is
 * the length of the name string. This routine increases "len" by 3 to cater
 * for the type and zone characters.
 */
static ListItem *FN_Struct_GetZoned(Sixteen Zone,int len,int type,int create) {
  int size = 0;

  if(create) size = sizeof(SizeStruct) + len + 2;
  StringItem.String[0] = (char) (Zone & 255);/* zone = given zone */
  StringItem.String[1] = (char) ((Zone >> 8) & 255);
  /* search for list item. If not found, create with given size */
  return(FN_Struct_Search(&StringItem, type, len + 3, size));
}

/*
 * Search for label. If it was created, it is now given the size info "pf".
 */
static ListItem *FN_Struct_GetPreparedLabel(Sixteen Zone, int len, int pf) {
  ListItem *p;

  p = FN_Struct_GetZoned(Zone, len, HTYPE_LABEL, TRUE);
  if(fMadeItem) {
    /* Finish empty label item */
    p->Data.Bytes.FLAGS = (unsigned char) (LABELFLAG_DEFAULT | pf);
    p->Data.Bytes.LOW  = 0;
    p->Data.Bytes.HIGH = 0;
    p->Data.Bytes.BANK = 0;
  } else {
    if(pf != MVALUESIZE_UK) {
      if((p->Data.Bytes.FLAGS & MVALUE_SIZE) != pf) {
#ifdef FDEBUG
        printf("Sizeflag=%d !", p->Data.Bytes.FLAGS & MVALUE_SIZE);
#endif
        FN_Message(pseTooLate, EERROR);/* too late for postfix */
      }
    }
  }
  return(p);
}
