/* Implements directives, pseudo-ops and processor opcodes
   Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
   James Bowman, Craig Franklin

This file is part of gputils.

gputils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

gputils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gputils; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "stdhdr.h"

#include "libgputils.h"
#include "gpasm.h"
#include "directive.h"
#include "evaluate.h"
#include "parse.h"
#include "processor.h"
#include "lst.h"
#include "macro.h"
#include "coff.h"
#include "gperror.h"
#include "special.h"

/* Forward declarations */
void execute_body(struct macro_head *h);

#define ATTRIB_COND 1

/************************************************************************/
/* create flags that control the behavior of the table macros like data,
 * dt,de, and dw */

#define DEFAULT_LIT_MASK 0xffff
#define ENDIAN_SWAP      (1<<31)
#define PACKING_BYTES    (1<<30)
#define SPLIT_PACK       (1<<29)

struct sllist {
  struct pnode *p;
  struct sllist *next;
};

extern struct pnode *mk_constant(int value);

/************************************************************************/
int _16bit_core = 0;         /* The 16bit core is handled differently in
			      * some instances. */
int _17cxx_core = 0;         /* 17cxx is different from 18cxx */

/************************************************************************/

/* Write a word into the memory image at the current location */

static void emit(unsigned int value)
{

  /* only write the program data to memory on the second pass */
  if (state.pass == 2) {

    if ((state.mode == relocatable) && (state.obj.section == NULL)) {
      gperror(GPE_WRONG_SECTION, NULL);
    }

    if (_17cxx_core && (state.org > 0xffff)) {  
      gperror(GPE_ADDROVF, NULL);
    } 

    if(value > state.device.core_size) {
      gpmessage(GPM_RANGE,NULL);
      value &= state.device.core_size;
    }

    if (i_memory_get(state.i_memory, state.org) & MEM_USED_MASK) {
      gperror(GPE_ADDROVR, NULL);
    }

    i_memory_put(state.i_memory, state.org, MEM_USED_MASK | value);

  }

  state.org++;

}

int endian_swap_word(int x)
{
  return (  ( (x>>8) & 0xff) |  ( (x<<8) & 0xff00));
}

/* Write a word to memory unless we're packing successive bytes into a word
 *
 * The static variable 'packed_hi_lo' acts both as a flag and a shift amount.
 * when it's zero, the next byte to be emitted is packed into the low byte
 * of the word. When it is 8, the next byte is packed into high byte of the
 * word.  This variable is used in conjunction with the data( ) routine.
 *
 * The static variable 'packed_byte' is the word into which two bytes are
 * packed. When two bytes are packed, then the word is emitted.
 */

int packed_hi_lo = 0;
static int packed_byte = 0;

static void emit_packed(unsigned int value, unsigned int mode)
{

  if(value > 255) {
    gpwarning(GPW_RANGE,NULL);
    value &= 0xff;
  }

  packed_byte |= (value << packed_hi_lo);

  if(packed_hi_lo) {

    if(mode & ENDIAN_SWAP) 
      packed_byte = endian_swap_word(packed_byte);

    emit( packed_byte);
    packed_hi_lo = packed_byte = 0;

  } else
    packed_hi_lo = 8;

}

static int off_or_on(struct pnode *p)
{
  int had_error = 0, ret = 0;

  if (p->tag != symbol)
    had_error = 1;
  else if (strcasecmp(p->value.symbol, "OFF") == 0)
    ret = 0;
  else if (strcasecmp(p->value.symbol, "ON") == 0)
    ret = 1;
  else
    had_error = 1;

  if (had_error)
     gperror(GPE_EXPECTED, "Expected \"ON\" or \"OFF\".");

  return ret;
}

static void data(struct sllist *L, int flavor, int lit_mask)
{


  if (L) {

    struct sllist *list,*previous;
    int value;

    list = L->next;
    
    while(list) {

      value = reloc_evaluate(list->p, RELOCT_ALL);

      if(lit_mask & PACKING_BYTES)
	emit_packed(value | flavor, lit_mask);
      else {
	if((value > lit_mask) || (value < 0)) {
          gpwarning(GPW_RANGE,NULL);
	}
      
        if(lit_mask & SPLIT_PACK) {
	  emit(value & 0xff);
	  emit((value >> 8) & 0xff);
        } else {
	  emit((value & lit_mask) | flavor);
        }
      }

      previous = list;
      list = list->next;
      free(previous);

    }

  }

  if(packed_hi_lo) 
    emit_packed(flavor, lit_mask);
  
}


/* If we convert to glib, we can use the built in library call g_slist_append */

struct sllist *sllist_append(struct sllist   *list,
			     struct pnode *p)
{
  struct sllist *new;

  new = malloc(sizeof(*new));
  new->p = p;
  new->next  = NULL;
  list->next = new;

  return(new);
}


/* convert an expression list which may consist of strings, constants, labels,
 * etc. into a singly linked list of integers.
 * This is called by the db,dw,dt,de, and data directives. 
 * 
 * pnode *L -        A pointer to a doubly-linked list containing all of the 
 *                   expressions.
 * sllist *list -    A pointer to a singly-linked list into which we will place
 *                   the integer values of the expressions.
 * packing_strings - A flag indicating that char strings should be packed two to
 *                   a word. Note that this applies to only the da, dw, and data 
 *                   directives. The db directive does the byte packing later.
 */

static void simplify_data(struct pnode *L, struct sllist *list, int packing_strings)
{

  if (L) {
    struct pnode *p;
    unsigned int v = 0;
    unsigned int shift;
    int value;

    p = HEAD(L);

    if (p->tag == string) {
      char *pc = p->value.string;

      if(packing_strings == 2) {
        shift = 7;
      } else {
        shift = 8;
      }
      
      while (*pc) {
	pc = convert_escape_chars(pc, &value);

	if(packing_strings) {
	  if(v>= (1<<31) ) {
	    v = (v<<shift) |  value;
            if(_16bit_core) {
              v = endian_swap_word(v);
            }   	    
	    list = sllist_append(list, mk_constant(v));
	    v = 0;
	  } else
	    v = (1<<31) | value;
	  
	} else {
	  list = sllist_append(list, mk_constant(value));
	}
      }

      if(packing_strings && v >= 1<<31 ) {
        v = v <<shift;
        if(_16bit_core) {
          v = endian_swap_word(v);
        }        
	list = sllist_append(list, mk_constant(v));
      }

    } else {
      list = sllist_append(list, p);

    }
    simplify_data(TAIL(L), list, packing_strings);

  }

}

/* Do the work for beginning a conditional assembly block.  Leave it
   disabled by default.  This is used by do_if, do_ifdef and
   do_ifndef. */

static void enter_if(void)
{
  struct amode *new = malloc(sizeof(*new));

  new->mode = in_then;
  new->prev = state.astack;
  if (state.astack == NULL)
    new->prev_enabled = 1;
  else
    new->prev_enabled = state.astack->enabled && state.astack->prev_enabled;
  new->enabled = 0;	/* Only the default */
  state.astack = new;
}

/* Checking that a macro definition's parameters are correct */

static int macro_parms_simple(struct pnode *parms)
{
  if (parms == NULL) {
    return 1;
  } else if (HEAD(parms)->tag != symbol) {
    gperror(GPE_ILLEGAL_ARGU, NULL);
    return 0;
  } else {
    return (macro_parms_simple(TAIL(parms)));
  }
}

static int list_symbol_member(struct pnode *M, struct pnode *L)
{
  if (L == NULL) {
    return 0;
  } else if (STRCMP(M->value.symbol, HEAD(L)->value.symbol) == 0) {
    char buf[BUFSIZ];
    snprintf(buf,
             sizeof(buf), 
             "Duplicate macro parameter (%s).", 
             HEAD(L)->value.symbol);
    gperror(GPE_UNKNOWN, buf);
    return 1;
  } else {
    return list_symbol_member(M, TAIL(L));
  }
}

static int macro_parms_unique(struct pnode *parms)
{
  if (parms == NULL)
    return 1;
  else
    return (!list_symbol_member(HEAD(parms), TAIL(parms)) &&
	    macro_parms_unique(TAIL(parms)));
}

static int macro_parms_ok(struct pnode *parms)
{
  return (macro_parms_simple(parms) && macro_parms_unique(parms));
}

typedef gpasmVal opfunc(gpasmVal r,
			char *name,
			int arity,
			struct pnode *parms);

/************************************************************************/

static gpasmVal do_badram(gpasmVal r,
			  char *name,
			  int arity,
			  struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = dir;
  for (; parms != NULL; parms = TAIL(parms)) {
    p = HEAD(parms);
    if ((p->tag == binop) &&
	(p->value.binop.op == '-')) {
      int start, end;

      if (can_evaluate(p->value.binop.p0) &&
	  can_evaluate(p->value.binop.p1)) {
	start = evaluate(p->value.binop.p0);
	end = evaluate(p->value.binop.p1);

	if ((end < start) ||
	    (start < 0) ||
	    (MAX_RAM <= end)) {
	  gpwarning(GPW_INVALID_RAM, NULL);
	} else {
	  for (; start <= end; start++)
	    state.badram[start] = 1;
	}
      }
    } else {
      if (can_evaluate(p)) {
	int loc;

	loc = evaluate(p);
	if ((loc < 0) ||
	    (MAX_RAM <= loc)) {
	  gpwarning(GPW_INVALID_RAM, NULL);
	 } else
	  state.badram[loc] = 1;
      }
    }
  }

  return r;
}

static gpasmVal do_badrom(gpasmVal r,
			  char *name,
			  int arity,
			  struct pnode *parms)
{

  state.lst.line.linetype = dir;

  /* FIXME: implement this directive */
  gpwarning(GPW_UNKNOWN, "gpasm doesn't support the badrom directive yet"); 

  return r;
}

static void
set_bankisel(int address)
{
  if (state.device.class == PROC_CLASS_PIC14) {
    if (address < 0x100) {
      /* bcf 0x3, 0x7 */
      emit(0x1383);
    } else {
      /* bsf 0x3, 0x7 */
      emit(0x1783);
    }
  } else {
    /* movlb bank */
    emit(0xb800 | gp_processor_check_bank(state.device.class, address));
  }

}

static gpasmVal do_bankisel(gpasmVal r,
		            char *name,
		            int arity,
		            struct pnode *parms)
{
  struct pnode *p;
  int num_reloc;

  if ((state.device.class != PROC_CLASS_PIC14) &&
      (state.device.class != PROC_CLASS_PIC16)) {
    gpmessage(GPM_EXTPAGE, NULL);
    return r;
  }

  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (state.mode == absolute) {
      set_bankisel(maybe_evaluate(p));
    } else {
      num_reloc = count_reloc(p);
 
      if (num_reloc == 0) {
        /* it is an absolute address, generate the bankisel but no relocation */
        set_bankisel(maybe_evaluate(p));
      } else if (num_reloc != 1) {
        gperror(GPE_UNRESOLVABLE, NULL);
      } else {
        reloc_evaluate(p, RELOCT_IBANKSEL);
        emit(0);
      }
    }
  }

  return r;
}

static gpasmVal do_banksel(gpasmVal r,
		           char *name,
		           int arity,
		           struct pnode *parms)
{
  struct pnode *p;
  int address;
  int bank;
  int num_reloc;

  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (state.mode == absolute) {
      address = maybe_evaluate(p);
      bank = gp_processor_check_bank(state.device.class, address);
      state.org += gp_processor_set_bank(state.device.class, 
                                         state.processor_info->num_banks,
                                         bank, 
                                         state.i_memory, 
                                         state.org);
    } else {
      num_reloc = count_reloc(p);

      if (num_reloc == 0) {
        /* it is an absolute address, generate the banksel but no relocation */
        address = maybe_evaluate(p);
        bank = gp_processor_check_bank(state.device.class, address);
        state.org += gp_processor_set_bank(state.device.class, 
                                           state.processor_info->num_banks,
                                           bank, 
                                           state.i_memory, 
                                           state.org);
      } else if (num_reloc != 1) {
        gperror(GPE_UNRESOLVABLE, NULL);
      } else if (state.device.class == PROC_CLASS_PIC16) {
        reloc_evaluate(p, RELOCT_BANKSEL);
        emit(0);
      } else if (state.device.class == PROC_CLASS_PIC16E) {
        reloc_evaluate(p, RELOCT_BANKSEL);
        emit(0);
      } else {
        switch (state.processor_info->num_banks) {
        case 2:
          reloc_evaluate(p, RELOCT_BANKSEL);
          emit(0);
          break;      
        case 4:
          reloc_evaluate(p, RELOCT_BANKSEL);
          emit(0);
          emit(0);
          break;
        }
      }
    }
  }

  return r;
}

