/* $VER: pasm tables.c V0.8 (14.02.98)
 *
 * This file is part of pasm, a portable PowerPC assembler.
 * Copyright (c) 1997-98  Frank Wille
 *
 * pasm is freeware and part of the portable and retargetable ANSI C
 * compiler vbcc, copyright (c) 1995-98 by Volker Barthelmann.
 * pasm may be freely redistributed as long as no modifications are
 * made and nothing is charged for it. Non-commercial usage is allowed
 * without any restrictions.
 * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
 * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
 *
 *
 * v0.8 (14.02.98) phx
 *      Alignment list for each section. This fixes the problems
 *      with optimizations.
 * v0.7 (01.01.98) phx
 *      search_instr() is global.
 * v0.5 (08.10.97) phx
 *      .globl directive declares unknown symbols in pass 1 as
 *      externally defined. If the symbol is defined later in the
 *      source, it will be made global by add_symbol().
 * v0.3 (05.04.97) phx
 *      Minor changes.
 * v0.2 (25.03.97) phx
 *      Writes ELF object for 32-bit PowerPC big-endian. Either absolute
 *      or ELF output format may be selected. ELF is default for all
 *      currently supported platforms. PPCasm supports nine different
 *      relocation types (there are much more...).
 *      Compiles and works also under NetBSD/amiga (68k).
 *      Changed function declaration to 'new style' in all sources
 *      (to avoid problems with '...' for example).
 *      Externally defined symbols will automatically get global binding.
 * v0.1 (11.03.97) phx
 *      First test version with all PowerPC instructions and most
 *      important directives. Only raw, absolute output.
 * v0.0 (22.02.97) phx
 *      File created.
 */


#define TABLES_C
#include "ppcasm.h"


void init_hashtables(struct GlobalVars *);
void add_macro(struct GlobalVars *,struct Macro *);
struct Symbol *add_symbol(struct GlobalVars *,char *,uint8,uint32);
unsigned long elf_hash(unsigned char *);
struct Symbol *search_symbol(struct GlobalVars *,char *);
struct CPUInstr *search_instr(struct GlobalVars *,char *);
struct Section *search_section(struct GlobalVars *,char *);
void search_opcode(struct GlobalVars *,struct ParsedLine *,char *,char *);

static void *alloc_hashtable(size_t);
static void add_instruction(struct GlobalVars *,struct CPUInstr *);
static void add_directive(struct GlobalVars *,struct Directive *);
static struct Directive *search_directive(struct GlobalVars *,char *);
static struct Macro *search_macro(struct GlobalVars *,char *);
static void execute_macro(struct GlobalVars *,struct ParsedLine *);
static void execute_directive(struct GlobalVars *,struct ParsedLine *);



void init_hashtables(struct GlobalVars *gv)
{
  struct CPUInstr *ins = instructions;
  struct Directive *dir = directives;

  gv->symbols = alloc_hashtable(SYMHTABSIZE);
  gv->instr = alloc_hashtable(INSTRHTABSIZE);
  gv->directives = alloc_hashtable(DIRHTABSIZE);
  gv->macros = alloc_hashtable(MACROHTABSIZE);
  while (ins->name)
    add_instruction(gv,ins++);
  while (dir->name)
    add_directive(gv,dir++);
}


static void *alloc_hashtable(size_t entries)
{
  return (alloczero(entries * sizeof(void *)));
}


static void add_instruction(struct GlobalVars *gv,struct CPUInstr *ins)
/* make new entry into instruction hash table */
{
  struct CPUInstr *chain;
  unsigned long offs = elf_hash(ins->name)%INSTRHTABSIZE;

  if (!(ins->flags & F_EXTENDED) || !gv->noextmnemo) {
    if (chain = gv->instr[offs])
      ins->hash_chain = chain;
    else
      ins->hash_chain = NULL;
    gv->instr[offs] = ins;
  }
}


static void add_directive(struct GlobalVars *gv,struct Directive *dir)
/* make new entry into directive hash table */
{
  struct Directive *chain;
  unsigned long offs = elf_hash(dir->name)%DIRHTABSIZE;

  if (chain = gv->directives[offs])
    dir->hash_chain = chain;
  else
    dir->hash_chain = NULL;
  gv->directives[offs] = dir;
}


void add_macro(struct GlobalVars *gv,struct Macro *mac)
/* make new entry into macro hash table */
{
  struct Macro *chain,*m;
  unsigned long offs = elf_hash(mac->name)%MACROHTABSIZE;

  if (chain = m = gv->macros[offs]) {  /* hash code already used? */
    do {
      if (!strcmp(m->name,mac->name)) {
        error(8,mac->name);  /* macro defined twice! */
        break;
      }
    }
    while (m = m->hash_chain);
  }
  gv->macros[offs] = mac;
  mac->hash_chain = chain;
}


struct Symbol *add_symbol(struct GlobalVars *gv,char *name,uint8 type,
                          uint32 value)
/* define new symbol - absolute, relocatable or external */
{
  struct Symbol *sym,*chain;
  unsigned long offs = elf_hash(name)%SYMHTABSIZE;

  if (chain = sym = gv->symbols[offs]) {  /* hash code already used? */
    do {
      if (!strcmp(name,sym->name)) {
        if (sym->type == SYM_EXTERN) { /* declared ext. visible by .globl? */
          if ((sym->type = type) == SYM_RELOC) {
            sym->relsect = gv->csect;
            sym->alignpoint = gv->csect->current_align;
          }
          sym->value = value;
        }
        else
          error(7,name);  /* symbol defined twice! */
        return (sym);
      }
    }
    while (sym = sym->hash_chain);
  }
  gv->symbols[offs] = sym = alloczero(sizeof(struct Symbol));
  sym->hash_chain = chain;
  sym->name = allocstring(name);
  sym->value = value;
  if ((sym->type = type) == SYM_RELOC) {
    sym->relsect = gv->csect;
    sym->alignpoint = gv->csect->current_align;
  }
  else if (type == SYM_EXTERN)
    sym->bind = SYMB_GLOBAL;
  return (sym);
}


