/* $VER: pasm directives.c V0.8 (14.02.98)
 *
 * This file is part of pasm, a portable PowerPC assembler.
 * Copyright (c) 1997-98  Frank Wille
 *
 * pasm is freeware and part of the portable and retargetable ANSI C
 * compiler vbcc, copyright (c) 1995-98 by Volker Barthelmann.
 * pasm may be freely redistributed as long as no modifications are
 * made and nothing is charged for it. Non-commercial usage is allowed
 * without any restrictions.
 * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
 * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
 *
 *
 * v0.8 (14.02.98) phx
 *      Alignment list for each section. This fixes the problems
 *      with optimizations.
 * v0.6 (30.10.97) phx
 *      The names of macros will be converted to lower case (otherwise,
 *      they would remain inaccessible ;)
 * v0.5 (12.10.97) phx
 *      .set directive is allowed multiple times on the same symbol.
 *      If a .set-assignment can't be resolved in pass 1, the symbol
 *      is no longer undefined, but defined with an absolute default
 *      value of '1'. So .ifdef will work correctly.
 *      .globl directive declares unknown symbols in pass 1 as
 *      externally defined. If the symbol is defined later in the
 *      source, it will be made global by add_symbol()/tables.c.
 * v0.4 (29.04.97) phx
 *      .new_section sets type of section-symbol to SYMI_SECTION.
 *      New directives: .baserel
 * v0.3 (10.04.97) phx
 *      Some vbcc-specific changes.
 * v0.2 (25.03.97) phx
 *      Writes ELF object for 32-bit PowerPC big-endian. Either absolute
 *      or ELF output format may be selected. ELF is default for all
 *      currently supported platforms. PPCasm supports nine different
 *      relocation types (there are much more...).
 *      Compiles and works also under NetBSD/amiga (68k).
 *      Changed function declaration to 'new style' in all sources
 *      (to avoid problems with '...' for example).
 *      Floating pointer support. New directives: .fail, .ident, .file,
 *      .float, .ufloat, .double, .udouble, .local.
 * v0.1 (11.03.97) phx
 *      First test version with all PowerPC instructions and most
 *      important directives. Only raw, absolute output.
 *      .ident and .file directives are supported, but have no effect.
 *      Especially floating point directives are missing.
 * v0.0 (21.02.97) phx
 *      File created.
 */


#define DIRECTIVES_C
#include "ppcasm.h"


void activate_section(struct GlobalVars *,struct Section *);
void alignment(struct GlobalVars *,unsigned long);
char escchar(char);

static void start_section(struct GlobalVars *,struct ParsedLine *pl,bool);
static char *section_attributes(struct GlobalVars *,char *,uint8 *,
                                uint8 *,uint8 *,uint8 *);
static void _uahalf(struct GlobalVars *,struct ParsedLine *);
static void _uaword(struct GlobalVars *,struct ParsedLine *);
static void uafloat(struct GlobalVars *,struct ParsedLine *,bool);
static void ifeqs(struct GlobalVars *,struct ParsedLine *,int);
static void ifdef(struct GlobalVars *,struct ParsedLine *,bool);



static void _new_section(struct GlobalVars *gv,struct ParsedLine *pl)
{
  start_section(gv,pl,1);
}


static void _section(struct GlobalVars *gv,struct ParsedLine *pl)
{
  start_section(gv,pl,0);
}


static void start_section(struct GlobalVars *gv,struct ParsedLine *pl,
                          bool force_new)