static gpasmVal do_code(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = sec;
  state.next_state = state_section;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    switch (arity) {
    case 0:
      /* new relocatable section */
      strncpy(state.obj.new_sec_name, ".code", sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = 0;
      state.obj.new_sec_flags = STYP_TEXT;
      break;
    case 1:
      /* new absolute section */
      p = HEAD(parms);
      strncpy(state.obj.new_sec_name, ".code", sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = maybe_evaluate(p) >> _16bit_core;
      state.obj.new_sec_flags = STYP_TEXT | STYP_ABS;
      break;
    default:
      enforce_arity(arity, 1);
    }
  }

  return r;
}

static gpasmVal do_constant(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{

  struct pnode *p;
  int value;

  state.lst.line.linetype = dir;

  for (; parms; parms = TAIL(parms)) {
    p = HEAD(parms);
    if ((p->tag == binop) &&
        (p->value.binop.op == '=')) {
      if (enforce_simple(p->value.binop.p0)) {
        char *lhs;
        /* fetch the symbol */
        lhs = p->value.binop.p0->value.symbol;
        /* constants must be assigned a value at declaration */
        value = maybe_evaluate(p->value.binop.p1);
        /* put the symbol and value in the table*/
        set_global(lhs, value, PERMANENT, gvt_constant);
      }
    } else {
      gperror(GPE_ILLEGAL_ARGU, NULL);
    }
  }

  return r;
}

/*-------------------------------------------------------------------------
 *
 * configuration memory
 * 
 * In addition to memory for storing instructions, each pic has memory for
 * storing configuration data (e.g. code protection, wdt enable, etc.). Each
 * family of the pic microcontrollers treats this memory slightly different.
 *
 * do_config( ) - Called by the parser when a __CONFIG assembler directive
 *       is encountered.
 *
 */

static gpasmVal do_config(gpasmVal r,
			  char *name,
			  int arity,
			  struct pnode *parms)
{
  struct pnode *p;
  int ca;
  int value;

  state.lst.line.linetype = config;

  switch(arity) {
  case 1:
    if(_16bit_core) {
      gpwarning(GPW_EXPECTED,"18cxxx devices should specify __CONFIG address");
      ca = CONFIG1L;
    } else {
      ca = state.device.config_address;
    }
    p = HEAD(parms);
    break;

  case 2:
    ca = maybe_evaluate(HEAD(parms));
    p = HEAD(TAIL(parms));
    break;

  default:
    enforce_arity(arity,2);
    return r;
  }

  state.lst.config_address = ca;

  if (state.mode == relocatable) {
    if ((!state.found_devid) && ((ca == DEVID1) || (ca == DEVID2))) {
      coff_new_section(".devid", ca >> _16bit_core, STYP_ABS | STYP_TEXT);
      state.found_devid = true;
    } else if (!state.found_config) {
      coff_new_section(".config", ca >> _16bit_core, STYP_ABS | STYP_TEXT);
      state.found_config = true;
    }
  }

  if ((can_evaluate(p)) && (state.pass == 2)) { 
    value = evaluate(p);

    if(_16bit_core) {
      int curval = i_memory_get(state.c_memory, ca>>1);
      int mask = 0xff <<((ca&1) ? 0 : 8);

      if(value > 0xff) {
	gpwarning(GPW_RANGE,0);
      }
      /* If the config address is even, then this byte goes in LSB position */
      value = (value & 0xff) << ((ca&1) ? 8 : 0)  | MEM_USED_MASK; 

      if(curval & MEM_USED_MASK)
	curval &= mask;
      else
	curval |= mask;

      i_memory_put(state.c_memory, ca>>1,  curval | value);

    } else {
      if(value > state.device.core_size) {
        gpmessage(GPM_RANGE,NULL);
        value &= state.device.core_size;
      }

      if (i_memory_get(state.c_memory, ca) & MEM_USED_MASK) {
        gperror(GPE_ADDROVR, NULL);
      }

      i_memory_put(state.c_memory, ca, MEM_USED_MASK | value);
      /* FIXME: need line_number? this one will be wrong coff_linenum(1) */
    }

    /* force the section to end */
    state.obj.section = NULL;
  }
        
  return r;
}

/*-------------------------------------------------------------------------
 *  do_da - The 'da' directive. Generates a number representing two
 *          7 bit ascii characters. It can be used in place of the DATA
 *          directive for 14 bit cores to pack two characters into one
 *          word. 
 */
static gpasmVal do_da(gpasmVal r,
		      char *name,
		      int arity,
		      struct pnode *parms)
{

  struct sllist list;

  list.next = NULL;

  if(_16bit_core || state.device.core_size == CORE_12BIT_MASK) {
    simplify_data(parms, &list, 1);
  } else {
    simplify_data(parms, &list, 2);
  }

  data(&list, 0, DEFAULT_LIT_MASK);

  return r;
}

/*-------------------------------------------------------------------------
 * do_data - The 'data' directive. Initialize one or more words of program
 *           memory with data. On all families except the pic18cxxx, the 
 *           first character is in the most significant byte of the word.
 */

static gpasmVal do_data(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{

  struct sllist list;

  list.next = NULL;
  simplify_data(parms, &list, 1);

  if ((state.mode == relocatable) &&
      !(SECTION_FLAGS & STYP_TEXT)) {
    /* This is a data memory not program */
    state.lst.line.linetype = res;

    /* data memory is byte sized so split the data */  
    data(&list, 0, SPLIT_PACK);
  } else {
    data(&list, 0, DEFAULT_LIT_MASK);
  } 

  return r;
}
/*-------------------------------------------------------------------------
 * do_db  - Reserve program memory words with packed 8-bit values. On the
 *          18cxxx families, dw and db are the same. For the 12 and 14 bit
 *          cores, the upper bits are masked (e.g. the 14-bit core can only
 *          store 14bits at a given program memory address, so the upper 2
 *          in a db directive are meaningless.
 */


static gpasmVal do_db(gpasmVal r,
		      char *name,
		      int arity,
		      struct pnode *parms)
{

  struct sllist list;

  list.next = NULL;
  simplify_data(parms, &list,0);

  if ((state.mode == relocatable) &&
      !(SECTION_FLAGS & STYP_TEXT)) {
    /* This is a data memory not program */
    state.lst.line.linetype = res;
 
    /* only valid in initialized data sections */
    if (SECTION_FLAGS & STYP_BSS)
      gperror(GPE_WRONG_SECTION, NULL);

    data(&list, 0, 0xff);
  } else {
    if(_16bit_core) {
      data(&list, 0, PACKING_BYTES);
    } else {
      data(&list, 0, ENDIAN_SWAP | PACKING_BYTES);
    }    
  } 

  return r;
}

static gpasmVal do_de(gpasmVal r,
		      char *name,
		      int arity,
		      struct pnode *parms)
{

  struct sllist list;

  list.next = NULL;
  simplify_data(parms, &list,0);
  if(_16bit_core) {
    data(&list, 0, PACKING_BYTES);
  } else {
    data(&list, 0, 0xff);
  }  


  return r;
}

static gpasmVal do_def(gpasmVal r,
                       char *name,
                       int arity,
                       struct pnode *parms)
{
  struct pnode *p;
  char *symbol_name = NULL;
  gp_symbol_type *coff_symbol = NULL;
  int shift = 0;
  int eval;
  int value = 0;
  gp_boolean new_class = false;
  int coff_class = C_NULL;
  gp_boolean new_type = false;
  int coff_type = T_NULL;
  enum gpasmValTypes type = gvt_debug;

  state.lst.line.linetype = dir;

  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    if (arity < 2) { 
      enforce_arity(arity, 2);
      return r;
    }

    /* the first argument is the symbol name */
    p = HEAD(parms);
    if (enforce_simple(p)) {
      symbol_name = p->value.symbol;
    } else {
      return r;
    }
    parms = TAIL(parms);

    if ((SECTION_FLAGS & STYP_TEXT) && _16bit_core) {
      shift = 1;
    }

    /* update the properties */
    for (; parms; parms = TAIL(parms)) {
      p = HEAD(parms);
      if ((p->tag == binop) &&
	  (p->value.binop.op == '=')) {
        if (enforce_simple(p->value.binop.p0)) {
	  char *lhs;

	  lhs = p->value.binop.p0->value.symbol;
          if (strcasecmp(lhs, "value") == 0) {
            value = maybe_evaluate(p->value.binop.p1);   
    	  } else if (strcasecmp(lhs, "size") == 0) {
            state.org += (maybe_evaluate(p->value.binop.p1) >> shift);   
    	  } else if (strcasecmp(lhs, "type") == 0) {
            eval = maybe_evaluate(p->value.binop.p1);   
            if ((eval < 0) || (eval > 0xffff)) {
              gperror(GPE_RANGE, NULL);
            } else {
              new_type = true;
              coff_type = eval;   
            }
    	  } else if (strcasecmp(lhs, "class") == 0) {
            eval = maybe_evaluate(p->value.binop.p1);   
            if ((eval < -128) || (eval > 127)) {
              gperror(GPE_RANGE, NULL);
            } else {
              new_class = true;
              coff_class = eval;   
            }
          } else {
            gperror(GPE_ILLEGAL_ARGU, NULL);
          }
        }
      } else {
        if (enforce_simple(p)) {
	  if (strcasecmp(p->value.symbol, "absolute") == 0) {
            type = gvt_absolute;
            value = 0;   
  	  } else if (strcasecmp(p->value.symbol, "debug") == 0) {
            type = gvt_debug;
            value = 0;   
	  } else if (strcasecmp(p->value.symbol, "extern") == 0) {
            type = gvt_extern;
            value = 0;   
  	  } else if (strcasecmp(p->value.symbol, "global") == 0) {
            type = gvt_global;
            value = state.org << shift;
  	  } else if (strcasecmp(p->value.symbol, "static") == 0) {
            type = gvt_static;
            value = state.org << shift;
  	  } else {
            gperror(GPE_ILLEGAL_ARGU, NULL);
          }
        }
      }
    }
  }

  set_global(symbol_name, value, PERMANENT, type);

  /* update the symbol with the values */
  if ((state.pass == 2) && (new_class || new_type)) {
    coff_symbol = gp_coffgen_findsymbol(state.obj.object, symbol_name);
    assert(coff_symbol != NULL);
    if (new_class)
      coff_symbol->class = coff_class;
      
    if (new_type)
      coff_symbol->type = coff_type;
  }

  return r;
}

static gpasmVal do_dim(gpasmVal r,
                       char *name,
                       int arity,
                       struct pnode *parms)
{
  struct pnode *p;
  char *symbol_name = NULL;
  gp_symbol_type *coff_symbol = NULL;
  int number_symbols;
  gp_aux_type *aux_list;
  struct sllist first_list;
  struct sllist *list = &first_list;
  struct sllist *previous;
  int i;
  int value;

  state.lst.line.linetype = dir;

  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    if (arity < 3) { 
      enforce_arity(arity, 1);
      return r;
    }
    
    /* the first argument is the symbol name */
    p = HEAD(parms);
    if (enforce_simple(p)) {
      /* lookup the symbol */
      symbol_name = p->value.symbol;
      coff_symbol = gp_coffgen_findsymbol(state.obj.object, symbol_name);
      if (coff_symbol == NULL) {
        gperror(GPE_NOSYM, NULL);
        return r;
      }
    } else {
      return r;
    }
    parms = TAIL(parms);

    /* the second argument must be the number of aux symbols */
    p = HEAD(parms);
    number_symbols = maybe_evaluate(p);
    if ((number_symbols < 0) || (number_symbols > 127)) {
      gperror(GPE_UNKNOWN, "number of auxiliary symbols must be less then 128 and positive"); 
      return r;
    }
    state.obj.symbol_num += number_symbols;
    parms = TAIL(parms);

    /* create the symbols */
    aux_list = gp_coffgen_blockaux(number_symbols);
    coff_symbol->num_auxsym = number_symbols;
    coff_symbol->aux_list = aux_list;

    /* convert the arguments into a list of values */
    list->next = NULL;
    simplify_data(parms, list, 0);

    /* write the data to the auxiliary symbols */
    list = list->next;
    i = 0;
    while(list) {
      value = maybe_evaluate(list->p);
      if (value & (~0xff)) {
        gperror(GPE_RANGE, NULL); 
        return r;
      }
      
      if (aux_list == NULL) {
        gperror(GPE_UNKNOWN, "insufficent number of auxiliary symbols"); 
        return r;
      }
      
      if (i == SYMBOL_SIZE) {
        i = 0;
        aux_list = aux_list->next;      
      } else {
        aux_list->_aux_symbol.data[i++] = value;
      }

      previous = list;
      list = list->next;
      free(previous);
    }
  }

  return r;
}

static gpasmVal do_direct(gpasmVal r,
		          char *name,
		          int arity,
		          struct pnode *parms)
{

  struct pnode *p;
  int value;
  unsigned char direct_command = 0;
  char *direct_string = NULL;

  state.lst.line.linetype = dir;

  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else if (enforce_arity(arity, 2)) {
    p = HEAD(parms);
    value = maybe_evaluate(p);
    if ((value < 0) || (value > 255)) {
      gperror(GPE_RANGE, NULL);
    } else {
      direct_command = value;   
    }

    p= HEAD(TAIL(parms));
    if (p->tag == string) {
      if (strlen(p->value.string) < 255) {
        direct_string = convert_escaped_char(p->value.string,'"');
      } else {
        gperror(GPE_UNKNOWN, "string must be less than 255 bytes long");
      }
    } else {
      gperror(GPE_ILLEGAL_ARGU, NULL);
    }  

    if (direct_string == NULL) {
      return r;
    }

    if (SECTION_FLAGS & STYP_TEXT) {
      coff_add_directsym(direct_command, direct_string);
    } else {
      gperror(GPE_WRONG_SECTION, NULL);
    }
  
  }

  return r;
}

static gpasmVal do_dt(gpasmVal r,
		      char *name,
		      int arity,
		      struct pnode *parms)
{
  struct symbol *s;
  struct insn *i;

  struct sllist list;


  s = get_symbol(state.stBuiltin, "RETLW");
  assert(s != NULL); /* Every PIC has a RETLW instruction */
  i = get_symbol_annotation(s);

  list.next = NULL;
  simplify_data(parms, &list, 0);
  data(&list, i->opcode, 0xff);

  return r;
}

/*-------------------------------------------------------------------------
 *  do_dw - The 'dw' directive. On all families except for the p18cxxx, the
 *          dw directive is the same as the 'data' directive. For the p18cxxx
 *          it's the same as the 'db' directive. (That's strange, but it's
 *          also the way mpasm does it).
 */
static gpasmVal do_dw(gpasmVal r,
		      char *name,
		      int arity,
		      struct pnode *parms)
{

  struct sllist list;

  list.next = NULL;
  simplify_data(parms, &list, 1);

  if ((state.mode == relocatable) &&
      !(SECTION_FLAGS & STYP_TEXT)) {
    /* This is a data memory not program */
    state.lst.line.linetype = res;

    /* only valid in initialized data sections */
    if (SECTION_FLAGS & STYP_BSS)
      gperror(GPE_WRONG_SECTION, NULL);

    /* data memory is byte sized so split the data */  
    data(&list, 0, SPLIT_PACK);
  } else {
    data(&list, 0, DEFAULT_LIT_MASK);
  }
  

  return r;
}


static gpasmVal do_else(gpasmVal r,
			char *name,
			int arity,
			struct pnode *parms)
{
  state.lst.line.linetype = dir;
  if (state.astack == NULL)
    gperror(GPE_ILLEGAL_COND, NULL);
  else if ((state.astack->mode != in_then))
    gperror(GPE_ILLEGAL_COND, NULL);
  else
    state.astack->enabled = !state.astack->enabled;

  return r;
}

static gpasmVal do_endif(gpasmVal r,
			 char *name,
			 int arity,
			 struct pnode *parms)
{
  state.lst.line.linetype = dir;
  if (state.astack == NULL) {
    gperror(GPE_ILLEGAL_COND, "Illegal condition (ENDIF).");
  } else if ((state.astack->mode != in_then) &&
	     (state.astack->mode != in_else)) {
    gperror(GPE_ILLEGAL_COND, "Illegal condition (ENDIF).");
  } else {
    struct amode *old;

    old = state.astack;
    state.astack = state.astack->prev;
    free(old);
  }
  return r;
}

static gpasmVal do_endm(gpasmVal r,
			char *name,
			int arity,
			struct pnode *parms)
{
  assert(!state.mac_head);
  state.lst.line.linetype = dir;
  if (state.mac_prev == NULL)
    gperror(GPE_UNMATCHED_ENDM, NULL);
  else
    state.mac_prev = NULL;

  state.mac_body = NULL;

  return r;
}

static gpasmVal do_endw(gpasmVal r,
			char *name,
			int arity,
			struct pnode *parms)
{
  state.lst.line.linetype = dir;

  assert(!state.mac_head);
  if (state.mac_prev == NULL) {
    gperror(GPE_ILLEGAL_COND, "Illegal condition (ENDW).");
  } else if (maybe_evaluate(state.while_head->parms)) {
    state.next_state = state_while;  
    state.next_buffer.macro = state.while_head;
  }

  state.mac_body = NULL;
  state.mac_prev = NULL;
  state.while_head = NULL;

  return r;
}

static gpasmVal do_eof(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  state.lst.line.linetype = dir;

  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else if (enforce_arity(arity, 0)) {
    if (state.debug_info) { 
      coff_add_eofsym();
    } else {
      gpwarning(GPW_UNKNOWN, "directive ignored when debug info is disabled");
    }
  }

  return r;
}

static gpasmVal do_equ(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = equ;
  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (can_evaluate(p))
      r = evaluate(p);
  }

  return r;
}