unsigned long elf_hash(unsigned char *name)
/* calculate a hash code as used in ELF objects */
{
  unsigned long h=0,g;

  while (*name) {
    h = (h << 4) + *name++;
    if (g = h & 0xf0000000)
      h ^= g >> 24;
    h &= ~g;
  }
  return (h);
}


struct Symbol *search_symbol(struct GlobalVars *gv,char *name)
/* find a symbol in the symbol hash table */
{
  struct Symbol *chain;
  unsigned long offs = elf_hash(name)%SYMHTABSIZE;

  chain = gv->symbols[offs];
  while (chain) {
    if (!strcmp(chain->name,name))
      return (chain);
    chain = chain->hash_chain;
  }
  return (NULL);
}


struct CPUInstr *search_instr(struct GlobalVars *gv,char *name)
/* find an instruction in the instruction hash table */
{
  struct CPUInstr *chain;
  unsigned long offs = elf_hash(name)%INSTRHTABSIZE;

  chain = gv->instr[offs];
  while (chain) {
    if (!strcmp(chain->name,name))
      return (chain);
    chain = chain->hash_chain;
  }
  return (NULL);
}


static struct Directive *search_directive(struct GlobalVars *gv,char *name)
/* find a directive in the directive hash table */
{
  struct Directive *chain;
  unsigned long offs = elf_hash(name)%DIRHTABSIZE;

  chain = gv->directives[offs];
  while (chain) {
    if (!strcmp(chain->name,name))
      return (chain);
    chain = chain->hash_chain;
  }
  return (NULL);
}


static struct Macro *search_macro(struct GlobalVars *gv,char *name)
/* find a macro in the macro hash table */
{
  struct Macro *chain;
  unsigned long offs = elf_hash(name)%MACROHTABSIZE;

  chain = gv->macros[offs];
  while (chain) {
    if (!strcmp(chain->name,name))
      return (chain);
    chain = chain->hash_chain;
  }
  return (NULL);
}


struct Section *search_section(struct GlobalVars *gv,char *name)
{
  struct Section *nextsec,*sec=(struct Section *)gv->sectionlist.first;

  while (nextsec = (struct Section *)sec->n.next) {
    if (!strcmp(name,sec->name))
      return (sec);
    sec = nextsec;
  }
  return (NULL);
}


void search_opcode(struct GlobalVars *gv,struct ParsedLine *pl,
                   char *opname,char *operand)
{
  if (gv->alignflag)
    pl->flags |= PLF_ALIGN;
  gv->alignflag = FALSE;
  if (pl->opcode = (void *)search_macro(gv,opname)) {
    pl->type = OT_MACRO;
    pl->operand = operand;
    execute_macro(gv,pl);
  }
  else if (pl->opcode = (void *)search_instr(gv,opname)) {
    pl->type = OT_INSTRUCTION;
    pl->operand = allocstring(operand);
    gv->csect->pc += 4;  /* every PPC instruction has a size of 4 bytes */
  }
  else if (pl->opcode = (void *)search_directive(gv,opname)) {
    pl->type = OT_DIRECTIVE;
    pl->operand = allocstring(operand);
    execute_directive(gv,pl);
  }
  else if (pl->opcode = (void *)search_section(gv,opname)) {
    pl->type = OT_SECTION;
    activate_section(gv,(struct Section *)pl->opcode);
  }
  else
    error(10,opname);  /* Unknown opcode */
}


static void execute_macro(struct GlobalVars *gv,struct ParsedLine *pl)
/* creates a SourceText structure for every macro invocation, parses */
/* macro parameters and recursively calls pass1() to execute the macro */
{
  struct Macro *m = (struct Macro *)pl->opcode;
  struct MacroParams *mp = alloczero(sizeof(struct MacroParams));
  struct SourceText *stxt = alloc(sizeof(struct SourceText));
  char *mac_params = alloc(strlen(pl->operand)+1);
  uint32 oldnarg = gv->nargsym->value;

  /* create SourceText node for macro-call */
  stxt->name = m->name;
  stxt->text = m->text;
  stxt->nlines = m->nlines;
  stxt->plin = alloczero(m->nlines * sizeof(struct ParsedLine));
  addtail(&gv->sourcelist,&stxt->n);

  /* initialize MacroParams structure */
  mp->call_id = gv->macrocnt++;
  strcpy(mac_params,pl->operand);
  read_macro_params(gv,pl,mp,mac_params);
  pl->narg = (uint8)mp->narg;
  gv->nargsym->value = mp->narg;

  /* recursively call pass 1 and create new SourceThread */
  pass1(gv,stxt,mp,gv->cthread);
  gv->nargsym->value = oldnarg;

  /* cleanup */
  free(mp);
  pl->opcode = stxt;
}


static void execute_directive(struct GlobalVars *gv,struct ParsedLine *pl)
{
  struct Directive *d = (struct Directive *)pl->opcode;

  (d->dfunct)(gv,pl);
}