/* define a new section with new attributes or reactivate a */
/* previously defined one */
{
  struct Section *nexts,*sec = (struct Section *)gv->sectionlist.first;
  struct Symbol *sym;
  struct AlignPoint *ap;
  char *s,*name;
  uint8 type,flags,protection,alignment;

  s = getarg(gv,pl->operand);  /* section's name */
  name = remquotes(gv->strbuf);

  while (nexts = (struct Section *)sec->n.next) {
    if (!strcmp(name,sec->name)) {

      if (force_new) {  /* overwrite an old section with the same name? */
        sprintf(gv->strbuf,"%s_%08lx",sec->name,(unsigned long)sec);
        name = sec->name;
        sec->name = allocstring(gv->strbuf);
        break;
      }

      else {  /* reactivate old section with the same name */
        s = section_attributes(gv,s,&type,&flags,&protection,&alignment);
        if (type != ST_UNDEFINED)  /* attributes specified? */
          if (sec->type!=type || sec->flags!=flags ||
              sec->protection!=protection || sec->alignment!=alignment)
            error(16);  /* section attributes don't match */
        pl->type = OT_SECTION;
        pl->opcode = (void *)sec;
        activate_section(gv,sec);
        checkEOL(s);
        return;
      }

    }
    sec = nexts;
  }

  /* create a new section */
  sec = alloczero(sizeof(struct Section));
  sec->name = allocstring(name);
  s = section_attributes(gv,s,&sec->type,&sec->flags,&sec->protection,
                         &sec->alignment);
  if (sec->type == ST_UNDEFINED) {  /* set default attributes */
    sec->type = ST_DATA;
    sec->protection = SP_READ|SP_WRITE;
    sec->alignment = 2;
  }
  initlist(&sec->reloclist);
  initlist(&sec->xreflist);
  ap = alloc(sizeof(struct AlignPoint));
  ap->next = NULL;
  ap->offset = ap->gap = 0;
  ap->val = 1 << sec->alignment;
  sec->first_align = sec->current_align = ap;
  addtail(&gv->sectionlist,&sec->n);
  pl->type = OT_SECTION;
  pl->opcode = (void *)sec;
  activate_section(gv,sec);
  sym = add_symbol(gv,sec->name,SYM_RELOC,0);
  sym->info = SYMI_SECTION;
  checkEOL(s);
}


void activate_section(struct GlobalVars *gv,struct Section *sec)
{
  gv->csect = sec;  /* set active section */
  gv->lcsym->relsect = sec;
  gv->lcsym->value = sec->pc;
}


static char *section_attributes(struct GlobalVars *gv,char *s,uint8 *t,
                                uint8 *f,uint8 *p,uint8 *a)
/* fill in section attributes, if specified */
{
  char c,*attr;

  *t = ST_UNDEFINED;
  *f = *p = *a = 0;
  s = skipspaces(s);
  if (*s) {
    if (*s == ',') {
      s = skipspaces(++s);
      s = getarg(gv,s);
      c = *gv->strbuf;
      if (c=='\"' || c=='\'') {
        attr = remquotes(gv->strbuf);
        while (c = *attr++) {
          switch (c) {
            /* contents type */
            case 'c':
              *t = ST_CODE;
              break;
            case 'd':
              *t = ST_DATA;
              break;
            case 'u':
              *t = ST_UDATA;
              *f |= SF_UNINITIALIZED;
              break;
            case 'i':
              *t = ST_STRUCT;
              *f |= SF_DISCARD|SF_UNINITIALIZED;
              break;
            /* protection */
            case 'r':
              *p |= SP_READ;
              break;
            case 'w':
              *p |= SP_WRITE;
              break;
            case 'x':
              *p |= SP_EXEC;
              break;
            case 's':
              *p |= SP_SHARE;
              break;
            case 'n': /* remove and discard currently not supported */
            case 'R':
              break;
            default:
              if (c>='0' && c <='6')
                *a = (uint8)(c-'0');  /* alignment */
              else
                error(15,c);  /* illegal section attribute */
              break;
          }
        }
      }
      else
        error(14);  /* string constant expected */
    }
    else if (*s != '#')
      error(12);  /* colon expected */
  }
  return (s);
}