static gpasmVal do_error(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = dir;
  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (p->tag == string) {
      gperror(GPE_USER, p->value.string);
    } else {
      gperror(GPE_ILLEGAL_ARGU, NULL);
    }
  }

  return r;
}

/************************************************************************
 * do_errlvl - parse the ERRORLEVEL directive
 *
 * If the file gpasm is assembling contains a ERRORLEVEL directive, then scan
 * the comma delimited list of options in *parms
 *
 * Inputs:
 *   gpasmVal r - not used, but is returned
 *   char *name - not used, but contains the directive name 'list'.
 *   int arity - not used, but should contain '1'
 *   struct pnode *parms - a linked list of the parsed parameters
 *
 *
 *
 ************************************************************************/

static gpasmVal do_errlvl(gpasmVal r,
                          char *name,
                          int arity,
                          struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = dir;

  if (state.pass == 2) {
    for (; parms; parms = TAIL(parms)) {
      p = HEAD(parms);
      if (p->tag == unop)  {
        gpasmVal value = evaluate(p->value.unop.p0);
       
        if (p->value.unop.op == '-') {
          add_code(-value);
        } else if (p->value.unop.op == '+') {
          add_code(value);	    
        } else {
          gperror(GPE_ILLEGAL_ARGU, "Expected 0, 1, 2, +|-<message number>");
        }
      } else if (p->tag == constant) {
        select_errorlevel(p->value.constant);
      } else {
        gperror(GPE_ILLEGAL_ARGU, "Expected 0, 1, 2, +|-<message number>");
      }
    }
  }

  return r;
}

static gpasmVal do_exitm(gpasmVal r,
			 char *name,
			 int arity,
			 struct pnode *parms)
{
  state.lst.line.linetype = dir;
  if (enforce_arity(arity, 0)) {
    if (state.stGlobal == state.stTop) {
      gperror(GPE_UNKNOWN, "Attempt to use \"exitm\" outside of macro");
    } else {
      state.next_state = state_exitmacro;
    }
  }

  return r;
}

static gpasmVal do_expand(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  state.lst.line.linetype = dir;
  if (state.cmd_line.macro_expand) {
    gpmessage(GPM_SUPLIN, NULL);
  } else {
    if (enforce_arity(arity, 0)) {
      state.lst.expand = true;
    }
  }
  return r;
}

static gpasmVal do_extern(gpasmVal r,
		          char *name,
		          int arity,
		          struct pnode *parms)
{
  char *p;
  state.lst.line.linetype = dir;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    for (; parms; parms = TAIL(parms)) {
      p = maybe_evaluate_concat(HEAD(parms));
      if (p) {
        set_global(p, 0, PERMANENT, gvt_extern);
      }
    }
  }

  return r;
}

static gpasmVal do_file(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = dir;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else if (enforce_arity(arity, 1)) {
    if (state.debug_info) {
      p = HEAD(parms);
      if (p->tag == string) {
        state.obj.debug_file = coff_add_filesym(p->value.string, 0);
      } else {
        gperror(GPE_ILLEGAL_ARGU, NULL);
      }
    } else {
      gpwarning(GPW_UNKNOWN, "directive ignored when debug info is disabled");
    }
  }

  return r;
}

/* Filling constants is handled here. Filling instructions is handled
   in the parser. */ 
   
static gpasmVal do_fill(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *h;
  int number;
  int i;

  if (enforce_arity(arity, 2)) {
    h = HEAD(parms);
    number = eval_fill_number(HEAD(TAIL(parms)));

    for (i = 1; i <= number ; i += 1) {
      /* we must evaluate each loop, because some symbols change (i.e. $) */
      emit(maybe_evaluate(h));
    }
  }
  return r;
}

static gpasmVal do_global(gpasmVal r,
		          char *name,
		          int arity,
		          struct pnode *parms)
{
  char *p;
  char buf[BUFSIZ];
  struct symbol *s;
  struct variable *var;

  state.lst.line.linetype = dir;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    for (; parms; parms = TAIL(parms)) {
      p = maybe_evaluate_concat(HEAD(parms));
      if (p) {
        s = get_symbol(state.stTop, p);
        if (s == NULL) {
          snprintf(buf,
                   sizeof(buf),
                   "Symbol not previously defined (%s).",
                   p);
          gperror(GPE_NOSYM, buf);    
	} else {
          var = get_symbol_annotation(s);
          if (var == NULL) {
            snprintf(buf,
                     sizeof(buf),
                     "Symbol not assigned a value (%s).",
                     p);
            gpwarning(GPW_UNKNOWN, buf);    
          } else {
            if ((var->previous_type == gvt_address) || 
                (var->previous_type == gvt_global)  ||
                (var->previous_type == gvt_static)) {
              /* make the symbol global */
              var->type = gvt_global;
            } else if (var->previous_type == gvt_extern) { 
              gperror(GPE_DUPLAB, NULL);    
            } else {
              snprintf(buf,
                       sizeof(buf),
                       "Operand must be an address label (%s).", 
                       p);
              gperror(GPE_MUST_BE_LABEL, buf);    
            } 
          }         
        }  
      } else {
        gperror(GPE_ILLEGAL_ARGU, NULL);
      }
    }  
  }

  return r;
}

static gpasmVal do_idata(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = sec;
  state.next_state = state_section;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    switch (arity) {
    case 0:
      /* new relocatable section */
      strncpy(state.obj.new_sec_name, ".idata", sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = 0;
      state.obj.new_sec_flags = STYP_DATA;
      break;
    case 1:
      /* new absolute section */
      p = HEAD(parms);
      strncpy(state.obj.new_sec_name, ".idata", sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = maybe_evaluate(p) >> _16bit_core;
      state.obj.new_sec_flags = STYP_DATA | STYP_ABS;
      break;
    default:
      enforce_arity(arity, 1);
    }  
  }

  return r;
}

static gpasmVal do_ident(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = dir;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (p->tag == string) {
      coff_add_identsym(p->value.string);
    } else {
      gperror(GPE_ILLEGAL_ARGU, NULL);
    }
  }

  return r;
}

static gpasmVal do_idlocs(gpasmVal r,
		          char *name,
		          int arity,
		          struct pnode *parms)
{
  int value;
  int idreg;
  int curvalue;
  int mask;

  if (_17cxx_core) {
    gperror(GPE_ILLEGAL_DIR, NULL);
    return r;
  }

  if (_16bit_core) {
    if (enforce_arity(arity,2)) {
      idreg = maybe_evaluate(HEAD(parms));
      value = maybe_evaluate(HEAD(TAIL(parms)));
    } else {
      gperror(GPW_EXPECTED,"18cxxx devices should specify __IDLOC address");
      return r;
    }
  } else {
    if (enforce_arity(arity,1)) {
      idreg = state.device.id_location;
      value = maybe_evaluate(HEAD(parms));
    } else {
      return r;
    }
  }

  if ((state.mode == relocatable) && (!state.found_idlocs)) {
    coff_new_section(".idlocs", idreg >> _16bit_core, STYP_ABS | STYP_TEXT);
    state.found_idlocs = true;
  }

  state.lst.config_address = idreg;
  state.device.id_location = idreg;

  if (state.pass == 2) {
    if (_16bit_core) {
      state.lst.line.linetype = config;

      if (idreg > IDLOC7 || idreg < IDLOC0) {
        gperror(GPE_RANGE,NULL);
      } else {
        if(value > 0xff) {
          gpwarning(GPW_RANGE, NULL);
        }
        curvalue = i_memory_get(state.c_memory, idreg>>1);
        mask = 0xff <<((idreg&1) ? 0 : 8);
	
        /* If the address is even, then this byte goes in LSB position */
        value = (value & 0xff) << ((idreg&1) ? 8 : 0)  | MEM_USED_MASK; 

        if(curvalue & MEM_USED_MASK)
          curvalue &= mask;
        else
          curvalue |= mask;

        i_memory_put(state.c_memory, idreg>>1,  curvalue | value);
      }

    } else {
      state.lst.line.linetype = idlocs;

      if (value > 0xffff) {
        gpmessage(GPM_IDLOC, NULL);
        value &= 0xffff;
      }     
      
      if (i_memory_get(state.c_memory, idreg) & MEM_USED_MASK) {
        gperror(GPE_ADDROVR, NULL);
      }

      i_memory_put(state.c_memory, idreg,     
                   ((value & 0xf000) >> 12) | MEM_USED_MASK);
      i_memory_put(state.c_memory, idreg + 1,
                   ((value & 0x0f00) >> 8) | MEM_USED_MASK);
      i_memory_put(state.c_memory, idreg + 2,
                   ((value & 0x00f0) >> 4) | MEM_USED_MASK);
      i_memory_put(state.c_memory, idreg + 3,
                   (value & 0x000f) | MEM_USED_MASK);

    }
  }
  
  return r;
}

static gpasmVal do_if(gpasmVal r,
		      char *name,
		      int arity,
		      struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = dir;
  enter_if();

  /* Only evaluate the conditional if it matters... */
  if (state.astack->prev_enabled) {
    if (enforce_arity(arity, 1)) {
      p = HEAD(parms);
      state.astack->enabled = maybe_evaluate(p);
    }
  }

  return r;
}

static gpasmVal do_ifdef(gpasmVal r,
			 char *name,
			 int arity,
			 struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = dir;
  enter_if();

  /* Only evaluate the conditional if it matters... */
  if (state.astack->prev_enabled) {
    if (enforce_arity(arity, 1)) {
      p = HEAD(parms);
      if (p->tag != symbol) {
	gperror(GPE_ILLEGAL_LABEL, NULL);
      } else {
	if ((get_symbol(state.stDefines, p->value.symbol)) ||
            (get_symbol(state.stTop, p->value.symbol)))
	  state.astack->enabled = 1;
      }
    }
  }

  return r;
}

static gpasmVal do_ifndef(gpasmVal r,
			  char *name,
			  int arity,
			  struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = dir;
  enter_if();

  /* Only evaluate the conditional if it matters... */
  if (state.astack->prev_enabled) {
    if (enforce_arity(arity, 1)) {
      p = HEAD(parms);
      if (p->tag != symbol) {
	gperror(GPE_ILLEGAL_LABEL, NULL);
      } else {
	if ((!get_symbol(state.stDefines, p->value.symbol)) &&
            (!get_symbol(state.stTop, p->value.symbol)))
	  state.astack->enabled = 1;
      }
    }
  }

  return r;
}

static gpasmVal do_include(gpasmVal r,
		           char *name,
		           int arity,
		           struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = dir;

  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (p->tag == string) {
      state.next_state = state_include;  
      state.next_buffer.file = strdup(p->value.string);
    } else {
      gperror(GPE_ILLEGAL_ARGU, NULL);
    }
  }

  return r;
}

static gpasmVal do_line(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = dir;

  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else if (enforce_arity(arity, 1)) {
    if (state.debug_info) {
      p = HEAD(parms);
      state.obj.debug_line = maybe_evaluate(p);
    } else {
      gpwarning(GPW_UNKNOWN, "directive ignored when debug info is disabled");
    }
  }

  return r;
}

/************************************************************************
 * do_list - parse the LIST directive
 *
 * If the file gpasm is assembling contains a LIST directive, then scan
 * and parse will call do_list and pass the comma delimited list of LIST
 * options in *parms
 *
 * Inputs:
 *   gpasmVal r - not used, but is returned
 *   char *name - not used, but contains the directive name 'list'.
 *   int arity - not used, but should contain '1'
 *   struct pnode *parms - a linked list of the parsed parameters
 *
 *
 *
 ************************************************************************/

static gpasmVal do_list(gpasmVal r,
			char *name,
			int arity,
			struct pnode *parms)
{
  struct pnode *p;

  state.lst.enabled = true;
  state.lst.line.linetype = dir;

  for (; parms; parms = TAIL(parms)) {
    p = HEAD(parms);
    if ((p->tag == binop) &&
	(p->value.binop.op == '=')) {
      if (enforce_simple(p->value.binop.p0)) {
	char *lhs;

	lhs = p->value.binop.p0->value.symbol;
	if (strcasecmp(lhs, "b") == 0) {
	  int b;

	  b = maybe_evaluate(p->value.binop.p1);
	  if (b != 0)
	    state.lst.tabstop = b;
	} else if (strcasecmp(lhs, "c") == 0) {
	  ; /* Ignore this for now: column width not used */
	} else if (strcasecmp(lhs, "f") == 0) { 
	  if (enforce_simple(p->value.binop.p1))
            select_hexformat(p->value.binop.p1->value.symbol);
	} else if (strcasecmp(lhs, "l") == 0) {
	  ; /* Ignore this for now: page length */
	} else if (strcasecmp(lhs, "mm") == 0) {
	  state.lst.memorymap = off_or_on(p->value.binop.p1);
	} else if (strcasecmp(lhs, "n") == 0) {
          if (can_evaluate(p->value.binop.p1)) {
            int number = evaluate(p->value.binop.p1);

            if ((number > 6) || (number == 0)) {
              state.lst.linesperpage = number;
            } else {
              gperror(GPE_RANGE, NULL);
            } 
          }
	} else if (strcasecmp(lhs, "p") == 0) {
	  if (enforce_simple(p->value.binop.p1))
	    select_processor(p->value.binop.p1->value.symbol);
	} else if (strcasecmp(lhs, "pe") == 0) {
          state.extended_pic16e = true;
	  if (enforce_simple(p->value.binop.p1))
	    select_processor(p->value.binop.p1->value.symbol);
	} else if (strcasecmp(lhs, "r") == 0) {
	  if (enforce_simple(p->value.binop.p1))
	    select_radix(p->value.binop.p1->value.symbol);
	} else if (strcasecmp(lhs, "st") == 0) {
	  state.lst.symboltable = off_or_on(p->value.binop.p1);
	} else if (strcasecmp(lhs, "t") == 0) {
	  ; /* Ignore this for now: always wrap long list lines */
	} else if (strcasecmp(lhs, "w") == 0) {
          select_errorlevel(maybe_evaluate(p->value.binop.p1));
	} else if (strcasecmp(lhs, "x") == 0) {
	  if (enforce_simple(p->value.binop.p1))
            select_expand(p->value.binop.p1->value.symbol);
        } else {
          gperror(GPE_ILLEGAL_ARGU, NULL);
        }
      }
    } else {
      if (enforce_simple(p)) {
	if (strcasecmp(p->value.symbol, "free") == 0) {
	  ; /* Ignore this directive */
	} else if (strcasecmp(p->value.symbol, "fixed") == 0) {
	  ; /* Ignore this directive */
	} else if (strcasecmp(p->value.symbol, "nowrap") == 0) {
	  ; /* Ignore this directive */
	} else if (strcasecmp(p->value.symbol, "wrap") == 0) {
	  ; /* Ignore this directive */
	} else {
          gperror(GPE_ILLEGAL_ARGU, NULL);
        }
      }
    }
  }

  /* The .list symbol is only added to the COFF file if its only action is to turn on
     the listing */
  if (arity == 0)
    coff_add_listsym();

  return r;
}

static gpasmVal do_local(gpasmVal r,
			 char *name,
			 int arity,
			 struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = dir;

  /* like variable except it is put in TOP instead of GLOBAL */

  if (state.stGlobal == state.stTop) {
    gperror(GPE_UNKNOWN, "Attempt to use \"local\" outside of macro");
  } else {
    for (; parms; parms = TAIL(parms)) {
      p = HEAD(parms);
      if ((p->tag == binop) &&
          (p->value.binop.op == '=')) {
        if (enforce_simple(p->value.binop.p0)) {
          char *lhs;
          gpasmVal value;
          /* fetch the symbol */
          lhs = p->value.binop.p0->value.symbol;
          value = maybe_evaluate(p->value.binop.p1);
          /* put the symbol and value in the TOP table*/
          add_symbol(state.stTop, lhs);
          set_global(lhs, value, TEMPORARY, gvt_constant);
        }
      } else if (p->tag == symbol) {
        /* put the symbol in the Top table */
        add_symbol(state.stTop, p->value.symbol);
      } else {
        gperror(GPE_ILLEGAL_ARGU, NULL);
      }
    }
  }

  return r;
}

static gpasmVal do_noexpand(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  state.lst.line.linetype = dir;
  if (state.cmd_line.macro_expand) {
    gpmessage(GPM_SUPLIN, NULL);
  } else {
    if (enforce_arity(arity, 0)) {
      state.lst.expand = false;
    }
  }
  return r;
}

static gpasmVal do_nolist(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  state.lst.line.linetype = dir;
  
  if (!state.lst.force)
    state.lst.enabled = false;
    
  coff_add_nolistsym();
  
  return r;
}

static gpasmVal do_maxram(gpasmVal r,
			  char *name,
			  int arity,
			  struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = dir;
  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (can_evaluate(p))
      state.maxram = evaluate(p);
  }

  return r;
}

static gpasmVal do_maxrom(gpasmVal r,
			  char *name,
			  int arity,
			  struct pnode *parms)
{

  state.lst.line.linetype = dir;

  /* FIXME: implement this directive */
  gpwarning(GPW_UNKNOWN, "gpasm doesn't support the maxrom directive yet"); 

  return r;
}

static gpasmVal do_macro(gpasmVal r,
			 char *name,
			 int arity,
			 struct pnode *parms)
{
  struct macro_head *head = malloc(sizeof(*head));

  head->parms = parms;
  head->body = NULL;
  head->defined = 0;
  /* Record data for the list, cod, and coff files */
  head->line_number = state.src->line_number;
  head->file_symbol = state.src->file_symbol;

  head->src_name = strdup(state.src->name);
  
  state.lst.line.linetype = dir;
  if (macro_parms_ok(parms))
    state.mac_head = head;

  state.mac_prev = &(head->body);
  state.mac_body = NULL;

  return r;
}

static gpasmVal do_messg(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;
  state.lst.line.linetype = dir;
  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (p->tag == string) {
      gpmessage(GPM_USER, p->value.string);
    } else {
      gperror(GPE_ILLEGAL_ARGU, NULL);
    }
  }

  return r;
}

static gpasmVal do_org(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = org;

  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (can_evaluate(p)) {
      r = evaluate(p) >> _16bit_core;
      if (state.mode == absolute) {
        state.org = r;
      }	else {
        /* Default section name, this will be overwritten if a label is 
           present. */
        snprintf(state.obj.new_sec_name,
                 sizeof(state.obj.new_sec_name),
                 ".org_%x",
                 r);
        state.obj.new_sec_addr = r;
        state.obj.new_sec_flags = STYP_TEXT | STYP_ABS;
        state.lst.line.linetype = sec;
        state.next_state = state_section;
      }
    }
  }

  return r;
}

static gpasmVal do_page(gpasmVal r,
			char *name,
			int arity,
			struct pnode *parms)
{
  state.lst.line.linetype = dir;
  if (enforce_arity(arity, 0))
    lst_throw();

  return r;
}

static gpasmVal do_pagesel(gpasmVal r,
		           char *name,
		           int arity,
		           struct pnode *parms)
{
  struct pnode *p;
  int address;
  int page;
  int num_reloc;
  