static void _set(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s = getsymbol(gv,pl->operand);
  struct Expression exp;
  struct Section *cs;
  char setname[STRBUFSIZE];
  struct Symbol *sym;

  if (*gv->strbuf) {
    strcpy(setname,gv->strbuf);
    if (s = check_comma(s)) {
      s = eval_expression(gv,&exp,s);
      if (exp.type != SYM_UNDEF) {
        if (exp.type != SYM_EXTERN) {
          if (exp.type == SYM_RELOC) {
            if (exp.reloctype == R_PPC_ADDR32) {
              if (sym = search_symbol(gv,setname)) {
                sym->value = exp.value;
                sym->type = SYM_RELOC;
                sym->relsect = exp.symbol->relsect;
              }
              else {
                cs = gv->csect;
                gv->csect = exp.symbol->relsect;
                add_symbol(gv,setname,SYM_RELOC,exp.value);
                gv->csect = cs;
              }
            }
            else
              error(29);  /* can't assign relocatable half word */
          }
          else {
            if (sym = search_symbol(gv,setname)) {
              sym->value = exp.value;
              sym->type = SYM_ABS;
            }
            else
              add_symbol(gv,setname,SYM_ABS,exp.value);
          }
        }
        else
          error(28);  /* can't assign external symbol */
      }
      else {
        if (gv->pass)  /* expression still undefined in pass 2 ? */
          error(19);  /* undefined symbol */
        else if (!search_symbol(gv,setname))
          /* set value to 1 if unknown in first pass */
          add_symbol(gv,setname,SYM_ABS,1);
      }
      checkEOL(s);
      return;
    }
  }
  else
    error(17);  /* missing argument */
}


static void _macro(struct GlobalVars *gv,struct ParsedLine *pl)
{
  struct Macro *mac = alloczero(sizeof(struct Macro));
  char *s = getsymbol(gv,pl->operand);

  mac->name = allocstring(gv->strbuf);
  lower_case(mac->name);  /* convert macro name to lower case */
  mac->text = gv->cthread->srcptr;
  mac->nlines = gv->absline + 1;
  gv->cthread->macskip = mac;
  pl->type = OT_IGNORE;  /* ignore in pass 2 */
  checkEOL(s);
}


static void _include(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s;

  if (!gv->pass) {  /* pass 1 */
    s = getarg(gv,pl->operand);  /* include file's name */

    /* recursively call pass 1 and create new SourceThread */
    pass1(gv,include_source(gv,remquotes(gv->strbuf)),NULL,gv->cthread);
    checkEOL(s);
  }

  else  /* recursively call pass 2 */
    pass2(gv,get_source(gv),NULL,gv->cthread);
}


void alignment(struct GlobalVars *gv,unsigned long a)
{
  unsigned long x = (1<<a) - 1;
  unsigned long pc, oldpc = gv->csect->pc;
  struct AlignPoint *ap = gv->csect->current_align;

  pc = (oldpc + x) & ~x;
  if (!gv->pass) {
    if (ap->next)
      ierror("Current alignment point is not the last one!");
    ap->next = alloc(sizeof(struct AlignPoint));
    ap = ap->next;
    ap->next = NULL;
    ap->offset = oldpc;
    ap->val = 1 << a;
    ap->gap = pc - oldpc;
    gv->csect->current_align = ap;
    gv->csect->pc = pc;
  }
  else {
    if (!(gv->csect->current_align = ap->next))  /* next alignment section */
      ierror("No more alignments in list!");
    store_space(gv,pc-oldpc);
  }
}


static void _align(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s;
  uint32 a=2;
  static uint8 na=0;

  if (pl->flags & PLF_ALIGN) {
    if (++na > 2)
      gv->vc = TRUE;
  }
  else
    na = 0;
  s = getintexp(gv,pl->operand,&a);
  alignment(gv,a);
  gv->alignflag = TRUE;
  if (!gv->pass)
    checkEOL(s);
}


static void _space(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s;
  uint32 n=0;

  s = getintexp(gv,pl->operand,&n);
  if (!gv->pass) {
    pcadd(gv,n);
    checkEOL(s);
  }
  else
    store_space(gv,n);
}


char escchar(char c)
/* convert escape code */
{
  switch (c) {
    case 'b':
      c = 8;
      break;
    case 'f':
      c = 12;
      break;
    case 'n':
      c = 10;
      break;
    case 'r':
      c = 13;
      break;
    case 't':
      c = 8;
      break;
  }
  return (c);
}