  if ((state.device.class == PROC_CLASS_EEPROM8) ||
      (state.device.class == PROC_CLASS_PIC16E)) {
    /* do nothing */
    return r;
  } else if (state.device.class == PROC_CLASS_PIC16) {
    gpmessage(GPM_W_MODIFIED, NULL);
  }

  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (state.mode == absolute) {
      address = maybe_evaluate(p);
      page = gp_processor_check_page(state.device.class, address);
      state.org += gp_processor_set_page(state.device.class, 
                                         state.processor_info->num_pages,
                                         page, 
                                         state.i_memory, 
                                         state.org);
    } else {
      num_reloc = count_reloc(p);
      
      if (num_reloc == 0) {
        /* it is an absolute address, generate the pagesel but no relocation */
        address = maybe_evaluate(p);
        page = gp_processor_check_page(state.device.class, address);

        state.org += gp_processor_set_page(state.device.class, 
                                           state.processor_info->num_pages,
                                           page, 
                                           state.i_memory, 
                                           state.org);
      } else if (num_reloc != 1) {
        gperror(GPE_ILLEGAL_LABEL, NULL);
      } else if (state.device.class == PROC_CLASS_PIC16) {
        reloc_evaluate(p, RELOCT_PAGESEL_WREG);
        emit(0);
        emit(0);
      } else {
        switch (state.processor_info->num_pages) {
        case 2:
          reloc_evaluate(p, RELOCT_PAGESEL_BITS);
          emit(0);
          break;      
        case 4:
          reloc_evaluate(p, RELOCT_PAGESEL_BITS);
          emit(0);
          emit(0);
          break;
        }
      }
    }

  }

  return r;
}

static gpasmVal do_processor(gpasmVal r,
			     char *name,
			     int arity,
			     struct pnode *parms)
{
  state.lst.line.linetype = dir;
  if (enforce_arity(arity, 1)) {
    struct pnode *p = HEAD(parms);
    
    if (enforce_simple(p))
      select_processor(p->value.symbol);
  }

  return r;
}

static gpasmVal do_radix(gpasmVal r,
			 char *name,
			 int arity,
			 struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = dir;
  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (enforce_simple(p)) {
      select_radix(p->value.symbol);
    }
  }

  return r;
}

static gpasmVal do_res(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;
  int count;
  int i;

  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (can_evaluate(p)) {
      count = evaluate(p);
      r = state.org;

      if (state.mode == absolute) {
        state.lst.line.linetype = equ;
        state.org += (count >> _16bit_core);
      } else {
        state.lst.line.linetype = res;
        if (SECTION_FLAGS & STYP_TEXT)
          count >>= _16bit_core;

        for (i = 0; i < count; i++) {
          if (SECTION_FLAGS & STYP_TEXT) {
            /* For some reason program memory is filled with a different 
               value. */
            emit(state.device.core_size);     
          } else {
            emit(0);     
          }
        }
      }
    }
  }

  return r;
}

static gpasmVal do_set(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = set;
  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (can_evaluate(p))
      r = evaluate(p);
  }

  return r;
}

static gpasmVal do_space(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = dir;

  if (state.pass == 2) {
    switch (arity) {
      case 0:
        /* do nothing */
        break;
      case 1:
        p = HEAD(parms);
        if (can_evaluate(p)) {
	  int i;
	
	  for (i = evaluate(p); i > 0; i--)
	    lst_line("");
        }
        break;
      default:
        enforce_arity(arity, 1);
    }
  }

  return r;
}

static gpasmVal do_type(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{
  struct pnode *p;
  char *symbol_name = NULL;
  gp_symbol_type *coff_symbol = NULL;
  int value;

  state.lst.line.linetype = dir;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else if (enforce_arity(arity, 2)) {
    /* the first argument is the symbol name */
    p = HEAD(parms);
    if (enforce_simple(p)) {
      symbol_name = p->value.symbol;
      coff_symbol = gp_coffgen_findsymbol(state.obj.object, symbol_name);
      if (coff_symbol == NULL) {
        gperror(GPE_NOSYM, NULL);
      } else {
        p = HEAD(TAIL(parms));
        value = maybe_evaluate(p);   
        if ((value < 0) || (value > 0xffff)) {
          gperror(GPE_RANGE, NULL);
        } else {
          coff_symbol->type = value;   
        }
      }
    }
  }

  return r;
}

static gpasmVal do_udata(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = sec;
  state.next_state = state_section;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    switch (arity) {
    case 0:
      /* new relocatable section */
      strncpy(state.obj.new_sec_name, ".udata", sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = 0;
      state.obj.new_sec_flags = STYP_BSS;
      break;
    case 1:
      /* new absolute section */
      p = HEAD(parms);
      strncpy(state.obj.new_sec_name, ".udata", sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = maybe_evaluate(p);
      state.obj.new_sec_flags = STYP_BSS | STYP_ABS;
      break;
    default:
      enforce_arity(arity, 1);
    }
  }

  return r;
}

static gpasmVal do_udata_acs(gpasmVal r,
		             char *name,
		             int arity,
		             struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = sec;
  state.next_state = state_section;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    switch (arity) {
    case 0:
      /* new relocatable section */
      strncpy(state.obj.new_sec_name,
              ".udata_acs",
              sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = 0;
      state.obj.new_sec_flags = STYP_BSS | STYP_ACCESS;
      break;
    case 1:
      /* new absolute section */
      p = HEAD(parms);
      strncpy(state.obj.new_sec_name,
              ".udata_acs",
              sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = maybe_evaluate(p);
      state.obj.new_sec_flags = STYP_BSS | STYP_ABS | STYP_ACCESS;
      break;
    default:
      enforce_arity(arity, 1);
    }
  }

  return r;
}

static gpasmVal do_udata_ovr(gpasmVal r,
		             char *name,
		             int arity,
		             struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = sec;
  state.next_state = state_section;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    switch (arity) {
    case 0:
      /* new relocatable section */
      strncpy(state.obj.new_sec_name,
              ".udata_ovr",
              sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = 0;
      state.obj.new_sec_flags = STYP_BSS | STYP_OVERLAY;
      break;
    case 1:
      /* new absolute section */
      p = HEAD(parms);
      strncpy(state.obj.new_sec_name,
              ".udata_ovr",
              sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = maybe_evaluate(p);
      state.obj.new_sec_flags = STYP_BSS | STYP_ABS | STYP_OVERLAY;
      break;
    default:
      enforce_arity(arity, 1);
    }
  }

  return r;
}

static gpasmVal do_udata_shr(gpasmVal r,
		             char *name,
		             int arity,
		             struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = sec;
  state.next_state = state_section;
  
  if (state.mode == absolute) {
    gperror(GPE_OBJECT_ONLY, NULL);
  } else {
    switch (arity) {
    case 0:
      /* new relocatable section */
      strncpy(state.obj.new_sec_name,
              ".udata_shr",
              sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = 0;
      state.obj.new_sec_flags = STYP_BSS | STYP_SHARED;
      break;
    case 1:
      /* new absolute section */
      p = HEAD(parms);
      strncpy(state.obj.new_sec_name,
              ".udata_shr",
              sizeof(state.obj.new_sec_name));
      state.obj.new_sec_addr = maybe_evaluate(p);
      state.obj.new_sec_flags = STYP_BSS | STYP_ABS | STYP_SHARED;
      break;
    default:
      enforce_arity(arity, 1);
    }
  }

  return r;
}

static gpasmVal do_undefine(gpasmVal r,
		            char *name,
		            int arity,
		            struct pnode *parms)
{
  struct pnode *p;

  state.lst.line.linetype = dir;

  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    if (p->tag == symbol) {
      if (remove_symbol(state.stDefines, p->value.symbol) == 0)
        gpwarning(GPW_NOT_DEFINED, NULL);
    } else {
      gperror(GPE_ILLEGAL_ARGU, NULL);
    }
  }

  return r;
}

static gpasmVal do_variable(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{
  struct pnode *p;
  int value;

  state.lst.line.linetype = dir;

  for (; parms; parms = TAIL(parms)) {
    p = HEAD(parms);
    if ((p->tag == binop) &&
        (p->value.binop.op == '=')) {
      if (enforce_simple(p->value.binop.p0)) {
        char *lhs;
        /* fetch the symbol */
        lhs = p->value.binop.p0->value.symbol;
        value = maybe_evaluate(p->value.binop.p1);
        /* put the symbol and value in the table*/
        set_global(lhs, value, TEMPORARY, gvt_constant);
      }
    } else if (p->tag == symbol) {
      /* put the symbol with a 0 value in the table*/
      set_global(p->value.symbol, 0, TEMPORARY, gvt_constant);
    } else {
      gperror(GPE_ILLEGAL_ARGU, NULL);
    }
  }

  return r;
}

static gpasmVal do_while(gpasmVal r,
			 char *name,
			 int arity,
			 struct pnode *parms)
{
  struct macro_head *head = malloc(sizeof(*head));
  struct pnode *p;

  if (state.src->type == src_while) {
    state.pass = 2; /* Ensure error actually gets displayed */
    gperror(GPE_UNKNOWN, "gpasm does not yet support nested while loops");
    exit (1);
  }
  
  state.lst.line.linetype = dir;
  if (enforce_arity(arity, 1)) {
    p = HEAD(parms);
    head->parms = p;
  } else {
    head->parms = NULL;
  }

  head->body = NULL;
  /* Record data for the list, cod, and coff files */
  head->line_number = state.src->line_number;
  head->file_symbol = state.src->file_symbol;

  head->src_name = strdup(state.src->name);

  /* DON'T set up state.mac_head; this isn't a macro head. */
  state.while_head = head;

  state.mac_prev = &(head->body);
  state.mac_body = NULL;

  return r;
}

int asm_enabled(void)
{
  return ((state.astack == NULL) ||
	  (state.astack->enabled &&
	   state.astack->prev_enabled));
}

/* Check that a register file address is ok */
void file_ok(unsigned int file)
{
  /* don't check address, the linker takes care of it */   
  if (state.mode == relocatable)
    return;

  if ((0 > file) || (file > state.maxram) || (state.badram[file])) {
    gpwarning(GPW_INVALID_RAM, NULL);
  }

  /* Issue bank message if necessary */
  switch (state.device.class) {
  case PROC_CLASS_EEPROM8:
    /* do nothing */
    break;
  case PROC_CLASS_GENERIC:
  case PROC_CLASS_PIC12:
  case PROC_CLASS_SX:
    if (file & (~0x1f))
      gpmessage(GPM_BANK, NULL);   
    break;
  case PROC_CLASS_PIC14:
    if (file & (~0x7f))
      gpmessage(GPM_BANK, NULL);  
    break;
  case PROC_CLASS_PIC16:
    if (file & (~0xff))
      gpmessage(GPM_BANK, NULL);  
    break;
  case PROC_CLASS_PIC16E:
    /* do nothing */
    break;
  default:
    assert(0);
  }

  return;
}

static void emit_check(int insn, int argument, int mask)
{
  int test = argument;

  if (test < 0)
    test = -test;

  /* If there are bits that shouldn't be set then issue a warning */
  if (test & (~mask)) {
    gpwarning(GPW_RANGE, NULL);
  }
  emit(insn | (argument & mask));
  
  return;
} 

/* 
   For relative branches, issue a warning if the absolute value of
   argument is greater than range
*/
static void emit_check_relative(int insn, int argument, int mask, int range)
{
  char full_message[BUFSIZ];

  /* If the branch is too far then issue an error */
  if ((argument > range) || (argument < -(range+1))) {
    snprintf(full_message,
             sizeof(full_message),
             "Argument out of range (%d not between %d and %d)\n",
             argument,
             -(range+1),
             range);
    gperror(GPE_RANGE, full_message);
  }

  emit(insn | (argument & mask));
  
  return;
} 

static int check_flag(int flag)
{
  if ((flag != 0) && (flag != 1))
    gpwarning(GPW_RANGE, NULL);

  return flag & 0x1;
}

gpasmVal do_insn(char *name, struct pnode *parms)
{
  struct symbol *s;
  int arity;
  struct pnode *p;
  int file;		/* register file address, if applicable */
  gpasmVal r;		/* Return value */

  r = state.org;

  arity = list_length(parms);

  s = get_symbol(state.stBuiltin, name);

  if (s) {
    struct insn *i;

    i = get_symbol_annotation(s);
    /* Interpret the instruction if assembly is enabled, or if it's a
       conditional. */
    if (asm_enabled() || (i->attribs & ATTRIB_COND)) {
      state.lst.line.linetype = insn;
      switch (i->class) {
      case INSN_CLASS_LIT3_BANK:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  emit_check(i->opcode, (reloc_evaluate(p, RELOCT_F) >> 5), 0x07);
	}
	break;
      case INSN_CLASS_LIT3_PAGE:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  emit_check(i->opcode, (reloc_evaluate(p, RELOCT_F) >> 9), 0x07);
	}
	break;
      case INSN_CLASS_LIT1:
	{
	  int s = 0;
	  switch (arity) {
	  case 1:
	    s = check_flag(reloc_evaluate(HEAD(parms), RELOCT_F));
	  case 0:
	    emit(i->opcode | s);
	    break;
	  default:
	    enforce_arity(arity, 1);
	  }
	}
	break;
      case INSN_CLASS_LIT4:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  emit_check(i->opcode, reloc_evaluate(p, RELOCT_F), 0x0f);
	}
	break;
      case INSN_CLASS_LIT4S:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  emit_check(i->opcode, (reloc_evaluate(p, RELOCT_MOVLR) << 4), 0xf0);
	}
	break;
      case INSN_CLASS_LIT6:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  /* The literal cannot be a relocatable address */
          emit_check(i->opcode, maybe_evaluate(p), 0x3f);
	}
	break;
      case INSN_CLASS_LIT8:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  if (strcasecmp(i->name, "movlb") == 0) {
	    emit_check(i->opcode, reloc_evaluate(p, RELOCT_MOVLB), 0xf);
	  } else {
	    emit_check(i->opcode, reloc_evaluate(p, RELOCT_LOW), 0xff);
	  }
	}
	break;
      case INSN_CLASS_LIT8C12:
	if (enforce_arity(arity, 1)) {
	  int value;

	  p = HEAD(parms);
	  value = reloc_evaluate(p, RELOCT_CALL);	  

	  /* PC is 11 bits.  mpasm checks the maximum device address. */
	  if (value & (~0x7ff))
	    gperror(GPE_RANGE, NULL);

	  if ((value & 0x600) != (state.org & 0x600))
	    gpmessage(GPM_PAGE, NULL);	  

	  if (value & 0x100)
	    gperror(GPE_BAD_CALL_ADDR, NULL);
	  	  
	  emit(i->opcode | (value & 0xff));	      
	}
	break;
      case INSN_CLASS_LIT8C16:
	if (enforce_arity(arity, 1)) {
	  int value;

	  p = HEAD(parms);
	  value = reloc_evaluate(p, RELOCT_LOW);	  

	  /* PC is 16 bits.  mpasm checks the maximum device address. */
	  if (value & (~0xffff))
	    gperror(GPE_RANGE, NULL); 	  
	  	  
	  emit(i->opcode | (value & 0xff));	      
	}
	break;
      case INSN_CLASS_LIT9:
	if (enforce_arity(arity, 1)) {
	  int value;

	  p = HEAD(parms);
	  value = reloc_evaluate(p, RELOCT_GOTO);

	  /* PC is 11 bits.  mpasm checks the maximum device address. */
	  if (value & (~0x7ff))
	    gperror(GPE_RANGE, NULL); 	  
	  
	  if ((value & 0x600) != (state.org & 0x600))
	    gpmessage(GPM_PAGE, NULL);	  
	  	  
	  emit(i->opcode | (value & 0x1ff));
	}
	break;
      case INSN_CLASS_LIT11:
	if (enforce_arity(arity, 1)) {
	  int value;
	  
	  p = HEAD(parms);
          if (strcasecmp(i->name, "goto") == 0) {
	    value = reloc_evaluate(p, RELOCT_GOTO);
          } else {
	    value = reloc_evaluate(p, RELOCT_CALL);
          }
	  
	  /* PC is 13 bits.  mpasm checks the maximum device address. */
	  if (value & (~0x1fff))
	    gperror(GPE_RANGE, NULL); 	  
	  
	  if ((value & 0x1800) != (state.org & 0x1800))
	    gpmessage(GPM_PAGE, NULL);
	  
	  emit(i->opcode | (value & 0x7ff));
	}
	break;
      case INSN_CLASS_LIT13:
	if (enforce_arity(arity, 1)) {
	  int value;

	  p = HEAD(parms);
          if (strcasecmp(i->name, "goto") == 0) {
	    value = reloc_evaluate(p, RELOCT_GOTO);
          } else {
	    value = reloc_evaluate(p, RELOCT_CALL);
          }

	  /* PC is 16 bits.  mpasm checks the maximum device address. */
	  if (value & (~0xffff))
	    gperror(GPE_RANGE, NULL); 	  

	  if ((value & 0xe000) != (state.org & 0xe000))
	    gpmessage(GPM_PAGE, NULL);

	  emit(i->opcode | (value & 0x1fff));
	}
	break;
      case INSN_CLASS_LITFSR:
	if (enforce_arity(arity, 2)) {
	  int value;
          int fsr;

	  p = HEAD(parms);
          fsr = maybe_evaluate(p);
	  if ((fsr < 0) || (fsr > 2))
            gperror(GPE_RANGE, NULL); 	  

          p = HEAD(TAIL(parms));
	  /* the offset cannot be a relocatable address */
          value = maybe_evaluate(p);
	  if (value & (~0x3f))
            gperror(GPE_RANGE, NULL); 	  

	  emit(i->opcode | ((fsr & 0x3) << 6) | (value & 0x3f));
	}
	break;
      case INSN_CLASS_RBRA8:
	if (enforce_arity(arity, 1)) {
	  int offset;

	  p = HEAD(parms);
	  if (count_reloc(p) == 0) {
	    offset = maybe_evaluate(p) - ((state.org + 1)<<_16bit_core);
	    offset >>= _16bit_core;
	  } else {
	    offset = reloc_evaluate(p, RELOCT_CONDBRA);
	  }

	  /* The offset for the relative branch must be 
	     between -127 <= offset <= 127. */
	  emit_check_relative(i->opcode, offset, 0xff, 127);
	}
	break;
      case INSN_CLASS_RBRA11:
	if (enforce_arity(arity, 1)) {
	  int offset;

	  p = HEAD(parms);
	  if (count_reloc(p) == 0) {
	    offset = maybe_evaluate(p) - ((state.org + 1)<<_16bit_core);
	    offset >>= _16bit_core;
	  } else {
	    offset = reloc_evaluate(p, RELOCT_BRA);
	  }	  
         
	  emit_check_relative(i->opcode, offset, 0x7ff, 0x3ff);
	}
	break;
      case INSN_CLASS_LIT20:
	if (enforce_arity(arity, 1)) {
	  int dest;
	  p = HEAD(parms);
	  dest = reloc_evaluate(p, RELOCT_GOTO) >> _16bit_core;
	  emit(i->opcode | (dest & 0xff));
	  reloc_evaluate(p, RELOCT_GOTO2);     /* add the second relocation */
	  emit_check(0xf000, dest>>8, 0xfff);
	}
	break;
      case INSN_CLASS_CALL20:
	{
	  int dest;
	  int s = 0; /* By default, fast push is not used */
	  struct pnode *p2; /* second parameter */
	    
	  p = HEAD(parms);
	  switch (arity) {
	  case 2:
	    p2 = HEAD(TAIL(parms));
	    /* Allow "s" for fast push */
	    if ((p2->tag == symbol) &&
		(strcasecmp(p2->value.symbol, "s") == 0))
	      s = 1;
	    else
	      s = check_flag(maybe_evaluate(p2));
	    break;
	  case 1:
	    s = 0;
	    break;
	  default:
	    enforce_arity(arity, 2);
	  }
	  dest = reloc_evaluate(p, RELOCT_CALL) >> _16bit_core;
	  emit(i->opcode | (s<<8) | (dest & 0xff));
	  reloc_evaluate(p, RELOCT_CALL2);     /* add the second relocation */
	  emit_check(0xf000, (dest>>8), 0xfff);
	}
	break;
      case INSN_CLASS_FLIT12:
	{
	  int k;

	  if (enforce_arity(arity, 2)) {
	    p = HEAD(parms);
	    file = maybe_evaluate(p);
	    if(file > 3)
	      gperror(GPE_UNKNOWN, "FSR is out of range");

	    p = HEAD(TAIL(parms));
	    k = reloc_evaluate(p, RELOCT_LFSR1);
	    emit_check(i->opcode | ((file & 3) << 4), (k>>8), 0xf);
	    reloc_evaluate(p, RELOCT_LFSR2); /* add the second relocation */
	    emit(0xf000 | (k & 0xff));
	  }
	}
	break;

      case INSN_CLASS_FF:
	if (enforce_arity(arity, 2)) {
          int dest = maybe_evaluate(HEAD(TAIL(parms)));

          /* destination can't be PCL, TOSU, TOSH, TOSL */
          if ((dest == 0xff9) ||
              (dest == 0xfff) ||
              (dest == 0xffe) ||
              (dest == 0xffd)) {
            gperror(GPE_UNKNOWN, "Invalid destination");
          }
          
	  emit_check(i->opcode, reloc_evaluate(HEAD(parms), RELOCT_FF1), 0xfff);
	  emit_check(0xf000, reloc_evaluate(HEAD(TAIL(parms)), RELOCT_FF2), 0xfff);

	}
	break;
      case INSN_CLASS_FP:
        if (enforce_arity(arity, 2)) {
          int reg=0;
          file=reloc_evaluate(HEAD(parms), RELOCT_F);
          reg=reloc_evaluate(HEAD(TAIL(parms)), RELOCT_P);
          file_ok(file);
          if (reg & ~0xf1f) {
            gpwarning(GPW_RANGE, NULL);
          }
          emit(i->opcode | ( (reg  & 0x1f) << 8) |
              (file & 0xff) );
        }
        break;
      case INSN_CLASS_PF:
        if (enforce_arity(arity, 2)) {
          int reg=0;
          file=reloc_evaluate(HEAD(TAIL(parms)), RELOCT_F);
          reg=reloc_evaluate(HEAD(parms), RELOCT_P);
          file_ok(file);
          if (reg & ~0xf1f) {
            gpwarning(GPW_RANGE, NULL);
          }
          emit(i->opcode | ( (reg & 0x1f) << 8) |
              (file & 0xff) );
        }
        break;
      case INSN_CLASS_SF:
        if (enforce_arity(arity, 2)) {
          int source;
	  int dest;

	  p = HEAD(parms);
          if (p->tag != offset)
	    gperror(GPE_MISSING_BRACKET, NULL);
          source = maybe_evaluate(p);

          p = HEAD(TAIL(parms));
          dest = maybe_evaluate(p);

          /* destination can't be PCL, TOSU, TOSH, TOSL */
          if ((dest == 0xff9) ||
              (dest == 0xfff) ||
              (dest == 0xffe) ||
              (dest == 0xffd)) {
            gperror(GPE_UNKNOWN, "Invalid destination");
          }
          
	  emit_check(i->opcode, source, 0x7f);
	  emit_check(0xf000, reloc_evaluate(HEAD(TAIL(parms)), RELOCT_FF2), 0xfff);
        }
        break;
      case INSN_CLASS_SS:
        if (enforce_arity(arity, 2)) {
          int source;
	  int dest;

	  p = HEAD(parms);
          if (p->tag != offset)
	    gperror(GPE_MISSING_BRACKET, NULL);
          source = maybe_evaluate(p);

          p = HEAD(TAIL(parms));
          if (p->tag != offset)
	    gperror(GPE_MISSING_BRACKET, NULL);
          dest = maybe_evaluate(p);

	  emit_check(i->opcode, source, 0x7f);
	  emit_check(0xf000, dest, 0x7f);
        }
        break;
      case INSN_CLASS_OPF5:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  if (strcasecmp(i->name, "tris") == 0) {
	    file = reloc_evaluate(p, RELOCT_TRIS);
	  } else {
	    file = reloc_evaluate(p, RELOCT_F);
	  }
	  file_ok(file);
	  emit(i->opcode | (file & 0x1f));
	}
	break;
      case INSN_CLASS_OPWF5:
	{
	  int d = 1; /* Default destination of 1 (file) */
	  struct pnode *p2; /* second parameter */
	    
	  if(arity == 0) {
	    enforce_arity(arity, 2);
	    break;
	  }

	  p = HEAD(parms);
	  switch (arity) {
	  case 2:
	    p2 = HEAD(TAIL(parms));
	    /* Allow "w" and "f" as destinations */
	    if ((p2->tag == symbol) &&
		(strcasecmp(p2->value.symbol, "f") == 0))
	      d = 1;
	    else if ((p2->tag == symbol) &&
		     (strcasecmp(p2->value.symbol, "w") == 0))
	      d = 0;
	    else
	      d = check_flag(maybe_evaluate(p2));
	    break;
	  case 1:
            d = 1;
            gpmessage(GPM_NOF,NULL);
	    break;
	  default:
	    enforce_arity(arity, 2);
	  }
	  file = reloc_evaluate(p, RELOCT_F);
	  file_ok(file);
	  emit(i->opcode | (d << 5) | (file & 0x1f));
	}
	break;
      case INSN_CLASS_B5:
	{
	  struct pnode *f, *b;
	  int bit;
	    
	  if (enforce_arity(arity, 2)) {
	    f = HEAD(parms);
	    b = HEAD(TAIL(parms));
	    file = reloc_evaluate(f, RELOCT_F);
	    bit = maybe_evaluate(b);
	    if (!((0 <= bit) && (bit <= 7)))
              gpwarning(GPW_RANGE, NULL);
	    file_ok(file);
	    emit(i->opcode | ((bit & 7) << 5) |(file & 0x1f));
	  }
	}
	break;

      case INSN_CLASS_B8:
	{
	  struct pnode *f, *b;
	  int bit;
	    
	  if (enforce_arity(arity, 2)) {
	    f = HEAD(parms);
	    b = HEAD(TAIL(parms));
	    file = reloc_evaluate(f, RELOCT_F);
	    bit = maybe_evaluate(b);
	    if (!((0 <= bit) && (bit <= 7)))
              gpwarning(GPW_RANGE, NULL);
	    file_ok(file);
	    emit(i->opcode | ((bit & 7) << 8) | (file & 0xff));
	  }
	}
	break;

      case INSN_CLASS_OPF7:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  if (strcasecmp(i->name, "tris") == 0) {
	    gpwarning(GPW_NOT_RECOMMENDED, NULL);
	    file = reloc_evaluate(p, RELOCT_TRIS);
	  } else {
	    file = reloc_evaluate(p, RELOCT_F);
	  }
	  file_ok(file);
	  emit(i->opcode | (file & 0x7f));
	}
	break;

      case INSN_CLASS_OPF8:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  file = reloc_evaluate(p, RELOCT_F);
	  file_ok(file);
	  emit(i->opcode | (file & 0xff));
	}
	break;

      case INSN_CLASS_OPWF7:
	{
	  int d = 1; /* Default destination of 1 (file) */
	  struct pnode *p2; /* second parameter */
	    
	  if(arity == 0) {
	    enforce_arity(arity, 2);
	    break;
	  }

	  p = HEAD(parms);
	  switch (arity) {
	  case 2:
	    p2 = HEAD(TAIL(parms));
	    /* Allow "w" and "f" as destinations */
	    if ((p2->tag == symbol) &&
		(strcasecmp(p2->value.symbol, "f") == 0))
	      d = 1;
	    else if ((p2->tag == symbol) &&
		     (strcasecmp(p2->value.symbol, "w") == 0))
	      d = 0;
	    else
	      d = check_flag(maybe_evaluate(p2));
	    break;
	  case 1:
            d = 1;
            gpmessage(GPM_NOF,NULL);
	    break;
	  default:
	    enforce_arity(arity, 2);
	  }
	  file = reloc_evaluate(p, RELOCT_F);
	  file_ok(file);
	  emit(i->opcode | (d << 7) | (file & 0x7f));
	}
	break;

      case INSN_CLASS_OPWF8:
	{
	  int d = 1; /* Default destination of 1 (file) */
	  struct pnode *p2; /* second parameter */
	    
	  if(arity == 0) {
	    enforce_arity(arity, 2);
	    break;
	  }

	  p = HEAD(parms);
	  switch (arity) {
	  case 2:
	    p2 = HEAD(TAIL(parms));
	    /* Allow "w" and "f" as destinations */
	    if ((p2->tag == symbol) &&
		(strcasecmp(p2->value.symbol, "f") == 0))
	      d = 1;
	    else if ((p2->tag == symbol) &&
		     (strcasecmp(p2->value.symbol, "w") == 0))
	      d = 0;
	    else
	      d = check_flag(maybe_evaluate(p2));
	    break;
	  case 1:
            d = 1;
            gpmessage(GPM_NOF,NULL);
	    break;
	  default:
	    enforce_arity(arity, 2);
	  }
	  file = reloc_evaluate(p, RELOCT_F);
	  file_ok(file);
	  emit(i->opcode | (d << 8) | (file & 0xff));
	}
	break;
      case INSN_CLASS_B7:
	{
	  struct pnode *f, *b;
	  int bit;
	    
	  if (enforce_arity(arity, 2)) {
	    f = HEAD(parms);
	    b = HEAD(TAIL(parms));
	    file = reloc_evaluate(f, RELOCT_F);
	    bit = maybe_evaluate(b);
	    if (!((0 <= bit) && (bit <= 7)))
              gpwarning(GPW_RANGE, NULL);
	    file_ok(file);
	    emit(i->opcode | ((bit & 7) << 7) | (file & 0x7f));
	  }
	}
	break;

      case INSN_CLASS_OPFA8:
	{
	  int a = 0;
	  struct pnode *p2; /* second parameter */

	  if(arity == 0) {
	    enforce_arity(arity, 2);
	    break;
	  }

	  p = HEAD(parms);
	  file = reloc_evaluate(p, RELOCT_F);
	  file_ok(file);

	  /* add relocation for the access bit, if necessary */          
	  reloc_evaluate(p, RELOCT_ACCESS);

	  /* Default access (use the BSR unless access is to special registers) */
	  /* If extended instructions are enabled, access bit should default to 1 for low-end */
	  /* of Access Memory unless the file is explicitly an offset (e.g. [foo]) */
	  if ((state.extended_pic16e == true) && (file <= 0x5f)) {
	    if (p->tag == offset) {
	      a = 0;
	    } else {
	      a = 1;
	    }
	  } else if ((file < state.device.bsr_boundary) || (file >= (0xf00 + state.device.bsr_boundary))) {
	    a = 0;
	  } else {
	    a = 1;
	  }

	  switch (arity) {
	  case 2:
	    p2 = HEAD(TAIL(parms));
	    /* Allow "w" and "f" as destinations */
	    if ((p2->tag == symbol) &&
		(strcasecmp(p2->value.symbol, "b") == 0))
	      a = 1;
	    else
	      a = check_flag(maybe_evaluate(p2));
	    break;
	  case 1:
	    /* use default a */
	    break;
	  default:
	    enforce_arity(arity, 2);
	  }
	  emit(i->opcode | (a << 8) | (file & 0xff));
	}
	break;

      case INSN_CLASS_BA8:
	{
	  struct pnode *f, *b,*par;
	  int bit,a=0;

	  if ((arity != 2) && (arity != 3)) {
	    enforce_arity(arity, 3);
	    break;
	  }

	  f = HEAD(parms);
	  file = reloc_evaluate(f, RELOCT_F);

	  if (arity == 3) {
	    par = HEAD(TAIL(TAIL(parms)));

	    if ((par->tag == symbol) &&
		(strcasecmp(par->value.symbol, "b") == 0))
	      a = 1;
	    else
	      a = check_flag(maybe_evaluate(par));
	  } else {
	    /* Default access (use the BSR unless access is to special 
               registers) */
	    if ((file < state.device.bsr_boundary) || 
		(file >= (0xf00 + state.device.bsr_boundary))) {
	      a = 0;
	    } else {
	      a = 1;
	    }          
	  }
	    
	  /* add relocation for the access bit, if necessary */          
	  reloc_evaluate(f, RELOCT_ACCESS);

	  b = HEAD(TAIL(parms));
	  bit = maybe_evaluate(b);
	  if (!((0 <= bit) && (bit <= 7)))
	    gpwarning(GPW_RANGE, NULL);
	  file_ok(file);
	  emit(i->opcode | ( a << 8) | ((bit & 7) << 9) | (file & 0xff));
	}
	break;

      case INSN_CLASS_OPWFA8:
	{
	  int d = 1; /* Default destination of 1 (file) */
	  int a = 0;
	  struct pnode *par; /* second parameter */
	    
	  if(arity == 0) {
	    enforce_arity(arity, 2);
	    break;
	  }

	  p = HEAD(parms);
	  file = reloc_evaluate(p, RELOCT_F);
	  file_ok(file);

	  /* add relocation for the access bit, if necessary */          
	  reloc_evaluate(p, RELOCT_ACCESS);

	  /* Default access (use the BSR unless access is to special registers) */
	  /* If extended instructions are enabled, access bit should default to 1 for low-end */
	  /* of Access Memory */
	  if ((state.extended_pic16e == true) && (file <= 0x5f)) {
	    if (p->tag == offset) {
	      a = 0;
	    } else {
	      a = 1;
	    }
	  } else if ((file < state.device.bsr_boundary) || (file >= (0xf00 + state.device.bsr_boundary))) {
	    a = 0;
	  } else {
	    a = 1;
	  }

	  switch (arity) {
	  case 3:
	    par = HEAD(TAIL(TAIL(parms)));

	    if ((par->tag == symbol) &&
		(strcasecmp(par->value.symbol, "b") == 0))
	      a = 1;
	    else
	      a = check_flag(maybe_evaluate(par));
	    /* fall through */
	  case 2:
	    par = HEAD(TAIL(parms));
	    /* Allow "w" and "f" as destinations */
	    if ((par->tag == symbol) &&
		(strcasecmp(par->value.symbol, "f") == 0))
	      d = 1;
	    else if ((par->tag == symbol) &&
		     (strcasecmp(par->value.symbol, "w") == 0))
	      d = 0;
	    else
	      d = check_flag(maybe_evaluate(par));
	    break;
	  case 1:
            /* use default a and d */
            gpmessage(GPM_NOF,NULL);
            break;
	  default:
            enforce_arity(arity, 3);
	  }
	  emit(i->opcode | (d << 9) | (a << 8) | (file & 0xff));
	}
	break;

      case INSN_CLASS_IMPLICIT:
	if (arity != 0) {
          gpwarning(GPW_EXTRANEOUS, NULL);
	}
	if ((strcasecmp(i->name, "option") == 0) &&
            (state.device.core_size != CORE_12BIT_MASK)){
          gpwarning(GPW_NOT_RECOMMENDED, NULL);
        }
	emit(i->opcode);
	break;
      case INSN_CLASS_TBL:
	if (enforce_arity(arity, 1)) {
	  p = HEAD(parms);
	  switch(maybe_evaluate(p))
	    {
	    case TBL_NO_CHANGE:
	      emit(i->opcode);
	      break;
	    case TBL_POST_INC:
	      emit(i->opcode | 1);
	      break;
	    case TBL_POST_DEC:
	      emit(i->opcode | 2);
	      break;
	    case TBL_PRE_INC:
	      emit(i->opcode | 3);
	      break;
	    default:
	      gperror(GPE_ILLEGAL_ARGU, NULL);
	    }
	}
	break;

      case INSN_CLASS_TBL2:
	if (enforce_arity(arity, 2)) {
	  int t=0; /* read low byte by default */
	  struct pnode *p2; /* second parameter */

	  /* "0" (lower byte) and "1" (upper byte) */
	  p = HEAD(parms);
	  t = check_flag(maybe_evaluate(p));

	  p2 = HEAD(TAIL(parms));
	  file = reloc_evaluate(p2, RELOCT_F);

	  file_ok(file);
          emit(i->opcode | (t << 9) | (file & 0xff));
	}
	break;

      case INSN_CLASS_TBL3:
	if (enforce_arity(arity, 3)) {
	  int inc=0,t=0;
	  struct pnode *p2; /* second parameter */
	  struct pnode *p3; /* third parameter */

	  /* "0" (lower byte) and "1" (upper byte) */
	  p = HEAD(parms);
	  t = check_flag(maybe_evaluate(p));

	  /* "0" (no change) and "1" (postincrement) */
	  p2 = HEAD(TAIL(parms));
	  inc = check_flag(maybe_evaluate(p2));

	  p3 = HEAD(TAIL(TAIL(parms)));
	  file = reloc_evaluate(p3, RELOCT_F);

	  file_ok(file);          
          emit(i->opcode | (t << 9) | (inc << 8) | (file & 0xff));
	}
	break;

      case INSN_CLASS_FUNC:
	r = (*(opfunc*)i->opcode)(r, name, arity, parms);
	break;
      }
    }
  } else {
    s = get_symbol(state.stMacros, name);
    if (s) {
      struct macro_head *h = get_symbol_annotation(s);

      /* Found the macro: execute it */
      if (asm_enabled()) {
	if ((h->defined != 1) && (state.pass == 2)) {
	  gperror(GPE_UNKNOWN, "Forward references to macros are not allowed.");	
	} else {
	  setup_macro(h, arity, parms);
	}
      }
    } else {

      if (asm_enabled()) {
        if (state.processor_chosen == 0){
          gperror(GPE_UNDEF_PROC, NULL);
        } else {
          char mesg[80];
          snprintf(mesg, sizeof(mesg), "Unknown opcode \"%.40s\"", name);
          gperror(GPE_UNKNOWN, mesg);
        }
      }

    }
  }

  return r;
}