static void _byte(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s = pl->operand;
  char *sb,c;
  uint8 pass = gv->pass;
  uint32 i;

  for (;;) {
    if ((c = *s) == '\"') {  /* read string */
      s = getarg(gv,s);
      sb = remquotes(gv->strbuf);
      if (!pass)
        while (c = *sb++) {
          if (c=='\\' || c=='\"')
            if (*sb++ == 0)
              break;
          pcadd(gv,1);
        }
      else
        while (c = *sb++) {
          if (c == '\\') {
            if (!(c = escchar(*sb++)))
              break;
          }
          else if (c == '\"') {
            if (!(c = *sb++))
              break;
          }
          store_byte(gv,(uint8)c);
        }
    }

    else {  /* get integer expression */
      if (c==0 || c=='#') {
        error(17);  /* missing argument */
        break;
      }
      if (!pass) {
        s = skipexpression(gv,s);
        pcadd(gv,1);
      }
      else {
        s = getintexp(gv,s,&i);
        store_byte(gv,(uint8)i);
        s = skipspaces(s);
      }
    }

    c = *s++;
    if (c==0 || c=='#')  /* no more arguments? */
      break;
    if (c != ',') {
      error(12);  /* colon expected */
      break;
    }
    s = skipspaces(s);
  }
}


static void _string(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s = pl->operand;
  char *sb,c;
  uint8 pass = gv->pass;

  for (;;) {
    if ((c = *s) == '\"') {  /* read string */
      s = getarg(gv,s);
      sb = remquotes(gv->strbuf);
      if (!pass) {
        while (c = *sb++) {
          if (c=='\\' || c=='\"')
            if (*sb++ == 0)
              break;
          pcadd(gv,1);
        }
        pcadd(gv,1);
      }
      else {
        while (c = *sb++) {
          if (c == '\\') {
            if (!(c = escchar(*sb++)))
              break;
          }
          else if (c == '\"') {
            if (!(c = *sb++))
              break;
          }
          store_byte(gv,(uint8)c);
        }
        store_byte(gv,0);
      }
    }
    else {
      error(14);  /* string constant expected */
      break;
    }

    c = *s++;
    if (c==0 || c=='#')  /* no more arguments? */
      break;
    if (c != ',') {
      error(12);  /* colon expected */
      break;
    }
    s = skipspaces(s);
  }
}


static void _half(struct GlobalVars *gv,struct ParsedLine *pl)
{
  alignment(gv,1);
  _uahalf(gv,pl);
}


static void _uahalf(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s = pl->operand;
  char *sb,c;
  uint8 pass = gv->pass;
  uint32 i;

  for (;;) {
    c = *s;
    if (c==0 || c=='#') {
      error(17);  /* missing argument */
      break;
    }
    if (!pass) {
      s = skipexpression(gv,s);
      pcadd(gv,2);
    }
    else {
      s = getexp(gv,s,&i,2);
      store_half(gv,(uint16)i);
      s = skipspaces(s);
    }

    c = *s++;
    if (c==0 || c=='#')  /* no more arguments? */
      break;
    if (c != ',') {
      error(12);  /* colon expected */
      break;
    }
    s = skipspaces(s);
  }
}


static void _word(struct GlobalVars *gv,struct ParsedLine *pl)
{
  alignment(gv,2);
  _uaword(gv,pl);
}


static void _uaword(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s = pl->operand;
  char *sb,c;
  uint8 pass = gv->pass;
  uint32 i;

  for (;;) {
    c = *s;
    if (c==0 || c=='#') {
      error(17);  /* missing argument */
      break;
    }
    if (!pass) {
      s = skipexpression(gv,s);
      pcadd(gv,4);
    }
    else {
      s = getexp(gv,s,&i,4);
      store_word(gv,i);
      s = skipspaces(s);
    }

    c = *s++;
    if (c==0 || c=='#')  /* no more arguments? */
      break;
    if (c != ',') {
      error(12);  /* colon expected */
      break;
    }
    s = skipspaces(s);
  }
}


static void _float(struct GlobalVars *gv,struct ParsedLine *pl)
{
  alignment(gv,2);
  uafloat(gv,pl,FALSE);
}


static void _uafloat(struct GlobalVars *gv,struct ParsedLine *pl)
{
  uafloat(gv,pl,FALSE);
}