/************************************************************************/

/* There are several groups of operations that we handle here.  First
   is op_0: the instructions that can happen before the processor type
   is known.  Second is op_1, the set of instructions that are common
   to all processors, third is processor-family specific: op_XXX */

/* Note that instructions within each group are sorted alphabetically */

struct insn op_0[] = {
  { "code",       0, (long int)do_code,      INSN_CLASS_FUNC,   0 },
  { "constant",   0, (long int)do_constant,  INSN_CLASS_FUNC,   0 },
  { "else",       0, (long int)do_else,      INSN_CLASS_FUNC,   ATTRIB_COND },
  { "endif",      0, (long int)do_endif,     INSN_CLASS_FUNC,   ATTRIB_COND },
  { "endm",       0, (long int)do_endm,	     INSN_CLASS_FUNC,   0 },
  { "endw",       0, (long int)do_endw,	     INSN_CLASS_FUNC,   0 },
  { "equ",        0, (long int)do_equ,       INSN_CLASS_FUNC,   0 },
  { "error",      0, (long int)do_error,     INSN_CLASS_FUNC,   0 },
  { "exitm",      0, (long int)do_exitm,     INSN_CLASS_FUNC,   0 },
  { "expand",     0, (long int)do_expand,    INSN_CLASS_FUNC,   0 },
  { "extern",     0, (long int)do_extern,    INSN_CLASS_FUNC,   0 },
  { "errorlevel", 0, (long int)do_errlvl,    INSN_CLASS_FUNC,   0 },
  { "global",     0, (long int)do_global,    INSN_CLASS_FUNC,   0 },
  { "idata",      0, (long int)do_idata,     INSN_CLASS_FUNC,   0 },
  { "if",         0, (long int)do_if,        INSN_CLASS_FUNC,   ATTRIB_COND },
  { "ifdef",      0, (long int)do_ifdef,     INSN_CLASS_FUNC,   ATTRIB_COND },
  { "ifndef",     0, (long int)do_ifndef,    INSN_CLASS_FUNC,   ATTRIB_COND },
  { "include",    0, (long int)do_include,   INSN_CLASS_FUNC,   0 },
  { "list",       0, (long int)do_list,	     INSN_CLASS_FUNC,   0 },
  { "local",      0, (long int)do_local,     INSN_CLASS_FUNC,   0 },
  { "macro",      0, (long int)do_macro,     INSN_CLASS_FUNC,	0 },
  { "messg",      0, (long int)do_messg,     INSN_CLASS_FUNC,   0 },
  { "noexpand",   0, (long int)do_noexpand,  INSN_CLASS_FUNC,   0 },
  { "nolist",     0, (long int)do_nolist,    INSN_CLASS_FUNC,   0 },
  { "page",       0, (long int)do_page,      INSN_CLASS_FUNC,   0 },
  { "processor",  0, (long int)do_processor, INSN_CLASS_FUNC,   0 },
  { "radix",      0, (long int)do_radix,     INSN_CLASS_FUNC,   0 },
  { "set",        0, (long int)do_set, 	     INSN_CLASS_FUNC,   0 },
  { "space",      0, (long int)do_space,     INSN_CLASS_FUNC,   0 },
  { "udata",      0, (long int)do_udata,     INSN_CLASS_FUNC,   0 },
  { "udata_acs",  0, (long int)do_udata_acs, INSN_CLASS_FUNC,   0 },
  { "udata_ovr",  0, (long int)do_udata_ovr, INSN_CLASS_FUNC,   0 },
  { "udata_shr",  0, (long int)do_udata_shr, INSN_CLASS_FUNC,   0 },
  { "variable",   0, (long int)do_variable,  INSN_CLASS_FUNC,   0 },
  { "while",      0, (long int)do_while,     INSN_CLASS_FUNC,   0 },
  { ".def",       0, (long int)do_def, 	     INSN_CLASS_FUNC,   0 },
  { ".dim",       0, (long int)do_dim, 	     INSN_CLASS_FUNC,   0 },
  { ".direct",    0, (long int)do_direct,    INSN_CLASS_FUNC,   0 },
  { ".eof",       0, (long int)do_eof,	     INSN_CLASS_FUNC,   0 },
  { ".file",      0, (long int)do_file,	     INSN_CLASS_FUNC,   0 },
  { ".ident",     0, (long int)do_ident,     INSN_CLASS_FUNC,   0 },
  { ".line",      0, (long int)do_line,	     INSN_CLASS_FUNC,   0 },
  { ".set",       0, (long int)do_set, 	     INSN_CLASS_FUNC,   0 },
  { ".type",      0, (long int)do_type,	     INSN_CLASS_FUNC,   0 },
  { "#if",        0, (long int)do_if,        INSN_CLASS_FUNC,   ATTRIB_COND },
  { "#else",      0, (long int)do_else,	     INSN_CLASS_FUNC,   ATTRIB_COND },
  { "#endif",     0, (long int)do_endif,     INSN_CLASS_FUNC,   ATTRIB_COND },
  { "#ifdef",     0, (long int)do_ifdef,     INSN_CLASS_FUNC,   ATTRIB_COND },
  { "#ifndef",    0, (long int)do_ifndef,    INSN_CLASS_FUNC,   ATTRIB_COND },
  { "#undefine",  0, (long int)do_undefine,  INSN_CLASS_FUNC,   0 }
};