static void _double(struct GlobalVars *gv,struct ParsedLine *pl)
{
  alignment(gv,3);
  uafloat(gv,pl,TRUE);
}


static void _uadouble(struct GlobalVars *gv,struct ParsedLine *pl)
{
  uafloat(gv,pl,TRUE);
}


static void uafloat(struct GlobalVars *gv,struct ParsedLine *pl,bool dbl)
{
  char *s = pl->operand;
  char *sb,c;
  uint8 pass = gv->pass;
  double f;

  for (;;) {
    c = *s;
    if (c==0 || c=='#') {
      error(17);  /* missing argument */
      break;
    }
    if (!pass) {
      s = skipexpression(gv,s);
      pcadd(gv,dbl?8:4);
    }
    else {
      sscanf(s,"%lf",&f);
      if (dbl)
        store_double(gv,f);
      else
        store_float(gv,f);
      s = skipexpression(gv,s);
    }

    c = *s++;
    if (c==0 || c=='#')  /* no more arguments? */
      break;
    if (c != ',') {
      error(12);  /* colon expected */
      break;
    }
    s = skipspaces(s);
  }
}


static void _globl(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s;
  struct Symbol *sym;

  s = getsymbol(gv,pl->operand);
  if (*gv->strbuf) {
    if (!(sym = search_symbol(gv,gv->strbuf))) {
      /* unknown in pass 1, then declare as externally defined first */
      sym = add_symbol(gv,gv->strbuf,SYM_EXTERN,0);
    }
    sym->bind = SYMB_GLOBAL;
    checkEOL(s);
  }
  else
    error(17);  /* missing argument */
  pl->type = OT_IGNORE;  /* ignore in pass 2 */
}


static void _local(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s;
  struct Symbol *sym;

  if (gv->pass) {
    s = getsymbol(gv,pl->operand);
    if (*gv->strbuf) {
      if (!(sym = search_symbol(gv,gv->strbuf)))
        error(19);  /* undefined symbol */
      else
        sym->bind = SYMB_LOCAL;
      checkEOL(s);
    }
    else
      error(17);  /* missing argument */
  }
}


static void _bss(struct GlobalVars *gv,struct ParsedLine *pl)
{
  static char *bssname=".bss";
  char *s;
  struct Section *bss,*old=gv->csect;
  uint32 size,align;
  char symbname[STRBUFSIZE];
  struct Symbol *sym;

  s = getsymbol(gv,pl->operand);
  if (*gv->strbuf) {
    if (!gv->pass)
      strcpy(symbname,gv->strbuf);
    if (!(s = check_comma(s)))
      return;
    s = getintexp(gv,s,&size);  /* size of bss symbol */
    align = (size>=8)? 3 : 2;
    s = skipspaces(s);
    if (*s == ',') {
      s = getintexp(gv,++s,&align);  /* optional aligment */
      if (align < 2)
        align = 2;
    }
    if (bss = search_section(gv,bssname)) {
      activate_section(gv,bss);
      alignment(gv,align);
      if (!gv->pass) {
        sym = add_symbol(gv,symbname,SYM_RELOC,bss->pc);
        sym->info = SYMI_OBJECT;
        sym->size = size;
        sym->bind = SYMB_LOCAL;
        pcadd(gv,size);
        checkEOL(s);
      }
      else
        store_space(gv,size);
      activate_section(gv,old);
    }
    else
      error(47,bssname);  /* section .bss was never defined */
  }
  else
    error(17);  /* missing argument */
}


static void _comm(struct GlobalVars *gv,struct ParsedLine *pl)
{
  static char *bssname=".bss";
  char *s;
  struct Section *bss,*old=gv->csect;
  uint32 size;
  char symbname[STRBUFSIZE];
  struct Symbol *sym;

  s = getsymbol(gv,pl->operand);
  if (*gv->strbuf) {
    if (!gv->pass)
      strcpy(symbname,gv->strbuf);
    if (!(s = check_comma(s)))
      return;
    s = getintexp(gv,s,&size);  /* size of bss symbol */
    if (bss = search_section(gv,bssname)) {
      activate_section(gv,bss);
      alignment(gv,(size>=8)? 3:2);
      if (!gv->pass) {
        sym = add_symbol(gv,symbname,SYM_RELOC,bss->pc);
        sym->info = SYMI_OBJECT;
        sym->size = size;
        sym->bind = SYMB_GLOBAL;  /* or SYMB_WEAK ?? */
        pcadd(gv,size);
        checkEOL(s);
      }
      else
        store_space(gv,size);
      activate_section(gv,old);
    }
    else
      error(47,bssname);  /* section .bss was never defined */
  }
  else
    error(17);  /* missing argument */
}