const int num_op_0 = TABLE_SIZE(op_0);

struct insn op_1[] = {
  { "__badram",   0, (long int)do_badram,    INSN_CLASS_FUNC,   0 },
  { "__badrom",   0, (long int)do_badrom,    INSN_CLASS_FUNC,   0 },
  { "__config",   0, (long int)do_config,    INSN_CLASS_FUNC,   0 },
  { "__fuses",    0, (long int)do_config,    INSN_CLASS_FUNC,   0 },
  { "__idlocs",   0, (long int)do_idlocs,    INSN_CLASS_FUNC,   0 },
  { "__maxram",   0, (long int)do_maxram,    INSN_CLASS_FUNC,   0 },
  { "__maxrom",   0, (long int)do_maxrom,    INSN_CLASS_FUNC,   0 },
  { "bankisel",   0, (long int)do_bankisel,  INSN_CLASS_FUNC,   0 },
  { "banksel",    0, (long int)do_banksel,   INSN_CLASS_FUNC,   0 },
  { "data",       0, (long int)do_data,      INSN_CLASS_FUNC,   0 },
  { "da",         0, (long int)do_da, 	     INSN_CLASS_FUNC,   0 },
  { "db",         0, (long int)do_db, 	     INSN_CLASS_FUNC,   0 },
  { "de",         0, (long int)do_de, 	     INSN_CLASS_FUNC,   0 },
  { "dt",         0, (long int)do_dt, 	     INSN_CLASS_FUNC,   0 },
  { "dw",         0, (long int)do_dw, 	     INSN_CLASS_FUNC,   0 },
  { "fill",       0, (long int)do_fill,      INSN_CLASS_FUNC,   0 },
  { "org",        0, (long int)do_org,       INSN_CLASS_FUNC,   0 },
  { "pagesel",    0, (long int)do_pagesel,   INSN_CLASS_FUNC,   0 },
  { "res",        0, (long int)do_res,       INSN_CLASS_FUNC,   0 }
};

const int num_op_1 = TABLE_SIZE(op_1);

void opcode_init(int stage)
{
  int i;
  int count = 0;
  struct insn *base = NULL;

  switch (stage) {
  case 0:
    base = op_0;
    count = num_op_0;
    break;
  case 1:
    base = op_1;
    count = num_op_1;
    break;
  case 2:
    state.device.class = gp_processor_class(state.processor);
    switch (state.device.class) {
    case PROC_CLASS_EEPROM8:
      base = 0;
      count = 0;
      state.device.core_size = CORE_8BIT_MASK;
      state.device.config_address = 0;
      state.device.id_location = 0;
      break;
    case PROC_CLASS_GENERIC:
      base = 0;
      count = 0;
      state.device.core_size = CORE_12BIT_MASK;
      state.device.config_address = CONFIG_ADDRESS_12;
      state.device.id_location = IDLOC_ADDRESS_12;
      break;
    case PROC_CLASS_PIC12:
      base = op_12c5xx;
      count = num_op_12c5xx;
      state.device.core_size = CORE_12BIT_MASK;
      state.device.config_address = CONFIG_ADDRESS_12;
      state.device.id_location = IDLOC_ADDRESS_12;
      break;
    case PROC_CLASS_SX:
      base = op_sx;
      count = num_op_sx;
      state.device.core_size = CORE_12BIT_MASK;
      state.device.config_address = CONFIG_ADDRESS_12;
      state.device.id_location = IDLOC_ADDRESS_12;
      /* page instruction conflicts with the page directive */
      remove_symbol(state.stBuiltin, "page");    
      break;
    case PROC_CLASS_PIC14:
      base = op_16cxx;
      count = num_op_16cxx;
      state.device.core_size = CORE_14BIT_MASK;
      state.device.config_address = CONFIG_ADDRESS_14;
      state.device.id_location = IDLOC_ADDRESS_14;
      break;
    case PROC_CLASS_PIC16:
      base = op_17cxx;
      count = num_op_17cxx;
      state.device.core_size = CORE_16BIT_MASK;
      _17cxx_core = 1;
      state.device.config_address = CONFIG_17CXX;
      break;
    case PROC_CLASS_PIC16E:
      base = op_18cxx;
      count = num_op_18cxx;
      state.device.core_size = CORE_16BIT_MASK;
      _16bit_core = 1;
      state.c_memory_base = CONFIG1L;
      state.device.config_address = CONFIG1L;
      state.device.bsr_boundary = gp_processor_bsr_boundary(state.processor);

      /* The 16_bit core special macros are encoded directly into the
       * symbol table like regular instructions. */
      for (i = 0; i < num_op_18cxx_sp; i++)
	annotate_symbol( add_symbol(state.stBuiltin, op_18cxx_sp[i].name), 
			 &op_18cxx_sp[i]);

      if (state.extended_pic16e) {
        /* Some 18xx devices have an extended instruction set. */
        for (i = 0; i < num_op_18cxx_ext; i++)
	  annotate_symbol( add_symbol(state.stBuiltin, op_18cxx_ext[i].name), 
                           &op_18cxx_ext[i]);
      }

      break;

    default:
      assert(0);
    }
    break;
  case 3:
    /* add 12 and 14 bit special macros */
    base = special;
    count = num_op_special;
    break;
  default:
    assert(0);
  }

  for (i = 0; i < count; i++)
    annotate_symbol(add_symbol(state.stBuiltin, base[i].name), &base[i]);

  /* Special Case, Some instructions not available on 17c42 devices */
  if (state.processor == pic17c42) {
    remove_symbol(state.stBuiltin, "MULWF");    
    remove_symbol(state.stBuiltin, "MOVLR");    
    remove_symbol(state.stBuiltin, "MULLW");    
  }

}

/************************************************************************/

void begin_cblock(struct pnode *c)
{
  if (asm_enabled()) { 
    state.cblock = maybe_evaluate(c);
  }
}

void cblock_expr(struct pnode *s)
{
  if ((asm_enabled()) && (can_evaluate_concatenation(s))) {
    set_global(evaluate_concatenation(s), state.cblock, PERMANENT, gvt_cblock);
    state.cblock++;
  }
}

void cblock_expr_incr(struct pnode *s, struct pnode *incr)
{
  if ((asm_enabled()) && (can_evaluate_concatenation(s))) {
    set_global(evaluate_concatenation(s), state.cblock, PERMANENT, gvt_cblock);
    state.cblock += maybe_evaluate(incr);
  }
}


syntax highlighted by Code2HTML, v. 0.9.1