static void _type(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s;
  struct Symbol *sym;
  uint32 type;

  if (gv->pass) {
    s = getsymbol(gv,pl->operand);
    if (*gv->strbuf) {
      if (sym = search_symbol(gv,gv->strbuf)) {
        if (s = check_comma(s)) {
          s = getintexp(gv,s,&type);
          sym->info = (uint8)type;
          checkEOL(s);
        }
      }
      else
        error(19);  /* undefined symbol */
    }
    else
      error(17);  /* missing argument */
  }
}


static void _size(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s;
  struct Symbol *sym;
  uint32 size;

  if (gv->pass) {
    s = getsymbol(gv,pl->operand);
    if (*gv->strbuf) {
      if (sym = search_symbol(gv,gv->strbuf)) {
        if (s = check_comma(s)) {
          s = getintexp(gv,s,&size);
          sym->size = size;
          checkEOL(s);
        }
      }
      else
        error(19);  /* undefined symbol */
    }
    else
      error(17);  /* missing argument */
  }
}


static void _ifeq(struct GlobalVars *gv,struct ParsedLine *pl)
{
  ifeqs(gv,pl,0);
}


static void _ifne(struct GlobalVars *gv,struct ParsedLine *pl)
{
  ifeqs(gv,pl,1);
}


static void _ifgt(struct GlobalVars *gv,struct ParsedLine *pl)
{
  ifeqs(gv,pl,2);
}


static void _ifge(struct GlobalVars *gv,struct ParsedLine *pl)
{
  ifeqs(gv,pl,3);
}


static void _iflt(struct GlobalVars *gv,struct ParsedLine *pl)
{
  ifeqs(gv,pl,4);
}


static void _ifle(struct GlobalVars *gv,struct ParsedLine *pl)
{
  ifeqs(gv,pl,5);
}


static void ifeqs(struct GlobalVars *gv,struct ParsedLine *pl,int cond)
{
  char *s;
  int32 val;
  bool *condptr;

  if (gv->iflevel < (MAX_IFLEVELS-1)) {
    condptr = &gv->ifcond[++gv->iflevel];
    *condptr = FALSE;
    s = getintexp(gv,pl->operand,&val);
    switch (cond) {
      case 0: /* eq */
        if (val==0)
          *condptr = TRUE;
        break;
      case 1: /* ne */
        if (val!=0)
          *condptr = TRUE;
        break;
      case 2: /* gt */
        if (val>0)
          *condptr = TRUE;
        break;
      case 3: /* ge */
        if (val>=0)
          *condptr = TRUE;
        break;
      case 4: /* lt */
        if (val<0)
          *condptr = TRUE;
        break;
      case 5: /* le */
        if (val<=0)
          *condptr = TRUE;
        break;
    }
    checkEOL(s);
  }
  else
    error(43);  /* maximum nesting depth for conditional assembly exceeded */
  pl->type = OT_IGNORE;  /* ignore in pass 2 */
}


static void _ifdef(struct GlobalVars *gv,struct ParsedLine *pl)
{
  ifdef(gv,pl,TRUE);
}


static void _ifndef(struct GlobalVars *gv,struct ParsedLine *pl)
{
  ifdef(gv,pl,FALSE);
}


static void ifdef(struct GlobalVars *gv,struct ParsedLine *pl,bool c)
{
  char *s;
  bool *condptr;

  if (gv->iflevel < (MAX_IFLEVELS-1)) {
    condptr = &gv->ifcond[++gv->iflevel];
    *condptr = c ? FALSE:TRUE;
    s = getsymbol(gv,pl->operand);
    if (*gv->strbuf)
      if (search_symbol(gv,gv->strbuf))
        *condptr = c;
    checkEOL(s);
  }
  else
    error(43);  /* maximum nesting depth for conditional assembly exceeded */
  pl->type = OT_IGNORE;  /* ignore in pass 2 */
}


static void _else(struct GlobalVars *gv,struct ParsedLine *pl)
{
  if (gv->iflevel > 0) {
    gv->ifcond[gv->iflevel] = FALSE;
    checkEOL(pl->operand);
  }
  else
    error(44,".else");  /* .else without matching .if */
  pl->type = OT_IGNORE;  /* ignore in pass 2 */
}


static void _endif(struct GlobalVars *gv,struct ParsedLine *pl)
{
  if (gv->iflevel > 0) {
    gv->iflevel--;
    checkEOL(pl->operand);
  }
  else
    error(44,".endif");  /* .endif without matching .if */
  pl->type = OT_IGNORE;  /* ignore in pass 2 */
}


static void _fail(struct GlobalVars *gv,struct ParsedLine *pl)
{
  error(45);  /* fail directive encountered */
}


static void _noop(struct GlobalVars *gv,struct ParsedLine *pl)
{
}


static void _ident(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s = getarg(gv,pl->operand);  /* identification / comment */

  gv->ident = allocstring(remquotes(gv->strbuf));
  checkEOL(s);
  pl->type = OT_IGNORE;  /* ignore in pass 2 */
}


static void _file(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s = getarg(gv,pl->operand);  /* source file name */

  gv->file = allocstring(remquotes(gv->strbuf));
  checkEOL(s);
  pl->type = OT_IGNORE;  /* ignore in pass 2 */
}


static void _baserel(struct GlobalVars *gv,struct ParsedLine *pl)
{
  char *s;
  struct Symbol *sym;
  uint32 tocreg;

  if (gv->pass) {
    s = getsymbol(gv,pl->operand);
    if (*gv->strbuf) {
      if (sym = search_symbol(gv,gv->strbuf)) {
        if (s = check_comma(s)) {
          s = getintexp(gv,s,&tocreg);
          if (sym->info == SYMI_SECTION) {
            /* activate new base relative addressing mode */
            gv->tocsect = sym->relsect;
            gv->rtoc = (int)tocreg;
          }
          else
            error(50,sym->name);  /* symbol is not a section base address */
          checkEOL(s);
        }
      }
      else
        error(19);  /* undefined symbol */
    }
    else
      error(17);  /* missing argument */
  }
}



struct Directive directives[] = {
  0,".new_section",_new_section,
  0,".section",_section,
  0,".set",_set,
  0,".macro",_macro,
  0,".include",_include,
  0,".align",_align,
  0,".space",_space,
  0,".byte",_byte,
  0,".ascii",_byte,
  0,".string",_string,
  0,".asciiz",_string,
  0,".half",_half,
  0,".short",_half,
  0,".word",_word,
  0,".long",_word,
  0,".float",_float,
  0,".double",_double,
  0,".uahalf",_uahalf,
  0,".uashort",_uahalf,
  0,".uaword",_uaword,
  0,".ualong",_uaword,
  0,".uafloat",_uafloat,
  0,".uadouble",_uadouble,
  0,".globl",_globl,
  0,".global",_globl,
  0,".extern",_globl,
  0,".local",_local,
  0,".bss",_bss,
  0,".lcomm",_bss,
  0,".comm",_comm,
  0,".type",_type,
  0,".size",_size,
  0,".if",_ifne,
  0,".ifeq",_ifeq,
  0,".ifne",_ifne,
  0,".ifgt",_ifgt,
  0,".ifge",_ifge,
  0,".iflt",_iflt,
  0,".ifle",_ifle,
  0,".ifdef",_ifdef,
  0,".ifndef",_ifndef,
  0,".else",_else,
  0,".endif",_endif,
  0,".fail",_fail,
  0,".ident",_ident,
  0,".file",_file,
  0,".baserel",_baserel,
  0,0 /* end mark */
};
