]> granicus.if.org Git - yasm/commitdiff
Initial revision
authorPeter Johnson <peter@tortall.net>
Mon, 4 Nov 2002 04:47:41 +0000 (04:47 -0000)
committerPeter Johnson <peter@tortall.net>
Mon, 4 Nov 2002 04:47:41 +0000 (04:47 -0000)
svn path=/trunk/yasm/; revision=794

18 files changed:
modules/preprocs/nasm/macros.pl [new file with mode: 0644]
modules/preprocs/nasm/nasm-eval.c [new file with mode: 0644]
modules/preprocs/nasm/nasm-eval.h [new file with mode: 0644]
modules/preprocs/nasm/nasm-pp.c [new file with mode: 0644]
modules/preprocs/nasm/nasm-pp.h [new file with mode: 0644]
modules/preprocs/nasm/nasm.h [new file with mode: 0644]
modules/preprocs/nasm/nasmlib.c [new file with mode: 0644]
modules/preprocs/nasm/nasmlib.h [new file with mode: 0644]
modules/preprocs/nasm/standard.mac [new file with mode: 0644]
src/preprocs/nasm/macros.pl [new file with mode: 0644]
src/preprocs/nasm/nasm-eval.c [new file with mode: 0644]
src/preprocs/nasm/nasm-eval.h [new file with mode: 0644]
src/preprocs/nasm/nasm-pp.c [new file with mode: 0644]
src/preprocs/nasm/nasm-pp.h [new file with mode: 0644]
src/preprocs/nasm/nasm.h [new file with mode: 0644]
src/preprocs/nasm/nasmlib.c [new file with mode: 0644]
src/preprocs/nasm/nasmlib.h [new file with mode: 0644]
src/preprocs/nasm/standard.mac [new file with mode: 0644]

diff --git a/modules/preprocs/nasm/macros.pl b/modules/preprocs/nasm/macros.pl
new file mode 100644 (file)
index 0000000..0934d17
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/perl -w
+# 
+# macros.pl   produce macros.c from standard.mac
+#
+# The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+# Julian Hall. All rights reserved. The software is
+# redistributable under the licence given in the file "Licence"
+# distributed in the NASM archive.
+
+use strict;
+
+my $fname;
+my $line = 0;
+my $index      = 0;
+my $tasm_count;
+
+undef $tasm_count;
+
+open(OUTPUT,">macros.c") or die "unable to open macros.c\n";
+    
+print OUTPUT "/* This file auto-generated from standard.mac by macros.pl" .
+" - don't edit it */\n\n#include <stddef.h>\n\nstatic const char *stdmac[] = {\n";
+    
+foreach $fname ( @ARGV ) {
+    open(INPUT,$fname) or die "unable to open $fname\n";
+    while (<INPUT>) {
+       $line++;
+       chomp;
+       if (m/^\s*\*END\*TASM\*MACROS\*\s*$/) {
+           $tasm_count = $index;
+       } elsif (m/^\s*((\s*([^\"\';\s]+|\"[^\"]*\"|\'[^\']*\'))*)\s*(;.*)?$/) {
+           $_ = $1;
+           s/\\/\\\\/g;
+           s/"/\\"/g;
+           if (length > 0) {
+               print OUTPUT "    \"$_\",\n";
+               $index++;
+           } 
+       } else {
+           die "$fname:$line:  error unterminated quote";
+       }
+    }
+    close(INPUT);
+}
+print OUTPUT "    NULL\n};\n";
+$tasm_count = $index unless ( defined($tasm_count) );
+print OUTPUT "#define TASM_MACRO_COUNT $tasm_count\n";
+close(OUTPUT);
diff --git a/modules/preprocs/nasm/nasm-eval.c b/modules/preprocs/nasm/nasm-eval.c
new file mode 100644 (file)
index 0000000..28aca64
--- /dev/null
@@ -0,0 +1,825 @@
+/* eval.c    expression evaluator for the Netwide Assembler
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ *
+ * initial version 27/iii/95 by Simon Tatham
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "nasm.h"
+#include "nasmlib.h"
+#include "eval.h"
+#include "labels.h"
+
+#define TEMPEXPRS_DELTA 128
+#define TEMPEXPR_DELTA 8
+
+static scanner scan;   /* Address of scanner routine */
+static efunc error;    /* Address of error reporting routine */
+static lfunc labelfunc;        /* Address of label routine */
+
+static struct ofmt *outfmt;  /* Structure of addresses of output routines */
+
+static expr **tempexprs = NULL;
+static int    ntempexprs;
+static int    tempexprs_size = 0;
+
+static expr  *tempexpr;
+static int   ntempexpr;
+static int   tempexpr_size;
+
+static struct tokenval *tokval;          /* The current token */
+static int i;                    /* The t_type of tokval */
+
+static void *scpriv;
+static loc_t *location;                /* Pointer to current line's segment,offset */
+static int *opflags;
+
+static struct eval_hints *hint;
+
+extern int  in_abs_seg;                /* ABSOLUTE segment flag */
+extern long abs_seg;           /* ABSOLUTE segment */
+extern long abs_offset;                /* ABSOLUTE segment offset */
+
+/*
+ * Unimportant cleanup is done to avoid confusing people who are trying
+ * to debug real memory leaks
+ */
+void eval_cleanup(void) 
+{
+    while (ntempexprs)
+       nasm_free (tempexprs[--ntempexprs]);
+    nasm_free (tempexprs);
+}
+
+/*
+ * Construct a temporary expression.
+ */
+static void begintemp(void) 
+{
+    tempexpr = NULL;
+    tempexpr_size = ntempexpr = 0;
+}
+
+static void addtotemp(long type, long value) 
+{
+    while (ntempexpr >= tempexpr_size) {
+       tempexpr_size += TEMPEXPR_DELTA;
+       tempexpr = nasm_realloc(tempexpr,
+                                tempexpr_size*sizeof(*tempexpr));
+    }
+    tempexpr[ntempexpr].type = type;
+    tempexpr[ntempexpr++].value = value;
+}
+
+static expr *finishtemp(void) 
+{
+    addtotemp (0L, 0L);                       /* terminate */
+    while (ntempexprs >= tempexprs_size) {
+       tempexprs_size += TEMPEXPRS_DELTA;
+       tempexprs = nasm_realloc(tempexprs,
+                                tempexprs_size*sizeof(*tempexprs));
+    }
+    return tempexprs[ntempexprs++] = tempexpr;
+}
+
+/*
+ * Add two vector datatypes. We have some bizarre behaviour on far-
+ * absolute segment types: we preserve them during addition _only_
+ * if one of the segments is a truly pure scalar.
+ */
+static expr *add_vectors(expr *p, expr *q) 
+{
+    int preserve;
+
+    preserve = is_really_simple(p) || is_really_simple(q);
+
+    begintemp();
+
+    while (p->type && q->type &&
+          p->type < EXPR_SEGBASE+SEG_ABS &&
+          q->type < EXPR_SEGBASE+SEG_ABS)
+    {
+       int lasttype;
+
+       if (p->type > q->type) {
+           addtotemp(q->type, q->value);
+           lasttype = q++->type;
+       } else if (p->type < q->type) {
+           addtotemp(p->type, p->value);
+           lasttype = p++->type;
+       } else {                       /* *p and *q have same type */
+           long sum = p->value + q->value;
+           if (sum)
+               addtotemp(p->type, sum);
+           lasttype = p->type;
+           p++, q++;
+       }
+       if (lasttype == EXPR_UNKNOWN) {
+           return finishtemp();
+       }
+    }
+    while (p->type &&
+          (preserve || p->type < EXPR_SEGBASE+SEG_ABS)) 
+    {
+       addtotemp(p->type, p->value);
+       p++;
+    }
+    while (q->type &&
+          (preserve || q->type < EXPR_SEGBASE+SEG_ABS)) 
+    {
+       addtotemp(q->type, q->value);
+       q++;
+    }
+
+    return finishtemp();
+}
+
+/*
+ * Multiply a vector by a scalar. Strip far-absolute segment part
+ * if present.
+ *
+ * Explicit treatment of UNKNOWN is not required in this routine,
+ * since it will silently do the Right Thing anyway.
+ *
+ * If `affect_hints' is set, we also change the hint type to
+ * NOTBASE if a MAKEBASE hint points at a register being
+ * multiplied. This allows [eax*1+ebx] to hint EBX rather than EAX
+ * as the base register.
+ */
+static expr *scalar_mult(expr *vect, long scalar, int affect_hints) 
+{
+    expr *p = vect;
+
+    while (p->type && p->type < EXPR_SEGBASE+SEG_ABS) {
+       p->value = scalar * (p->value);
+       if (hint && hint->type == EAH_MAKEBASE &&
+           p->type == hint->base && affect_hints)
+           hint->type = EAH_NOTBASE;
+       p++;
+    }
+    p->type = 0;
+
+    return vect;
+}
+
+static expr *scalarvect (long scalar) 
+{
+    begintemp();
+    addtotemp(EXPR_SIMPLE, scalar);
+    return finishtemp();
+}
+
+static expr *unknown_expr (void) 
+{
+    begintemp();
+    addtotemp(EXPR_UNKNOWN, 1L);
+    return finishtemp();
+}
+
+/*
+ * The SEG operator: calculate the segment part of a relocatable
+ * value. Return NULL, as usual, if an error occurs. Report the
+ * error too.
+ */
+static expr *segment_part (expr *e) 
+{
+    long seg;
+
+    if (is_unknown(e))
+       return unknown_expr();
+
+    if (!is_reloc(e)) {
+       error(ERR_NONFATAL, "cannot apply SEG to a non-relocatable value");
+       return NULL;
+    }
+
+    seg = reloc_seg(e);
+    if (seg == NO_SEG) {
+       error(ERR_NONFATAL, "cannot apply SEG to a non-relocatable value");
+       return NULL;
+    } else if (seg & SEG_ABS) {
+       return scalarvect(seg & ~SEG_ABS);
+    } else if (seg & 1) {
+       error(ERR_NONFATAL, "SEG applied to something which"
+             " is already a segment base");
+       return NULL;
+    }
+    else {
+       long base = outfmt->segbase(seg+1);
+
+       begintemp();
+       addtotemp((base == NO_SEG ? EXPR_UNKNOWN : EXPR_SEGBASE+base), 1L);
+       return finishtemp();
+    }
+}
+
+/*
+ * Recursive-descent parser. Called with a single boolean operand,
+ * which is TRUE if the evaluation is critical (i.e. unresolved
+ * symbols are an error condition). Must update the global `i' to
+ * reflect the token after the parsed string. May return NULL.
+ *
+ * evaluate() should report its own errors: on return it is assumed
+ * that if NULL has been returned, the error has already been
+ * reported.
+ */
+
+/*
+ * Grammar parsed is:
+ *
+ * expr  : bexpr [ WRT expr6 ]
+ * bexpr : rexp0 or expr0 depending on relative-mode setting
+ * rexp0 : rexp1 [ {||} rexp1...]
+ * rexp1 : rexp2 [ {^^} rexp2...]
+ * rexp2 : rexp3 [ {&&} rexp3...]
+ * rexp3 : expr0 [ {=,==,<>,!=,<,>,<=,>=} expr0 ]
+ * expr0 : expr1 [ {|} expr1...]
+ * expr1 : expr2 [ {^} expr2...]
+ * expr2 : expr3 [ {&} expr3...]
+ * expr3 : expr4 [ {<<,>>} expr4...]
+ * expr4 : expr5 [ {+,-} expr5...]
+ * expr5 : expr6 [ {*,/,%,//,%%} expr6...]
+ * expr6 : { ~,+,-,SEG } expr6
+ *       | (bexpr)
+ *       | symbol
+ *       | $
+ *       | number
+ */
+
+static expr *rexp0(int), *rexp1(int), *rexp2(int), *rexp3(int);
+
+static expr *expr0(int), *expr1(int), *expr2(int), *expr3(int);
+static expr *expr4(int), *expr5(int), *expr6(int);
+
+static expr *(*bexpr)(int);
+
+static expr *rexp0(int critical) 
+{
+    expr *e, *f;
+
+    e = rexp1(critical);
+    if (!e)
+       return NULL;
+
+    while (i == TOKEN_DBL_OR) 
+    {  
+       i = scan(scpriv, tokval);
+       f = rexp1(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`|' operator may only be applied to"
+                 " scalar values");
+       }
+
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect ((long) (reloc_value(e) || reloc_value(f)));
+    }
+    return e;
+}
+
+static expr *rexp1(int critical) 
+{
+    expr *e, *f;
+
+    e = rexp2(critical);
+    if (!e)
+       return NULL;
+    
+    while (i == TOKEN_DBL_XOR) 
+    {
+       i = scan(scpriv, tokval);
+       f = rexp2(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`^' operator may only be applied to"
+                 " scalar values");
+       }
+
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect ((long) (!reloc_value(e) ^ !reloc_value(f)));
+    }
+    return e;
+}
+
+static expr *rexp2(int critical) 
+{
+    expr *e, *f;
+
+    e = rexp3(critical);
+    if (!e)
+       return NULL;
+    while (i == TOKEN_DBL_AND) 
+    {
+       i = scan(scpriv, tokval);
+       f = rexp3(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`&' operator may only be applied to"
+                 " scalar values");
+       }
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect ((long) (reloc_value(e) && reloc_value(f)));
+    }
+    return e;
+}
+
+static expr *rexp3(int critical) 
+{
+    expr *e, *f;
+    long v;
+
+    e = expr0(critical);
+    if (!e)
+       return NULL;
+
+    while (i == TOKEN_EQ || i == TOKEN_LT || i == TOKEN_GT ||
+          i == TOKEN_NE || i == TOKEN_LE || i == TOKEN_GE) 
+    {
+       int j = i;
+       i = scan(scpriv, tokval);
+       f = expr0(critical);
+       if (!f)
+           return NULL;
+
+       e = add_vectors (e, scalar_mult(f, -1L, FALSE));
+
+       switch (j) 
+       {
+         case TOKEN_EQ: case TOKEN_NE:
+           if (is_unknown(e))
+               v = -1;                /* means unknown */
+           else if (!is_really_simple(e) || reloc_value(e) != 0)
+               v = (j == TOKEN_NE);   /* unequal, so return TRUE if NE */
+           else
+               v = (j == TOKEN_EQ);   /* equal, so return TRUE if EQ */
+           break;
+         default:
+           if (is_unknown(e))
+               v = -1;                /* means unknown */
+           else if (!is_really_simple(e)) {
+               error(ERR_NONFATAL, "`%s': operands differ by a non-scalar",
+                     (j == TOKEN_LE ? "<=" : j == TOKEN_LT ? "<" :
+                      j == TOKEN_GE ? ">=" : ">"));
+               v = 0;                 /* must set it to _something_ */
+           } else {
+               int vv = reloc_value(e);
+               if (vv == 0)
+                   v = (j == TOKEN_LE || j == TOKEN_GE);
+               else if (vv > 0)
+                   v = (j == TOKEN_GE || j == TOKEN_GT);
+               else /* vv < 0 */
+                   v = (j == TOKEN_LE || j == TOKEN_LT);
+           }
+           break;
+       }
+
+       if (v == -1)
+           e = unknown_expr();
+       else
+           e = scalarvect(v);
+    }
+    return e;
+}
+
+static expr *expr0(int critical) 
+{
+    expr *e, *f;
+
+    e = expr1(critical);
+    if (!e)
+       return NULL;
+
+    while (i == '|') 
+    {
+       i = scan(scpriv, tokval);
+       f = expr1(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`|' operator may only be applied to"
+                 " scalar values");
+       }
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect (reloc_value(e) | reloc_value(f));
+    }
+    return e;
+}
+
+static expr *expr1(int critical) 
+{
+    expr *e, *f;
+
+    e = expr2(critical);
+    if (!e)
+       return NULL;
+
+    while (i == '^') {
+       i = scan(scpriv, tokval);
+       f = expr2(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`^' operator may only be applied to"
+                 " scalar values");
+       }
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect (reloc_value(e) ^ reloc_value(f));
+    }
+    return e;
+}
+
+static expr *expr2(int critical) 
+{
+    expr *e, *f;
+
+    e = expr3(critical);
+    if (!e)
+       return NULL;
+
+    while (i == '&') {
+       i = scan(scpriv, tokval);
+       f = expr3(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`&' operator may only be applied to"
+                 " scalar values");
+       }
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect (reloc_value(e) & reloc_value(f));
+    }
+    return e;
+}
+
+static expr *expr3(int critical) 
+{
+    expr *e, *f;
+
+    e = expr4(critical);
+    if (!e)
+       return NULL;
+
+    while (i == TOKEN_SHL || i == TOKEN_SHR) 
+    {
+       int j = i;
+       i = scan(scpriv, tokval);
+       f = expr4(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "shift operator may only be applied to"
+                 " scalar values");
+       } else if (is_just_unknown(e) || is_just_unknown(f)) {
+           e = unknown_expr();
+       } else switch (j) {
+         case TOKEN_SHL:
+           e = scalarvect (reloc_value(e) << reloc_value(f));
+           break;
+         case TOKEN_SHR:
+           e = scalarvect (((unsigned long)reloc_value(e)) >>
+                           reloc_value(f));
+           break;
+       }
+    }
+    return e;
+}
+
+static expr *expr4(int critical) 
+{
+    expr *e, *f;
+
+    e = expr5(critical);
+    if (!e)
+       return NULL;
+    while (i == '+' || i == '-') 
+    {
+       int j = i;
+       i = scan(scpriv, tokval);
+       f = expr5(critical);
+       if (!f)
+           return NULL;
+       switch (j) {
+         case '+':
+           e = add_vectors (e, f);
+           break;
+         case '-':
+           e = add_vectors (e, scalar_mult(f, -1L, FALSE));
+           break;
+       }
+    }
+    return e;
+}
+
+static expr *expr5(int critical) 
+{
+    expr *e, *f;
+
+    e = expr6(critical);
+    if (!e)
+       return NULL;
+    while (i == '*' || i == '/' || i == '%' ||
+          i == TOKEN_SDIV || i == TOKEN_SMOD) 
+    {
+       int j = i;
+       i = scan(scpriv, tokval);
+       f = expr6(critical);
+       if (!f)
+           return NULL;
+       if (j != '*' && (!(is_simple(e) || is_just_unknown(e)) ||
+                        !(is_simple(f) || is_just_unknown(f)))) 
+       {
+           error(ERR_NONFATAL, "division operator may only be applied to"
+                 " scalar values");
+           return NULL;
+       }
+       if (j != '*' && !is_unknown(f) && reloc_value(f) == 0) {
+           error(ERR_NONFATAL, "division by zero");
+           return NULL;
+       }
+       switch (j) {
+         case '*':
+           if (is_simple(e))
+               e = scalar_mult (f, reloc_value(e), TRUE);
+           else if (is_simple(f))
+               e = scalar_mult (e, reloc_value(f), TRUE);
+           else if (is_just_unknown(e) && is_just_unknown(f))
+               e = unknown_expr();
+           else {
+               error(ERR_NONFATAL, "unable to multiply two "
+                     "non-scalar objects");
+               return NULL;
+           }
+           break;
+         case '/':
+           if (is_just_unknown(e) || is_just_unknown(f))
+               e = unknown_expr();
+           else
+               e = scalarvect (((unsigned long)reloc_value(e)) /
+                               ((unsigned long)reloc_value(f)));
+           break;
+         case '%':
+           if (is_just_unknown(e) || is_just_unknown(f))
+               e = unknown_expr();
+           else
+               e = scalarvect (((unsigned long)reloc_value(e)) %
+                               ((unsigned long)reloc_value(f)));
+           break;
+         case TOKEN_SDIV:
+           if (is_just_unknown(e) || is_just_unknown(f))
+               e = unknown_expr();
+           else
+               e = scalarvect (((signed long)reloc_value(e)) /
+                               ((signed long)reloc_value(f)));
+           break;
+         case TOKEN_SMOD:
+           if (is_just_unknown(e) || is_just_unknown(f))
+               e = unknown_expr();
+           else
+               e = scalarvect (((signed long)reloc_value(e)) %
+                               ((signed long)reloc_value(f)));
+           break;
+       }
+    }
+    return e;
+}
+
+static expr *expr6(int critical) 
+{
+    long type;
+    expr *e;
+    long label_seg, label_ofs;
+
+    if (i == '-') {
+       i = scan(scpriv, tokval);
+       e = expr6(critical);
+       if (!e)
+           return NULL;
+       return scalar_mult (e, -1L, FALSE);
+    } else if (i == '+') {
+       i = scan(scpriv, tokval);
+       return expr6(critical);
+    } else if (i == '~') {
+       i = scan(scpriv, tokval);
+       e = expr6(critical);
+       if (!e)
+           return NULL;
+       if (is_just_unknown(e))
+           return unknown_expr();
+       else if (!is_simple(e)) {
+           error(ERR_NONFATAL, "`~' operator may only be applied to"
+                 " scalar values");
+           return NULL;
+       }
+       return scalarvect(~reloc_value(e));
+    } else if (i == TOKEN_SEG) {
+       i = scan(scpriv, tokval);
+       e = expr6(critical);
+       if (!e)
+           return NULL;
+       e = segment_part(e);
+       if (!e)
+           return NULL;
+       if (is_unknown(e) && critical) {
+           error(ERR_NONFATAL, "unable to determine segment base");
+           return NULL;
+       }
+       return e;
+    } else if (i == '(') {
+       i = scan(scpriv, tokval);
+       e = bexpr(critical);
+       if (!e)
+           return NULL;
+       if (i != ')') {
+           error(ERR_NONFATAL, "expecting `)'");
+           return NULL;
+       }
+       i = scan(scpriv, tokval);
+       return e;
+    } 
+    else if (i == TOKEN_NUM || i == TOKEN_REG || i == TOKEN_ID ||
+            i == TOKEN_HERE || i == TOKEN_BASE) 
+    {
+       begintemp();
+       switch (i) {
+         case TOKEN_NUM:
+           addtotemp(EXPR_SIMPLE, tokval->t_integer);
+           break;
+         case TOKEN_REG:
+           addtotemp(tokval->t_integer, 1L);
+           if (hint && hint->type == EAH_NOHINT)
+               hint->base = tokval->t_integer, hint->type = EAH_MAKEBASE;
+           break;
+         case TOKEN_ID:
+         case TOKEN_HERE:
+         case TOKEN_BASE:
+           /*
+            * If !location->known, this indicates that no
+            * symbol, Here or Base references are valid because we
+            * are in preprocess-only mode.
+            */
+           if (!location->known) {
+               error(ERR_NONFATAL,
+                     "%s not supported in preprocess-only mode",
+                     (i == TOKEN_ID ? "symbol references" :
+                      i == TOKEN_HERE ? "`$'" : "`$$'"));
+               addtotemp(EXPR_UNKNOWN, 1L);
+               break;
+           }
+
+           type = EXPR_SIMPLE;        /* might get overridden by UNKNOWN */
+           if (i == TOKEN_BASE)
+           {
+               label_seg = in_abs_seg ? abs_seg : location->segment;
+               label_ofs = 0;
+           } else if (i == TOKEN_HERE) {
+               label_seg = in_abs_seg ? abs_seg : location->segment;
+               label_ofs = in_abs_seg ? abs_offset : location->offset;
+           } else {
+               if (!labelfunc(tokval->t_charptr,&label_seg,&label_ofs))
+               {
+               if (critical == 2) {
+                   error (ERR_NONFATAL, "symbol `%s' undefined",
+                          tokval->t_charptr);
+                   return NULL;
+               } else if (critical == 1) {
+                       error (ERR_NONFATAL,
+                               "symbol `%s' not defined before use",
+                          tokval->t_charptr);
+                   return NULL;
+               } else {
+                   if (opflags)
+                       *opflags |= 1;
+                   type = EXPR_UNKNOWN;
+                   label_seg = NO_SEG;
+                   label_ofs = 1;
+               }
+           }
+               if (opflags && is_extern (tokval->t_charptr))
+                   *opflags |= OPFLAG_EXTERN;
+           }
+           addtotemp(type, label_ofs);
+           if (label_seg!=NO_SEG)
+               addtotemp(EXPR_SEGBASE + label_seg, 1L);
+           break;
+       }
+       i = scan(scpriv, tokval);
+       return finishtemp();
+    } else {
+       error(ERR_NONFATAL, "expression syntax error");
+       return NULL;
+    }
+}
+
+void eval_global_info (struct ofmt *output, lfunc lookup_label, loc_t *locp) 
+{
+    outfmt = output;
+    labelfunc = lookup_label;
+    location = locp;
+}
+
+expr *evaluate (scanner sc, void *scprivate, struct tokenval *tv,
+               int *fwref, int critical, efunc report_error,
+               struct eval_hints *hints) 
+{
+    expr *e;
+    expr *f = NULL;
+
+    hint = hints;
+    if (hint)
+       hint->type = EAH_NOHINT;
+
+    if (critical & CRITICAL) {
+       critical &= ~CRITICAL;
+       bexpr = rexp0;
+    } else
+       bexpr = expr0;
+
+    scan = sc;
+    scpriv = scprivate;
+    tokval = tv;
+    error = report_error;
+    opflags = fwref;
+
+    if (tokval->t_type == TOKEN_INVALID)
+       i = scan(scpriv, tokval);
+    else
+       i = tokval->t_type;
+
+    while (ntempexprs)                /* initialise temporary storage */
+       nasm_free (tempexprs[--ntempexprs]);
+
+    e = bexpr (critical);
+    if (!e)
+       return NULL;
+
+    if (i == TOKEN_WRT) {
+       i = scan(scpriv, tokval);      /* eat the WRT */
+       f = expr6 (critical);
+       if (!f)
+           return NULL;
+    }
+    e = scalar_mult (e, 1L, FALSE);    /* strip far-absolute segment part */
+    if (f) {
+       expr *g;
+       if (is_just_unknown(f))
+           g = unknown_expr();
+       else {
+           long value;
+           begintemp();
+           if (!is_reloc(f)) {
+               error(ERR_NONFATAL, "invalid right-hand operand to WRT");
+               return NULL;
+           }
+           value = reloc_seg(f);
+           if (value == NO_SEG)
+               value = reloc_value(f) | SEG_ABS;
+           else if (!(value & SEG_ABS) && !(value % 2) && critical) 
+           {
+               error(ERR_NONFATAL, "invalid right-hand operand to WRT");
+               return NULL;
+           }
+           addtotemp(EXPR_WRT, value);
+           g = finishtemp();
+       }
+       e = add_vectors (e, g);
+    }
+    return e;
+}
diff --git a/modules/preprocs/nasm/nasm-eval.h b/modules/preprocs/nasm/nasm-eval.h
new file mode 100644 (file)
index 0000000..a933cbf
--- /dev/null
@@ -0,0 +1,28 @@
+/* eval.h   header file for eval.c
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ */
+
+#ifndef NASM_EVAL_H
+#define NASM_EVAL_H
+
+/*
+ * Called once to tell the evaluator what output format is
+ * providing segment-base details, and what function can be used to
+ * look labels up.
+ */
+void eval_global_info (struct ofmt *output, lfunc lookup_label, loc_t *locp);
+
+/*
+ * The evaluator itself.
+ */
+expr *evaluate (scanner sc, void *scprivate, struct tokenval *tv,
+               int *fwref, int critical, efunc report_error,
+               struct eval_hints *hints);
+
+void eval_cleanup(void);
+
+#endif
diff --git a/modules/preprocs/nasm/nasm-pp.c b/modules/preprocs/nasm/nasm-pp.c
new file mode 100644 (file)
index 0000000..0770812
--- /dev/null
@@ -0,0 +1,4459 @@
+/* -*- mode: c; c-file-style: "bsd" -*- */
+/* preproc.c   macro preprocessor for the Netwide Assembler
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ *
+ * initial version 18/iii/97 by Simon Tatham
+ */
+
+/* Typical flow of text through preproc
+ *
+ * pp_getline gets tokenised lines, either
+ *
+ *   from a macro expansion
+ *
+ * or
+ *   {
+ *   read_line  gets raw text from stdmacpos, or predef, or current input file
+ *   tokenise   converts to tokens
+ *   }
+ *
+ * expand_mmac_params is used to expand %1 etc., unless a macro is being
+ * defined or a false conditional is being processed
+ * (%0, %1, %+1, %-1, %%foo
+ *
+ * do_directive checks for directives
+ *
+ * expand_smacro is used to expand single line macros
+ *
+ * expand_mmacro is used to expand multi-line macros
+ *
+ * detoken is used to convert the line back to text
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "nasm.h"
+#include "nasmlib.h"
+
+typedef struct SMacro SMacro;
+typedef struct MMacro MMacro;
+typedef struct Context Context;
+typedef struct Token Token;
+typedef struct Blocks Blocks;
+typedef struct Line Line;
+typedef struct Include Include;
+typedef struct Cond Cond;
+typedef struct IncPath IncPath;
+
+/*
+ * Store the definition of a single-line macro.
+ */
+struct SMacro
+{
+    SMacro *next;
+    char *name;
+    int casesense;
+    int nparam;
+    int in_progress;
+    Token *expansion;
+};
+
+/*
+ * Store the definition of a multi-line macro. This is also used to
+ * store the interiors of `%rep...%endrep' blocks, which are
+ * effectively self-re-invoking multi-line macros which simply
+ * don't have a name or bother to appear in the hash tables. %rep
+ * blocks are signified by having a NULL `name' field.
+ *
+ * In a MMacro describing a `%rep' block, the `in_progress' field
+ * isn't merely boolean, but gives the number of repeats left to
+ * run.
+ *
+ * The `next' field is used for storing MMacros in hash tables; the
+ * `next_active' field is for stacking them on istk entries.
+ *
+ * When a MMacro is being expanded, `params', `iline', `nparam',
+ * `paramlen', `rotate' and `unique' are local to the invocation.
+ */
+struct MMacro
+{
+    MMacro *next;
+    char *name;
+    int casesense;
+    int nparam_min, nparam_max;
+    int plus;                  /* is the last parameter greedy? */
+    int nolist;                        /* is this macro listing-inhibited? */
+    int in_progress;
+    Token *dlist;              /* All defaults as one list */
+    Token **defaults;          /* Parameter default pointers */
+    int ndefs;                 /* number of default parameters */
+    Line *expansion;
+
+    MMacro *next_active;
+    MMacro *rep_nest;          /* used for nesting %rep */
+    Token **params;            /* actual parameters */
+    Token *iline;              /* invocation line */
+    int nparam, rotate, *paramlen;
+    unsigned long unique;
+    int lineno;                        /* Current line number on expansion */
+};
+
+/*
+ * The context stack is composed of a linked list of these.
+ */
+struct Context
+{
+    Context *next;
+    SMacro *localmac;
+    char *name;
+    unsigned long number;
+};
+
+/*
+ * This is the internal form which we break input lines up into.
+ * Typically stored in linked lists.
+ *
+ * Note that `type' serves a double meaning: TOK_SMAC_PARAM is not
+ * necessarily used as-is, but is intended to denote the number of
+ * the substituted parameter. So in the definition
+ *
+ *     %define a(x,y) ( (x) & ~(y) )
+ * 
+ * the token representing `x' will have its type changed to
+ * TOK_SMAC_PARAM, but the one representing `y' will be
+ * TOK_SMAC_PARAM+1.
+ *
+ * TOK_INTERNAL_STRING is a dirty hack: it's a single string token
+ * which doesn't need quotes around it. Used in the pre-include
+ * mechanism as an alternative to trying to find a sensible type of
+ * quote to use on the filename we were passed.
+ */
+struct Token
+{
+    Token *next;
+    char *text;
+    SMacro *mac;               /* associated macro for TOK_SMAC_END */
+    int type;
+};
+enum
+{
+    TOK_WHITESPACE = 1, TOK_COMMENT, TOK_ID, TOK_PREPROC_ID, TOK_STRING,
+    TOK_NUMBER, TOK_SMAC_END, TOK_OTHER, TOK_SMAC_PARAM,
+    TOK_INTERNAL_STRING
+};
+
+/*
+ * Multi-line macro definitions are stored as a linked list of
+ * these, which is essentially a container to allow several linked
+ * lists of Tokens.
+ * 
+ * Note that in this module, linked lists are treated as stacks
+ * wherever possible. For this reason, Lines are _pushed_ on to the
+ * `expansion' field in MMacro structures, so that the linked list,
+ * if walked, would give the macro lines in reverse order; this
+ * means that we can walk the list when expanding a macro, and thus
+ * push the lines on to the `expansion' field in _istk_ in reverse
+ * order (so that when popped back off they are in the right
+ * order). It may seem cockeyed, and it relies on my design having
+ * an even number of steps in, but it works...
+ *
+ * Some of these structures, rather than being actual lines, are
+ * markers delimiting the end of the expansion of a given macro.
+ * This is for use in the cycle-tracking and %rep-handling code.
+ * Such structures have `finishes' non-NULL, and `first' NULL. All
+ * others have `finishes' NULL, but `first' may still be NULL if
+ * the line is blank.
+ */
+struct Line
+{
+    Line *next;
+    MMacro *finishes;
+    Token *first;
+};
+
+/*
+ * To handle an arbitrary level of file inclusion, we maintain a
+ * stack (ie linked list) of these things.
+ */
+struct Include
+{
+    Include *next;
+    FILE *fp;
+    Cond *conds;
+    Line *expansion;
+    char *fname;
+    int lineno, lineinc;
+    MMacro *mstk;              /* stack of active macros/reps */
+};
+
+/*
+ * Include search path. This is simply a list of strings which get
+ * prepended, in turn, to the name of an include file, in an
+ * attempt to find the file if it's not in the current directory.
+ */
+struct IncPath
+{
+    IncPath *next;
+    char *path;
+};
+
+/*
+ * Conditional assembly: we maintain a separate stack of these for
+ * each level of file inclusion. (The only reason we keep the
+ * stacks separate is to ensure that a stray `%endif' in a file
+ * included from within the true branch of a `%if' won't terminate
+ * it and cause confusion: instead, rightly, it'll cause an error.)
+ */
+struct Cond
+{
+    Cond *next;
+    int state;
+};
+enum
+{
+    /*
+     * These states are for use just after %if or %elif: IF_TRUE
+     * means the condition has evaluated to truth so we are
+     * currently emitting, whereas IF_FALSE means we are not
+     * currently emitting but will start doing so if a %else comes
+     * up. In these states, all directives are admissible: %elif,
+     * %else and %endif. (And of course %if.)
+     */
+    COND_IF_TRUE, COND_IF_FALSE,
+    /*
+     * These states come up after a %else: ELSE_TRUE means we're
+     * emitting, and ELSE_FALSE means we're not. In ELSE_* states,
+     * any %elif or %else will cause an error.
+     */
+    COND_ELSE_TRUE, COND_ELSE_FALSE,
+    /*
+     * This state means that we're not emitting now, and also that
+     * nothing until %endif will be emitted at all. It's for use in
+     * two circumstances: (i) when we've had our moment of emission
+     * and have now started seeing %elifs, and (ii) when the
+     * condition construct in question is contained within a
+     * non-emitting branch of a larger condition construct.
+     */
+    COND_NEVER
+};
+#define emitting(x) ( (x) == COND_IF_TRUE || (x) == COND_ELSE_TRUE )
+
+/* 
+ * These defines are used as the possible return values for do_directive
+ */
+#define NO_DIRECTIVE_FOUND  0
+#define DIRECTIVE_FOUND            1
+
+/*
+ * Condition codes. Note that we use c_ prefix not C_ because C_ is
+ * used in nasm.h for the "real" condition codes. At _this_ level,
+ * we treat CXZ and ECXZ as condition codes, albeit non-invertible
+ * ones, so we need a different enum...
+ */
+static const char *conditions[] = {
+    "a", "ae", "b", "be", "c", "cxz", "e", "ecxz", "g", "ge", "l", "le",
+    "na", "nae", "nb", "nbe", "nc", "ne", "ng", "nge", "nl", "nle", "no",
+    "np", "ns", "nz", "o", "p", "pe", "po", "s", "z"
+};
+enum
+{
+    c_A, c_AE, c_B, c_BE, c_C, c_CXZ, c_E, c_ECXZ, c_G, c_GE, c_L, c_LE,
+    c_NA, c_NAE, c_NB, c_NBE, c_NC, c_NE, c_NG, c_NGE, c_NL, c_NLE, c_NO,
+    c_NP, c_NS, c_NZ, c_O, c_P, c_PE, c_PO, c_S, c_Z
+};
+static int inverse_ccs[] = {
+    c_NA, c_NAE, c_NB, c_NBE, c_NC, -1, c_NE, -1, c_NG, c_NGE, c_NL, c_NLE,
+    c_A, c_AE, c_B, c_BE, c_C, c_E, c_G, c_GE, c_L, c_LE, c_O, c_P, c_S,
+    c_Z, c_NO, c_NP, c_PO, c_PE, c_NS, c_NZ
+};
+
+/*
+ * Directive names.
+ */
+static const char *directives[] = {
+    "%arg",
+    "%assign", "%clear", "%define", "%elif", "%elifctx", "%elifdef",
+    "%elifid", "%elifidn", "%elifidni", "%elifmacro", "%elifnctx", "%elifndef",
+    "%elifnid", "%elifnidn", "%elifnidni", "%elifnmacro", "%elifnnum", "%elifnstr",
+    "%elifnum", "%elifstr", "%else", "%endif", "%endm", "%endmacro",
+    "%endrep", "%error", "%exitrep", "%iassign", "%idefine", "%if",
+    "%ifctx", "%ifdef", "%ifid", "%ifidn", "%ifidni", "%ifmacro", "%ifnctx",
+    "%ifndef", "%ifnid", "%ifnidn", "%ifnidni", "%ifnmacro", "%ifnnum",
+    "%ifnstr", "%ifnum", "%ifstr", "%imacro", "%include",
+    "%ixdefine", "%line",
+    "%local",
+    "%macro", "%pop", "%push", "%rep", "%repl", "%rotate",
+    "%stacksize",
+    "%strlen", "%substr", "%undef", "%xdefine"
+};
+enum
+{
+    PP_ARG,
+    PP_ASSIGN, PP_CLEAR, PP_DEFINE, PP_ELIF, PP_ELIFCTX, PP_ELIFDEF,
+    PP_ELIFID, PP_ELIFIDN, PP_ELIFIDNI, PP_ELIFMACRO, PP_ELIFNCTX, PP_ELIFNDEF,
+    PP_ELIFNID, PP_ELIFNIDN, PP_ELIFNIDNI, PP_ELIFNMACRO, PP_ELIFNNUM, PP_ELIFNSTR,
+    PP_ELIFNUM, PP_ELIFSTR, PP_ELSE, PP_ENDIF, PP_ENDM, PP_ENDMACRO,
+    PP_ENDREP, PP_ERROR, PP_EXITREP, PP_IASSIGN, PP_IDEFINE, PP_IF,
+    PP_IFCTX, PP_IFDEF, PP_IFID, PP_IFIDN, PP_IFIDNI, PP_IFMACRO, PP_IFNCTX,
+    PP_IFNDEF, PP_IFNID, PP_IFNIDN, PP_IFNIDNI, PP_IFNMACRO, PP_IFNNUM,
+    PP_IFNSTR, PP_IFNUM, PP_IFSTR, PP_IMACRO, PP_INCLUDE,
+    PP_IXDEFINE, PP_LINE,
+    PP_LOCAL,
+    PP_MACRO, PP_POP, PP_PUSH, PP_REP, PP_REPL, PP_ROTATE,
+    PP_STACKSIZE,
+    PP_STRLEN, PP_SUBSTR, PP_UNDEF, PP_XDEFINE
+};
+
+/* If this is a an IF, ELIF, ELSE or ENDIF keyword */
+static int is_condition(int arg)
+{
+    return ((arg >= PP_ELIF) && (arg <= PP_ENDIF)) ||
+       ((arg >= PP_IF) && (arg <= PP_IFSTR));
+}
+
+/* For TASM compatibility we need to be able to recognise TASM compatible
+ * conditional compilation directives. Using the NASM pre-processor does
+ * not work, so we look for them specifically from the following list and
+ * then jam in the equivalent NASM directive into the input stream.
+ */
+
+#ifndef MAX
+#       define MAX(a,b) ( ((a) > (b)) ? (a) : (b))
+#endif
+
+enum
+{
+    TM_ARG, TM_ELIF, TM_ELSE, TM_ENDIF, TM_IF, TM_IFDEF, TM_IFDIFI,
+    TM_IFNDEF, TM_INCLUDE, TM_LOCAL
+};
+
+static const char *tasm_directives[] = {
+    "arg", "elif", "else", "endif", "if", "ifdef", "ifdifi",
+    "ifndef", "include", "local"
+};
+
+static int StackSize = 4;
+static char *StackPointer = "ebp";
+static int ArgOffset = 8;
+static int LocalOffset = 4;
+
+
+static Context *cstk;
+static Include *istk;
+static IncPath *ipath = NULL;
+
+static efunc _error;           /* Pointer to client-provided error reporting function */
+static evalfunc evaluate;
+
+static int pass;               /* HACK: pass 0 = generate dependencies only */
+
+static unsigned long unique;   /* unique identifier numbers */
+
+static Line *predef = NULL;
+
+static ListGen *list;
+
+/*
+ * The number of hash values we use for the macro lookup tables.
+ * FIXME: We should *really* be able to configure this at run time,
+ * or even have the hash table automatically expanding when necessary.
+ */
+#define NHASH 31
+
+/*
+ * The current set of multi-line macros we have defined.
+ */
+static MMacro *mmacros[NHASH];
+
+/*
+ * The current set of single-line macros we have defined.
+ */
+static SMacro *smacros[NHASH];
+
+/*
+ * The multi-line macro we are currently defining, or the %rep
+ * block we are currently reading, if any.
+ */
+static MMacro *defining;
+
+/*
+ * The number of macro parameters to allocate space for at a time.
+ */
+#define PARAM_DELTA 16
+
+/*
+ * The standard macro set: defined as `static char *stdmac[]'. Also
+ * gives our position in the macro set, when we're processing it.
+ */
+#include "macros.c"
+static const char **stdmacpos;
+
+/*
+ * The extra standard macros that come from the object format, if
+ * any.
+ */
+static const char **extrastdmac = NULL;
+int any_extrastdmac;
+
+/*
+ * Tokens are allocated in blocks to improve speed
+ */
+#define TOKEN_BLOCKSIZE 4096
+static Token *freeTokens = NULL;
+struct Blocks {
+       Blocks *next;
+       void *chunk;
+};
+
+static Blocks blocks = { NULL, NULL };
+
+/*
+ * Forward declarations.
+ */
+static Token *expand_mmac_params(Token * tline);
+static Token *expand_smacro(Token * tline);
+static Token *expand_id(Token * tline);
+static Context *get_ctx(char *name, int all_contexts);
+static void make_tok_num(Token * tok, long val);
+static void error(int severity, const char *fmt, ...);
+static void *new_Block(size_t size);
+static void delete_Blocks(void);
+static Token *new_Token(Token * next, int type, char *text, int txtlen);
+static Token *delete_Token(Token * t);
+
+/*
+ * Macros for safe checking of token pointers, avoid *(NULL)
+ */
+#define tok_type_(x,t) ((x) && (x)->type == (t))
+#define skip_white_(x) if (tok_type_((x), TOK_WHITESPACE)) (x)=(x)->next
+#define tok_is_(x,v) (tok_type_((x), TOK_OTHER) && !strcmp((x)->text,(v)))
+#define tok_isnt_(x,v) ((x) && ((x)->type!=TOK_OTHER || strcmp((x)->text,(v))))
+
+/* Handle TASM specific directives, which do not contain a % in
+ * front of them. We do it here because I could not find any other
+ * place to do it for the moment, and it is a hack (ideally it would
+ * be nice to be able to use the NASM pre-processor to do it).
+ */
+static char *
+check_tasm_directive(char *line)
+{
+    int i, j, k, m, len;
+    char *p = line, *oldline, oldchar;
+
+    /* Skip whitespace */
+    while (isspace(*p) && *p != 0)
+       p++;
+
+    /* Binary search for the directive name */
+    i = -1;
+    j = elements(tasm_directives);
+    len = 0;
+    while (!isspace(p[len]) && p[len] != 0)
+       len++;
+    if (len)
+    {
+       oldchar = p[len];
+       p[len] = 0;
+       while (j - i > 1)
+       {
+           k = (j + i) / 2;
+           m = nasm_stricmp(p, tasm_directives[k]);
+           if (m == 0)
+           {
+               /* We have found a directive, so jam a % in front of it
+                * so that NASM will then recognise it as one if it's own.
+                */
+               p[len] = oldchar;
+               len = strlen(p);
+               oldline = line;
+               line = nasm_malloc(len + 2);
+               line[0] = '%';
+               if (k == TM_IFDIFI)
+               {
+                   /* NASM does not recognise IFDIFI, so we convert it to
+                    * %ifdef BOGUS. This is not used in NASM comaptible
+                    * code, but does need to parse for the TASM macro
+                    * package.
+                    */
+                   strcpy(line + 1, "ifdef BOGUS");
+               }
+               else
+               {
+                   memcpy(line + 1, p, len + 1);
+               }
+               nasm_free(oldline);
+               return line;
+           }
+           else if (m < 0)
+           {
+               j = k;
+           }
+           else
+               i = k;
+       }
+       p[len] = oldchar;
+    }
+    return line;
+}
+
+/*
+ * The pre-preprocessing stage... This function translates line
+ * number indications as they emerge from GNU cpp (`# lineno "file"
+ * flags') into NASM preprocessor line number indications (`%line
+ * lineno file').
+ */
+static char *
+prepreproc(char *line)
+{
+    int lineno, fnlen;
+    char *fname, *oldline;
+
+    if (line[0] == '#' && line[1] == ' ')
+    {
+       oldline = line;
+       fname = oldline + 2;
+       lineno = atoi(fname);
+       fname += strspn(fname, "0123456789 ");
+       if (*fname == '"')
+           fname++;
+       fnlen = strcspn(fname, "\"");
+       line = nasm_malloc(20 + fnlen);
+       sprintf(line, "%%line %d %.*s", lineno, fnlen, fname);
+       nasm_free(oldline);
+    }
+    if (tasm_compatible_mode)
+       return check_tasm_directive(line);
+    return line;
+}
+
+/*
+ * The hash function for macro lookups. Note that due to some
+ * macros having case-insensitive names, the hash function must be
+ * invariant under case changes. We implement this by applying a
+ * perfectly normal hash function to the uppercase of the string.
+ */
+static int
+hash(char *s)
+{
+    unsigned int h = 0;
+    int i = 0;
+    /*
+     * Powers of three, mod 31.
+     */
+    static const int multipliers[] = {
+       1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10,
+       30, 28, 22, 4, 12, 5, 15, 14, 11, 2, 6, 18, 23, 7, 21
+    };
+
+
+    while (*s)
+    {
+       h += multipliers[i] * (unsigned char) (toupper(*s));
+       s++;
+       if (++i >= elements(multipliers))
+           i = 0;
+    }
+    h %= NHASH;
+    return h;
+}
+
+/*
+ * Free a linked list of tokens.
+ */
+static void
+free_tlist(Token * list)
+{
+    while (list)
+    {
+       list = delete_Token(list);
+    }
+}
+
+/*
+ * Free a linked list of lines.
+ */
+static void
+free_llist(Line * list)
+{
+    Line *l;
+    while (list)
+    {
+       l = list;
+       list = list->next;
+       free_tlist(l->first);
+       nasm_free(l);
+    }
+}
+
+/*
+ * Free an MMacro
+ */
+static void
+free_mmacro(MMacro * m)
+{
+    nasm_free(m->name);
+    free_tlist(m->dlist);
+    nasm_free(m->defaults);
+    free_llist(m->expansion);
+    nasm_free(m);
+}
+
+/*
+ * Pop the context stack.
+ */
+static void
+ctx_pop(void)
+{
+    Context *c = cstk;
+    SMacro *smac, *s;
+
+    cstk = cstk->next;
+    smac = c->localmac;
+    while (smac)
+    {
+       s = smac;
+       smac = smac->next;
+       nasm_free(s->name);
+       free_tlist(s->expansion);
+       nasm_free(s);
+    }
+    nasm_free(c->name);
+    nasm_free(c);
+}
+
+#define BUF_DELTA 512
+/*
+ * Read a line from the top file in istk, handling multiple CR/LFs
+ * at the end of the line read, and handling spurious ^Zs. Will
+ * return lines from the standard macro set if this has not already
+ * been done.
+ */
+static char *
+read_line(void)
+{
+    char *buffer, *p, *q;
+    int bufsize, continued_count;
+
+    if (stdmacpos)
+    {
+       if (*stdmacpos)
+       {
+           char *ret = nasm_strdup(*stdmacpos++);
+           if (!*stdmacpos && any_extrastdmac)
+           {
+               stdmacpos = extrastdmac;
+               any_extrastdmac = FALSE;
+               return ret;
+           }
+           /*
+            * Nasty hack: here we push the contents of `predef' on
+            * to the top-level expansion stack, since this is the
+            * most convenient way to implement the pre-include and
+            * pre-define features.
+            */
+           if (!*stdmacpos)
+           {
+               Line *pd, *l;
+               Token *head, **tail, *t;
+
+               for (pd = predef; pd; pd = pd->next)
+               {
+                   head = NULL;
+                   tail = &head;
+                   for (t = pd->first; t; t = t->next)
+                   {
+                       *tail = new_Token(NULL, t->type, t->text, 0);
+                       tail = &(*tail)->next;
+                   }
+                   l = nasm_malloc(sizeof(Line));
+                   l->next = istk->expansion;
+                   l->first = head;
+                   l->finishes = FALSE;
+                   istk->expansion = l;
+               }
+           }
+           return ret;
+       }
+       else
+       {
+           stdmacpos = NULL;
+       }
+    }
+
+    bufsize = BUF_DELTA;
+    buffer = nasm_malloc(BUF_DELTA);
+    p = buffer;
+    continued_count = 0;
+    while (1)
+    {
+       q = fgets(p, bufsize - (p - buffer), istk->fp);
+       if (!q)
+           break;
+       p += strlen(p);
+       if (p > buffer && p[-1] == '\n')
+       {
+           /* Convert backslash-CRLF line continuation sequences into
+              nothing at all (for DOS and Windows) */
+           if (((p - 2) > buffer) && (p[-3] == '\\') && (p[-2] == '\r')) {
+               p -= 3;
+               *p = 0;
+               continued_count++;
+           }
+           /* Also convert backslash-LF line continuation sequences into
+              nothing at all (for Unix) */
+           else if (((p - 1) > buffer) && (p[-2] == '\\')) {
+               p -= 2;
+               *p = 0;
+               continued_count++;
+           }
+           else {
+               break;
+           }
+       }
+       if (p - buffer > bufsize - 10)
+       {
+           long offset = p - buffer;
+           bufsize += BUF_DELTA;
+           buffer = nasm_realloc(buffer, bufsize);
+           p = buffer + offset;        /* prevent stale-pointer problems */
+       }
+    }
+
+    if (!q && p == buffer)
+    {
+       nasm_free(buffer);
+       return NULL;
+    }
+
+    src_set_linnum(src_get_linnum() + istk->lineinc + (continued_count * istk->lineinc));
+
+    /*
+     * Play safe: remove CRs as well as LFs, if any of either are
+     * present at the end of the line.
+     */
+    while (--p >= buffer && (*p == '\n' || *p == '\r'))
+       *p = '\0';
+
+    /*
+     * Handle spurious ^Z, which may be inserted into source files
+     * by some file transfer utilities.
+     */
+    buffer[strcspn(buffer, "\032")] = '\0';
+
+    list->line(LIST_READ, buffer);
+
+    return buffer;
+}
+
+/*
+ * Tokenise a line of text. This is a very simple process since we
+ * don't need to parse the value out of e.g. numeric tokens: we
+ * simply split one string into many.
+ */
+static Token *
+tokenise(char *line)
+{
+    char *p = line;
+    int type;
+    Token *list = NULL;
+    Token *t, **tail = &list;
+
+    while (*line)
+    {
+       p = line;
+       if (*p == '%')
+       {
+               p++;
+               if ( isdigit(*p) ||
+                       ((*p == '-' || *p == '+') && isdigit(p[1])) ||
+                       ((*p == '+') && (isspace(p[1]) || !p[1])))
+                               {
+                       do
+                       {
+                       p++;
+                       }
+                       while (isdigit(*p));
+                       type = TOK_PREPROC_ID;
+               }
+               else if (*p == '{')
+               {
+                       p++;
+                       while (*p && *p != '}')
+                       {
+                       p[-1] = *p;
+                       p++;
+                       }
+                       p[-1] = '\0';
+                       if (*p)
+                       p++;
+                       type = TOK_PREPROC_ID;
+               }
+               else if (isidchar(*p) ||
+                               ((*p == '!' || *p == '%' || *p == '$') &&
+                                       isidchar(p[1])))
+               {
+                       do
+                       {
+                       p++;
+                       }
+                       while (isidchar(*p));
+                       type = TOK_PREPROC_ID;
+               }
+               else
+               {
+                       type = TOK_OTHER;
+                       if (*p == '%')
+                               p++;
+               }
+       }
+       else if (isidstart(*p) || (*p == '$' && isidstart(p[1])))
+       {
+           type = TOK_ID;
+           p++;
+           while (*p && isidchar(*p))
+               p++;
+       }
+       else if (*p == '\'' || *p == '"')
+       {
+           /*
+            * A string token.
+            */
+           char c = *p;
+           p++;
+           type = TOK_STRING;
+           while (*p && *p != c)
+               p++;
+           if (*p)
+           {
+               p++;
+           }
+           else
+           {
+               error(ERR_WARNING, "unterminated string");
+           }
+       }
+       else if (isnumstart(*p))
+       {
+           /*
+            * A number token.
+            */
+           type = TOK_NUMBER;
+           p++;
+           while (*p && isnumchar(*p))
+               p++;
+       }
+       else if (isspace(*p))
+       {
+           type = TOK_WHITESPACE;
+           p++;
+           while (*p && isspace(*p))
+               p++;
+           /*
+            * Whitespace just before end-of-line is discarded by
+            * pretending it's a comment; whitespace just before a
+            * comment gets lumped into the comment.
+            */
+           if (!*p || *p == ';')
+           {
+               type = TOK_COMMENT;
+               while (*p)
+                   p++;
+           }
+       }
+       else if (*p == ';')
+       {
+           type = TOK_COMMENT;
+           while (*p)
+               p++;
+       }
+       else
+       {
+           /*
+            * Anything else is an operator of some kind. We check
+            * for all the double-character operators (>>, <<, //,
+            * %%, <=, >=, ==, !=, <>, &&, ||, ^^), but anything
+            * else is a single-character operator.
+            */
+           type = TOK_OTHER;
+           if ((p[0] == '>' && p[1] == '>') ||
+                   (p[0] == '<' && p[1] == '<') ||
+                   (p[0] == '/' && p[1] == '/') ||
+                   (p[0] == '<' && p[1] == '=') ||
+                   (p[0] == '>' && p[1] == '=') ||
+                   (p[0] == '=' && p[1] == '=') ||
+                   (p[0] == '!' && p[1] == '=') ||
+                   (p[0] == '<' && p[1] == '>') ||
+                   (p[0] == '&' && p[1] == '&') ||
+                   (p[0] == '|' && p[1] == '|') ||
+                   (p[0] == '^' && p[1] == '^'))
+           {
+               p++;
+           }
+           p++;
+       }
+       if (type != TOK_COMMENT)
+       {
+           *tail = t = new_Token(NULL, type, line, p - line);
+           tail = &t->next;
+       }
+       line = p;
+    }
+    return list;
+}
+
+/*
+ * this function allocates a new managed block of memory and
+ * returns a pointer to the block.  The managed blocks are 
+ * deleted only all at once by the delete_Blocks function.
+ */
+static void *
+new_Block(size_t size)
+{
+       Blocks *b = &blocks;
+       
+       /* first, get to the end of the linked list      */
+       while (b->next)
+               b = b->next;
+       /* now allocate the requested chunk */
+       b->chunk = nasm_malloc(size);
+       
+       /* now allocate a new block for the next request */
+       b->next = nasm_malloc(sizeof(Blocks));
+       /* and initialize the contents of the new block */
+       b->next->next = NULL;
+       b->next->chunk = NULL;
+       return b->chunk;
+}
+
+/*
+ * this function deletes all managed blocks of memory
+ */
+static void
+delete_Blocks(void)
+{
+       Blocks *a,*b = &blocks;
+
+       /* 
+        * keep in mind that the first block, pointed to by blocks
+        * is a static and not dynamically allocated, so we don't 
+        * free it.
+        */
+       while (b)
+       {
+               if (b->chunk)
+                       nasm_free(b->chunk);
+               a = b;
+               b = b->next;
+                if (a != &blocks)
+                       nasm_free(a);
+       }
+}      
+
+/*
+ *  this function creates a new Token and passes a pointer to it 
+ *  back to the caller.  It sets the type and text elements, and
+ *  also the mac and next elements to NULL.
+ */
+static Token *
+new_Token(Token * next, int type, char *text, int txtlen)
+{
+    Token *t;
+    int i;
+
+    if (freeTokens == NULL)
+    {
+       freeTokens = (Token *)new_Block(TOKEN_BLOCKSIZE * sizeof(Token));
+       for (i = 0; i < TOKEN_BLOCKSIZE - 1; i++)
+           freeTokens[i].next = &freeTokens[i + 1];
+       freeTokens[i].next = NULL;
+    }
+    t = freeTokens;
+    freeTokens = t->next;
+    t->next = next;
+    t->mac = NULL;
+    t->type = type;
+    if (type == TOK_WHITESPACE || text == NULL)
+    {
+       t->text = NULL;
+    }
+    else
+    {
+       if (txtlen == 0)
+           txtlen = strlen(text);
+       t->text = nasm_malloc(1 + txtlen);
+       strncpy(t->text, text, txtlen);
+       t->text[txtlen] = '\0';
+    }
+    return t;
+}
+
+static Token *
+delete_Token(Token * t)
+{
+    Token *next = t->next;
+    nasm_free(t->text);
+    t->next = freeTokens;
+    freeTokens = t;
+    return next;
+}
+
+/*
+ * Convert a line of tokens back into text.
+ * If expand_locals is not zero, identifiers of the form "%$*xxx"
+ * will be transformed into ..@ctxnum.xxx
+ */
+static char *
+detoken(Token * tlist, int expand_locals)
+{
+    Token *t;
+    int len;
+    char *line, *p;
+
+    len = 0;
+    for (t = tlist; t; t = t->next)
+    {
+       if (t->type == TOK_PREPROC_ID && t->text[1] == '!')
+       {
+           char *p = getenv(t->text + 2);
+           nasm_free(t->text);
+           if (p)
+               t->text = nasm_strdup(p);
+           else
+               t->text = NULL;
+       }
+       /* Expand local macros here and not during preprocessing */
+       if (expand_locals &&
+               t->type == TOK_PREPROC_ID && t->text &&
+               t->text[0] == '%' && t->text[1] == '$')
+       {
+           Context *ctx = get_ctx(t->text, FALSE);
+           if (ctx)
+           {
+               char buffer[40];
+               char *p, *q = t->text + 2;
+
+               q += strspn(q, "$");
+               sprintf(buffer, "..@%lu.", ctx->number);
+               p = nasm_strcat(buffer, q);
+               nasm_free(t->text);
+               t->text = p;
+           }
+       }
+       if (t->type == TOK_WHITESPACE)
+       {
+           len++;
+       }
+       else if (t->text)
+       {
+           len += strlen(t->text);
+       }
+    }
+    p = line = nasm_malloc(len + 1);
+    for (t = tlist; t; t = t->next)
+    {
+       if (t->type == TOK_WHITESPACE)
+       {
+           *p = ' ';
+               p++;
+               *p = '\0';
+       }
+       else if (t->text)
+       {
+           strcpy(p, t->text);
+           p += strlen(p);
+       }
+    }
+    *p = '\0';
+    return line;
+}
+
+/*
+ * A scanner, suitable for use by the expression evaluator, which
+ * operates on a line of Tokens. Expects a pointer to a pointer to
+ * the first token in the line to be passed in as its private_data
+ * field.
+ */
+static int
+ppscan(void *private_data, struct tokenval *tokval)
+{
+    Token **tlineptr = private_data;
+    Token *tline;
+
+    do
+    {
+       tline = *tlineptr;
+       *tlineptr = tline ? tline->next : NULL;
+    }
+    while (tline && (tline->type == TOK_WHITESPACE ||
+                   tline->type == TOK_COMMENT));
+
+    if (!tline)
+       return tokval->t_type = TOKEN_EOS;
+
+    if (tline->text[0] == '$' && !tline->text[1])
+       return tokval->t_type = TOKEN_HERE;
+    if (tline->text[0] == '$' && tline->text[1] == '$' && !tline->text[2])
+       return tokval->t_type = TOKEN_BASE;
+
+    if (tline->type == TOK_ID)
+    {
+       tokval->t_charptr = tline->text;
+       if (tline->text[0] == '$')
+       {
+           tokval->t_charptr++;
+           return tokval->t_type = TOKEN_ID;
+       }
+
+       /*
+        * This is the only special case we actually need to worry
+        * about in this restricted context.
+        */
+       if (!nasm_stricmp(tline->text, "seg"))
+           return tokval->t_type = TOKEN_SEG;
+
+       return tokval->t_type = TOKEN_ID;
+    }
+
+    if (tline->type == TOK_NUMBER)
+    {
+       int rn_error;
+
+       tokval->t_integer = readnum(tline->text, &rn_error);
+       if (rn_error)
+           return tokval->t_type = TOKEN_ERRNUM;
+       tokval->t_charptr = NULL;
+       return tokval->t_type = TOKEN_NUM;
+    }
+
+    if (tline->type == TOK_STRING)
+    {
+       int rn_warn;
+       char q, *r;
+       int l;
+
+       r = tline->text;
+       q = *r++;
+       l = strlen(r);
+
+       if (l == 0 || r[l - 1] != q)
+           return tokval->t_type = TOKEN_ERRNUM;
+       tokval->t_integer = readstrnum(r, l - 1, &rn_warn);
+       if (rn_warn)
+           error(ERR_WARNING | ERR_PASS1, "character constant too long");
+       tokval->t_charptr = NULL;
+       return tokval->t_type = TOKEN_NUM;
+    }
+
+    if (tline->type == TOK_OTHER)
+    {
+       if (!strcmp(tline->text, "<<"))
+           return tokval->t_type = TOKEN_SHL;
+       if (!strcmp(tline->text, ">>"))
+           return tokval->t_type = TOKEN_SHR;
+       if (!strcmp(tline->text, "//"))
+           return tokval->t_type = TOKEN_SDIV;
+       if (!strcmp(tline->text, "%%"))
+           return tokval->t_type = TOKEN_SMOD;
+       if (!strcmp(tline->text, "=="))
+           return tokval->t_type = TOKEN_EQ;
+       if (!strcmp(tline->text, "<>"))
+           return tokval->t_type = TOKEN_NE;
+       if (!strcmp(tline->text, "!="))
+           return tokval->t_type = TOKEN_NE;
+       if (!strcmp(tline->text, "<="))
+           return tokval->t_type = TOKEN_LE;
+       if (!strcmp(tline->text, ">="))
+           return tokval->t_type = TOKEN_GE;
+       if (!strcmp(tline->text, "&&"))
+           return tokval->t_type = TOKEN_DBL_AND;
+       if (!strcmp(tline->text, "^^"))
+           return tokval->t_type = TOKEN_DBL_XOR;
+       if (!strcmp(tline->text, "||"))
+           return tokval->t_type = TOKEN_DBL_OR;
+    }
+
+    /*
+     * We have no other options: just return the first character of
+     * the token text.
+     */
+    return tokval->t_type = tline->text[0];
+}
+
+/*
+ * Compare a string to the name of an existing macro; this is a
+ * simple wrapper which calls either strcmp or nasm_stricmp
+ * depending on the value of the `casesense' parameter.
+ */
+static int
+mstrcmp(char *p, char *q, int casesense)
+{
+    return casesense ? strcmp(p, q) : nasm_stricmp(p, q);
+}
+
+/*
+ * Return the Context structure associated with a %$ token. Return
+ * NULL, having _already_ reported an error condition, if the
+ * context stack isn't deep enough for the supplied number of $
+ * signs.
+ * If all_contexts == TRUE, contexts that enclose current are
+ * also scanned for such smacro, until it is found; if not -
+ * only the context that directly results from the number of $'s
+ * in variable's name.
+ */
+static Context *
+get_ctx(char *name, int all_contexts)
+{
+    Context *ctx;
+    SMacro *m;
+    int i;
+
+    if (!name || name[0] != '%' || name[1] != '$')
+       return NULL;
+
+    if (!cstk)
+    {
+       error(ERR_NONFATAL, "`%s': context stack is empty", name);
+       return NULL;
+    }
+
+    for (i = strspn(name + 2, "$"), ctx = cstk; (i > 0) && ctx; i--)
+    {
+       ctx = ctx->next;
+/*        i--;  Lino - 02/25/02 */
+    }
+    if (!ctx)
+    {
+       error(ERR_NONFATAL, "`%s': context stack is only"
+               " %d level%s deep", name, i - 1, (i == 2 ? "" : "s"));
+       return NULL;
+    }
+    if (!all_contexts)
+       return ctx;
+
+    do
+    {
+       /* Search for this smacro in found context */
+       m = ctx->localmac;
+       while (m)
+       {
+           if (!mstrcmp(m->name, name, m->casesense))
+               return ctx;
+           m = m->next;
+       }
+       ctx = ctx->next;
+    }
+    while (ctx);
+    return NULL;
+}
+
+/* Add a slash to the end of a path if it is missing. We use the
+ * forward slash to make it compatible with Unix systems.
+ */
+static void
+backslash(char *s)
+{
+    int pos = strlen(s);
+    if (s[pos - 1] != '\\' && s[pos - 1] != '/')
+    {
+       s[pos] = '/';
+       s[pos + 1] = '\0';
+    }
+}
+
+/*
+ * Open an include file. This routine must always return a valid
+ * file pointer if it returns - it's responsible for throwing an
+ * ERR_FATAL and bombing out completely if not. It should also try
+ * the include path one by one until it finds the file or reaches
+ * the end of the path.
+ */
+static FILE *
+inc_fopen(char *file)
+{
+    FILE *fp;
+    char *prefix = "", *combine;
+    IncPath *ip = ipath;
+    static int namelen = 0;
+    int len = strlen(file);
+
+    while (1)
+    {
+       combine = nasm_malloc(strlen(prefix) + 1 + len + 1);
+       strcpy(combine, prefix);
+       if (prefix[0] != 0)
+           backslash(combine);
+       strcat(combine, file);
+       fp = fopen(combine, "r");
+       if (pass == 0 && fp)
+       {
+           namelen += strlen(combine) + 1;
+           if (namelen > 62)
+           {
+               printf(" \\\n  ");
+               namelen = 2;
+           }
+           printf(" %s", combine);
+       }
+       nasm_free(combine);
+       if (fp)
+           return fp;
+       if (!ip)
+           break;
+       prefix = ip->path;
+       ip = ip->next;
+    }
+
+    error(ERR_FATAL, "unable to open include file `%s'", file);
+    return NULL;               /* never reached - placate compilers */
+}
+
+/*
+ * Determine if we should warn on defining a single-line macro of
+ * name `name', with `nparam' parameters. If nparam is 0 or -1, will
+ * return TRUE if _any_ single-line macro of that name is defined.
+ * Otherwise, will return TRUE if a single-line macro with either
+ * `nparam' or no parameters is defined.
+ *
+ * If a macro with precisely the right number of parameters is
+ * defined, or nparam is -1, the address of the definition structure
+ * will be returned in `defn'; otherwise NULL will be returned. If `defn'
+ * is NULL, no action will be taken regarding its contents, and no
+ * error will occur.
+ *
+ * Note that this is also called with nparam zero to resolve
+ * `ifdef'.
+ *
+ * If you already know which context macro belongs to, you can pass
+ * the context pointer as first parameter; if you won't but name begins
+ * with %$ the context will be automatically computed. If all_contexts
+ * is true, macro will be searched in outer contexts as well.
+ */
+static int
+smacro_defined(Context * ctx, char *name, int nparam, SMacro ** defn,
+       int nocase)
+{
+    SMacro *m;
+
+    if (ctx)
+       m = ctx->localmac;
+    else if (name[0] == '%' && name[1] == '$')
+    {
+       if (cstk)
+           ctx = get_ctx(name, FALSE);
+       if (!ctx)
+           return FALSE;       /* got to return _something_ */
+       m = ctx->localmac;
+    }
+    else
+       m = smacros[hash(name)];
+
+    while (m)
+    {
+       if (!mstrcmp(m->name, name, m->casesense && nocase) &&
+               (nparam <= 0 || m->nparam == 0 || nparam == m->nparam))
+       {
+           if (defn)
+           {
+               if (nparam == m->nparam || nparam == -1)
+                   *defn = m;
+               else
+                   *defn = NULL;
+           }
+           return TRUE;
+       }
+       m = m->next;
+    }
+
+    return FALSE;
+}
+
+/*
+ * Count and mark off the parameters in a multi-line macro call.
+ * This is called both from within the multi-line macro expansion
+ * code, and also to mark off the default parameters when provided
+ * in a %macro definition line.
+ */
+static void
+count_mmac_params(Token * t, int *nparam, Token *** params)
+{
+    int paramsize, brace;
+
+    *nparam = paramsize = 0;
+    *params = NULL;
+    while (t)
+    {
+       if (*nparam >= paramsize)
+       {
+           paramsize += PARAM_DELTA;
+           *params = nasm_realloc(*params, sizeof(**params) * paramsize);
+       }
+       skip_white_(t);
+       brace = FALSE;
+       if (tok_is_(t, "{"))
+           brace = TRUE;
+       (*params)[(*nparam)++] = t;
+       while (tok_isnt_(t, brace ? "}" : ","))
+           t = t->next;
+       if (t)
+       {                       /* got a comma/brace */
+           t = t->next;
+           if (brace)
+           {
+               /*
+                * Now we've found the closing brace, look further
+                * for the comma.
+                */
+               skip_white_(t);
+               if (tok_isnt_(t, ","))
+               {
+                   error(ERR_NONFATAL,
+                           "braces do not enclose all of macro parameter");
+                   while (tok_isnt_(t, ","))
+                       t = t->next;
+               }
+               if (t)
+                   t = t->next;        /* eat the comma */
+           }
+       }
+    }
+}
+
+/*
+ * Determine whether one of the various `if' conditions is true or
+ * not.
+ *
+ * We must free the tline we get passed.
+ */
+static int
+if_condition(Token * tline, int i)
+{
+    int j, casesense;
+    Token *t, *tt, **tptr, *origline;
+    struct tokenval tokval;
+    expr *evalresult;
+
+    origline = tline;
+
+    switch (i)
+    {
+       case PP_IFCTX:
+       case PP_ELIFCTX:
+       case PP_IFNCTX:
+       case PP_ELIFNCTX:
+           j = FALSE;          /* have we matched yet? */
+           while (cstk && tline)
+           {
+               skip_white_(tline);
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL,
+                           "`%s' expects context identifiers",
+                           directives[i]);
+                   free_tlist(origline);
+                   return -1;
+               }
+               if (!nasm_stricmp(tline->text, cstk->name))
+                   j = TRUE;
+               tline = tline->next;
+           }
+           if (i == PP_IFNCTX || i == PP_ELIFNCTX)
+               j = !j;
+           free_tlist(origline);
+           return j;
+
+       case PP_IFDEF:
+       case PP_ELIFDEF:
+       case PP_IFNDEF:
+       case PP_ELIFNDEF:
+           j = FALSE;          /* have we matched yet? */
+           while (tline)
+           {
+               skip_white_(tline);
+               if (!tline || (tline->type != TOK_ID &&
+                               (tline->type != TOK_PREPROC_ID ||
+                                       tline->text[1] != '$')))
+               {
+                   error(ERR_NONFATAL,
+                         "`%s' expects macro identifiers",
+                         directives[i]);
+                   free_tlist(origline);
+                   return -1;
+               }
+               if (smacro_defined(NULL, tline->text, 0, NULL, 1))
+                   j = TRUE;
+               tline = tline->next;
+           }
+           if (i == PP_IFNDEF || i == PP_ELIFNDEF)
+               j = !j;
+           free_tlist(origline);
+           return j;
+
+       case PP_IFIDN:
+       case PP_ELIFIDN:
+       case PP_IFNIDN:
+       case PP_ELIFNIDN:
+       case PP_IFIDNI:
+       case PP_ELIFIDNI:
+       case PP_IFNIDNI:
+       case PP_ELIFNIDNI:
+           tline = expand_smacro(tline);
+           t = tt = tline;
+           while (tok_isnt_(tt, ","))
+               tt = tt->next;
+           if (!tt)
+           {
+               error(ERR_NONFATAL,
+                       "`%s' expects two comma-separated arguments",
+                       directives[i]);
+               free_tlist(tline);
+               return -1;
+           }
+           tt = tt->next;
+           casesense = (i == PP_IFIDN || i == PP_ELIFIDN ||
+                   i == PP_IFNIDN || i == PP_ELIFNIDN);
+           j = TRUE;           /* assume equality unless proved not */
+           while ((t->type != TOK_OTHER || strcmp(t->text, ",")) && tt)
+           {
+               if (tt->type == TOK_OTHER && !strcmp(tt->text, ","))
+               {
+                   error(ERR_NONFATAL, "`%s': more than one comma on line",
+                           directives[i]);
+                   free_tlist(tline);
+                   return -1;
+               }
+               if (t->type == TOK_WHITESPACE)
+               {
+                   t = t->next;
+                   continue;
+               }
+               else if (tt->type == TOK_WHITESPACE)
+               {
+                   tt = tt->next;
+                   continue;
+               }
+               else if (tt->type != t->type ||
+                       mstrcmp(tt->text, t->text, casesense))
+               {
+                   j = FALSE;  /* found mismatching tokens */
+                   break;
+               }
+               else
+               {
+                   t = t->next;
+                   tt = tt->next;
+                   continue;
+               }
+           }
+           if ((t->type != TOK_OTHER || strcmp(t->text, ",")) || tt)
+               j = FALSE;      /* trailing gunk on one end or other */
+           if (i == PP_IFNIDN || i == PP_ELIFNIDN ||
+                   i == PP_IFNIDNI || i == PP_ELIFNIDNI)
+               j = !j;
+           free_tlist(tline);
+           return j;
+
+        case PP_IFMACRO:
+        case PP_ELIFMACRO:
+        case PP_IFNMACRO:
+        case PP_ELIFNMACRO:
+       {
+           int found = 0;
+           MMacro searching, *mmac;
+
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tok_type_(tline, TOK_ID))
+           {
+               error(ERR_NONFATAL,
+                       "`%s' expects a macro name",
+                     directives[i]);
+               return -1;
+           }
+           searching.name = nasm_strdup(tline->text);
+           searching.casesense = (i == PP_MACRO);
+           searching.plus = FALSE;
+           searching.nolist = FALSE;
+           searching.in_progress = FALSE;
+           searching.rep_nest = NULL;
+           searching.nparam_min = 0;
+           searching.nparam_max = INT_MAX;
+           tline = expand_smacro(tline->next);
+           skip_white_(tline);
+           if (!tline)
+           {
+           } else if (!tok_type_(tline, TOK_NUMBER))
+           {
+               error(ERR_NONFATAL,
+                     "`%s' expects a parameter count or nothing",
+                     directives[i]);
+           }
+           else
+           {
+               searching.nparam_min = searching.nparam_max =
+                       readnum(tline->text, &j);
+               if (j)
+                   error(ERR_NONFATAL,
+                         "unable to parse parameter count `%s'",
+                         tline->text);
+           }
+           if (tline && tok_is_(tline->next, "-"))
+           {
+               tline = tline->next->next;
+               if (tok_is_(tline, "*"))
+                   searching.nparam_max = INT_MAX;
+               else if (!tok_type_(tline, TOK_NUMBER))
+                   error(ERR_NONFATAL,
+                         "`%s' expects a parameter count after `-'",
+                         directives[i]);
+               else
+               {
+                   searching.nparam_max = readnum(tline->text, &j);
+                   if (j)
+                       error(ERR_NONFATAL,
+                               "unable to parse parameter count `%s'",
+                               tline->text);
+                   if (searching.nparam_min > searching.nparam_max)
+                       error(ERR_NONFATAL,
+                               "minimum parameter count exceeds maximum");
+               }
+           }
+           if (tline && tok_is_(tline->next, "+"))
+           {
+               tline = tline->next;
+               searching.plus = TRUE;
+           }
+           mmac = mmacros[hash(searching.name)];
+           while (mmac)
+           {
+               if (!strcmp(mmac->name, searching.name) &&
+                       (mmac->nparam_min <= searching.nparam_max
+                               || searching.plus)
+                       && (searching.nparam_min <= mmac->nparam_max
+                               || mmac->plus))
+               {
+                   found = TRUE;
+                   break;
+               }
+               mmac = mmac->next;
+           }
+           nasm_free(searching.name);
+           free_tlist(origline);
+           if (i == PP_IFNMACRO || i == PP_ELIFNMACRO)
+               found = !found;
+           return found;
+       }
+
+       case PP_IFID:
+       case PP_ELIFID:
+       case PP_IFNID:
+       case PP_ELIFNID:
+       case PP_IFNUM:
+       case PP_ELIFNUM:
+       case PP_IFNNUM:
+       case PP_ELIFNNUM:
+       case PP_IFSTR:
+       case PP_ELIFSTR:
+       case PP_IFNSTR:
+       case PP_ELIFNSTR:
+           tline = expand_smacro(tline);
+           t = tline;
+           while (tok_type_(t, TOK_WHITESPACE))
+               t = t->next;
+           j = FALSE;          /* placate optimiser */
+           if (t)
+               switch (i)
+               {
+                   case PP_IFID:
+                   case PP_ELIFID:
+                   case PP_IFNID:
+                   case PP_ELIFNID:
+                       j = (t->type == TOK_ID);
+                       break;
+                   case PP_IFNUM:
+                   case PP_ELIFNUM:
+                   case PP_IFNNUM:
+                   case PP_ELIFNNUM:
+                       j = (t->type == TOK_NUMBER);
+                       break;
+                   case PP_IFSTR:
+                   case PP_ELIFSTR:
+                   case PP_IFNSTR:
+                   case PP_ELIFNSTR:
+                       j = (t->type == TOK_STRING);
+                       break;
+               }
+           if (i == PP_IFNID || i == PP_ELIFNID ||
+                   i == PP_IFNNUM || i == PP_ELIFNNUM ||
+                   i == PP_IFNSTR || i == PP_ELIFNSTR)
+               j = !j;
+           free_tlist(tline);
+           return j;
+
+       case PP_IF:
+       case PP_ELIF:
+           t = tline = expand_smacro(tline);
+           tptr = &t;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult = evaluate(ppscan, tptr, &tokval,
+                   NULL, pass | CRITICAL, error, NULL);
+           free_tlist(tline);
+           if (!evalresult)
+               return -1;
+           if (tokval.t_type)
+               error(ERR_WARNING,
+                       "trailing garbage after expression ignored");
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL,
+                       "non-constant value given to `%s'", directives[i]);
+               return -1;
+           }
+           return reloc_value(evalresult) != 0;
+
+       default:
+           error(ERR_FATAL,
+                   "preprocessor directive `%s' not yet implemented",
+                   directives[i]);
+           free_tlist(origline);
+           return -1;          /* yeah, right */
+    }
+}
+
+/*
+ * Expand macros in a string. Used in %error and %include directives.
+ * First tokenise the string, apply "expand_smacro" and then de-tokenise back.
+ * The returned variable should ALWAYS be freed after usage.
+ */
+void
+expand_macros_in_string(char **p)
+{
+    Token *line = tokenise(*p);
+    line = expand_smacro(line);
+    *p = detoken(line, FALSE);
+}
+
+/**
+ * find and process preprocessor directive in passed line
+ * Find out if a line contains a preprocessor directive, and deal
+ * with it if so.
+ * 
+ * If a directive _is_ found, it is the responsibility of this routine
+ * (and not the caller) to free_tlist() the line.
+ *
+ * @param tline a pointer to the current tokeninzed line linked list
+ * @return DIRECTIVE_FOUND or NO_DIRECTIVE_FOUND
+ * 
+ */
+static int
+do_directive(Token * tline)
+{
+    int i, j, k, m, nparam, nolist;
+    int offset;
+    char *p, *mname;
+    Include *inc;
+    Context *ctx;
+    Cond *cond;
+    SMacro *smac, **smhead;
+    MMacro *mmac;
+    Token *t, *tt, *param_start, *macro_start, *last, **tptr, *origline;
+    Line *l;
+    struct tokenval tokval;
+    expr *evalresult;
+    MMacro *tmp_defining;      /* Used when manipulating rep_nest */
+
+    origline = tline;
+
+    skip_white_(tline);
+    if (!tok_type_(tline, TOK_PREPROC_ID) ||
+           (tline->text[1] == '%' || tline->text[1] == '$'
+                   || tline->text[1] == '!'))
+       return NO_DIRECTIVE_FOUND;
+
+    i = -1;
+    j = elements(directives);
+    while (j - i > 1)
+    {
+       k = (j + i) / 2;
+       m = nasm_stricmp(tline->text, directives[k]);
+       if (m == 0) {
+               if (tasm_compatible_mode) {
+               i = k;
+               j = -2;
+               } else if (k != PP_ARG && k != PP_LOCAL && k != PP_STACKSIZE) {
+                   i = k;
+               j = -2;
+               }
+           break;
+       }
+       else if (m < 0) {
+           j = k;
+       }
+       else
+           i = k;
+    }
+
+    /*
+     * If we're in a non-emitting branch of a condition construct,
+     * or walking to the end of an already terminated %rep block,
+     * we should ignore all directives except for condition
+     * directives.
+     */
+    if (((istk->conds && !emitting(istk->conds->state)) ||
+        (istk->mstk && !istk->mstk->in_progress)) &&
+       !is_condition(i))
+    {
+       return NO_DIRECTIVE_FOUND;
+    }
+
+    /*
+     * If we're defining a macro or reading a %rep block, we should
+     * ignore all directives except for %macro/%imacro (which
+     * generate an error), %endm/%endmacro, and (only if we're in a
+     * %rep block) %endrep. If we're in a %rep block, another %rep
+     * causes an error, so should be let through.
+     */
+    if (defining && i != PP_MACRO && i != PP_IMACRO &&
+           i != PP_ENDMACRO && i != PP_ENDM &&
+           (defining->name || (i != PP_ENDREP && i != PP_REP)))
+    {
+       return NO_DIRECTIVE_FOUND;
+    }
+
+    if (j != -2)
+    {
+       error(ERR_NONFATAL, "unknown preprocessor directive `%s'",
+               tline->text);
+       return NO_DIRECTIVE_FOUND;              /* didn't get it */
+    }
+
+    switch (i)
+    {
+       case PP_STACKSIZE:
+           /* Directive to tell NASM what the default stack size is. The
+            * default is for a 16-bit stack, and this can be overriden with
+            * %stacksize large.
+            * the following form:
+            *
+            *      ARG arg1:WORD, arg2:DWORD, arg4:QWORD
+            */
+           tline = tline->next;
+           if (tline && tline->type == TOK_WHITESPACE)
+               tline = tline->next;
+           if (!tline || tline->type != TOK_ID)
+           {
+               error(ERR_NONFATAL, "`%%stacksize' missing size parameter");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           if (nasm_stricmp(tline->text, "flat") == 0)
+           {
+               /* All subsequent ARG directives are for a 32-bit stack */
+               StackSize = 4;
+               StackPointer = "ebp";
+               ArgOffset = 8;
+               LocalOffset = 4;
+           }
+           else if (nasm_stricmp(tline->text, "large") == 0)
+           {
+               /* All subsequent ARG directives are for a 16-bit stack,
+                * far function call.
+                */
+               StackSize = 2;
+               StackPointer = "bp";
+               ArgOffset = 4;
+               LocalOffset = 2;
+           }
+           else if (nasm_stricmp(tline->text, "small") == 0)
+           {
+               /* All subsequent ARG directives are for a 16-bit stack,
+                  * far function call. We don't support near functions.
+                */
+               StackSize = 2;
+               StackPointer = "bp";
+               ArgOffset = 6;
+               LocalOffset = 2;
+           }
+           else
+           {
+               error(ERR_NONFATAL, "`%%stacksize' invalid size type");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_ARG:
+           /* TASM like ARG directive to define arguments to functions, in
+            * the following form:
+            *
+            *      ARG arg1:WORD, arg2:DWORD, arg4:QWORD
+            */
+           offset = ArgOffset;
+           do
+           {
+               char *arg, directive[256];
+               int size = StackSize;
+
+               /* Find the argument name */
+               tline = tline->next;
+               if (tline && tline->type == TOK_WHITESPACE)
+                   tline = tline->next;
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL, "`%%arg' missing argument parameter");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               arg = tline->text;
+
+               /* Find the argument size type */
+               tline = tline->next;
+               if (!tline || tline->type != TOK_OTHER
+                       || tline->text[0] != ':')
+               {
+                   error(ERR_NONFATAL,
+                           "Syntax error processing `%%arg' directive");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               tline = tline->next;
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL,
+                           "`%%arg' missing size type parameter");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+
+               /* Allow macro expansion of type parameter */
+               tt = tokenise(tline->text);
+               tt = expand_smacro(tt);
+               if (nasm_stricmp(tt->text, "byte") == 0)
+               {
+                   size = MAX(StackSize, 1);
+               }
+               else if (nasm_stricmp(tt->text, "word") == 0)
+               {
+                   size = MAX(StackSize, 2);
+               }
+               else if (nasm_stricmp(tt->text, "dword") == 0)
+               {
+                   size = MAX(StackSize, 4);
+               }
+               else if (nasm_stricmp(tt->text, "qword") == 0)
+               {
+                   size = MAX(StackSize, 8);
+               }
+               else if (nasm_stricmp(tt->text, "tword") == 0)
+               {
+                   size = MAX(StackSize, 10);
+               }
+               else
+               {
+                   error(ERR_NONFATAL,
+                           "Invalid size type for `%%arg' missing directive");
+                   free_tlist(tt);
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               free_tlist(tt);
+
+               /* Now define the macro for the argument */
+               sprintf(directive, "%%define %s (%s+%d)", arg, StackPointer,
+                       offset);
+               do_directive(tokenise(directive));
+               offset += size;
+
+               /* Move to the next argument in the list */
+               tline = tline->next;
+               if (tline && tline->type == TOK_WHITESPACE)
+                   tline = tline->next;
+           }
+           while (tline && tline->type == TOK_OTHER
+                   && tline->text[0] == ',');
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_LOCAL:
+           /* TASM like LOCAL directive to define local variables for a
+            * function, in the following form:
+            *
+            *      LOCAL local1:WORD, local2:DWORD, local4:QWORD = LocalSize
+            *
+            * The '= LocalSize' at the end is ignored by NASM, but is
+            * required by TASM to define the local parameter size (and used
+            * by the TASM macro package).
+            */
+           offset = LocalOffset;
+           do
+           {
+               char *local, directive[256];
+               int size = StackSize;
+
+               /* Find the argument name */
+               tline = tline->next;
+               if (tline && tline->type == TOK_WHITESPACE)
+                   tline = tline->next;
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL,
+                           "`%%local' missing argument parameter");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               local = tline->text;
+
+               /* Find the argument size type */
+               tline = tline->next;
+               if (!tline || tline->type != TOK_OTHER
+                       || tline->text[0] != ':')
+               {
+                   error(ERR_NONFATAL,
+                           "Syntax error processing `%%local' directive");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               tline = tline->next;
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL,
+                           "`%%local' missing size type parameter");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+
+               /* Allow macro expansion of type parameter */
+               tt = tokenise(tline->text);
+               tt = expand_smacro(tt);
+               if (nasm_stricmp(tt->text, "byte") == 0)
+               {
+                   size = MAX(StackSize, 1);
+               }
+               else if (nasm_stricmp(tt->text, "word") == 0)
+               {
+                   size = MAX(StackSize, 2);
+               }
+               else if (nasm_stricmp(tt->text, "dword") == 0)
+               {
+                   size = MAX(StackSize, 4);
+               }
+               else if (nasm_stricmp(tt->text, "qword") == 0)
+               {
+                   size = MAX(StackSize, 8);
+               }
+               else if (nasm_stricmp(tt->text, "tword") == 0)
+               {
+                   size = MAX(StackSize, 10);
+               }
+               else
+               {
+                   error(ERR_NONFATAL,
+                           "Invalid size type for `%%local' missing directive");
+                   free_tlist(tt);
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               free_tlist(tt);
+
+               /* Now define the macro for the argument */
+               sprintf(directive, "%%define %s (%s-%d)", local, StackPointer,
+                       offset);
+               do_directive(tokenise(directive));
+               offset += size;
+
+               /* Now define the assign to setup the enter_c macro correctly */
+               sprintf(directive, "%%assign %%$localsize %%$localsize+%d",
+                       size);
+               do_directive(tokenise(directive));
+
+               /* Move to the next argument in the list */
+               tline = tline->next;
+               if (tline && tline->type == TOK_WHITESPACE)
+                   tline = tline->next;
+           }
+           while (tline && tline->type == TOK_OTHER
+                   && tline->text[0] == ',');
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_CLEAR:
+           if (tline->next)
+               error(ERR_WARNING,
+                       "trailing garbage after `%%clear' ignored");
+           for (j = 0; j < NHASH; j++)
+           {
+               while (mmacros[j])
+               {
+                   MMacro *m = mmacros[j];
+                   mmacros[j] = m->next;
+                   free_mmacro(m);
+               }
+               while (smacros[j])
+               {
+                   SMacro *s = smacros[j];
+                   smacros[j] = smacros[j]->next;
+                   nasm_free(s->name);
+                   free_tlist(s->expansion);
+                   nasm_free(s);
+               }
+           }
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_INCLUDE:
+           tline = tline->next;
+           skip_white_(tline);
+           if (!tline || (tline->type != TOK_STRING &&
+                           tline->type != TOK_INTERNAL_STRING))
+           {
+               error(ERR_NONFATAL, "`%%include' expects a file name");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND; /* but we did _something_ */
+           }
+           if (tline->next)
+               error(ERR_WARNING,
+                       "trailing garbage after `%%include' ignored");
+           if (tline->type != TOK_INTERNAL_STRING)
+           {
+               p = tline->text + 1;    /* point past the quote to the name */
+               p[strlen(p) - 1] = '\0';        /* remove the trailing quote */
+           }
+           else
+               p = tline->text;        /* internal_string is easier */
+           expand_macros_in_string(&p);
+           inc = nasm_malloc(sizeof(Include));
+           inc->next = istk;
+           inc->conds = NULL;
+           inc->fp = inc_fopen(p);
+           inc->fname = src_set_fname(p);
+           inc->lineno = src_set_linnum(0);
+           inc->lineinc = 1;
+           inc->expansion = NULL;
+           inc->mstk = NULL;
+           istk = inc;
+           list->uplevel(LIST_INCLUDE);
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_PUSH:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tok_type_(tline, TOK_ID))
+           {
+               error(ERR_NONFATAL, "`%%push' expects a context identifier");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND; /* but we did _something_ */
+           }
+           if (tline->next)
+               error(ERR_WARNING, "trailing garbage after `%%push' ignored");
+           ctx = nasm_malloc(sizeof(Context));
+           ctx->next = cstk;
+           ctx->localmac = NULL;
+           ctx->name = nasm_strdup(tline->text);
+           ctx->number = unique++;
+           cstk = ctx;
+           free_tlist(origline);
+           break;
+
+       case PP_REPL:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tok_type_(tline, TOK_ID))
+           {
+               error(ERR_NONFATAL, "`%%repl' expects a context identifier");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND; /* but we did _something_ */
+           }
+           if (tline->next)
+               error(ERR_WARNING, "trailing garbage after `%%repl' ignored");
+           if (!cstk)
+               error(ERR_NONFATAL, "`%%repl': context stack is empty");
+           else
+           {
+               nasm_free(cstk->name);
+               cstk->name = nasm_strdup(tline->text);
+           }
+           free_tlist(origline);
+           break;
+
+       case PP_POP:
+           if (tline->next)
+               error(ERR_WARNING, "trailing garbage after `%%pop' ignored");
+           if (!cstk)
+               error(ERR_NONFATAL,
+                       "`%%pop': context stack is already empty");
+           else
+               ctx_pop();
+           free_tlist(origline);
+           break;
+
+       case PP_ERROR:
+           tline->next = expand_smacro(tline->next);
+           tline = tline->next;
+           skip_white_(tline);
+           if (tok_type_(tline, TOK_STRING))
+           {
+               p = tline->text + 1;    /* point past the quote to the name */
+               p[strlen(p) - 1] = '\0';        /* remove the trailing quote */
+               expand_macros_in_string(&p);
+               error(ERR_NONFATAL, "%s", p);
+               nasm_free(p);
+           }
+           else
+           {
+               p = detoken(tline, FALSE);
+               error(ERR_WARNING, "%s", p);
+               nasm_free(p);
+           }
+           free_tlist(origline);
+           break;
+
+       case PP_IF:
+       case PP_IFCTX:
+       case PP_IFDEF:
+       case PP_IFID:
+       case PP_IFIDN:
+       case PP_IFIDNI:
+        case PP_IFMACRO:
+       case PP_IFNCTX:
+       case PP_IFNDEF:
+       case PP_IFNID:
+       case PP_IFNIDN:
+       case PP_IFNIDNI:
+        case PP_IFNMACRO:
+       case PP_IFNNUM:
+       case PP_IFNSTR:
+       case PP_IFNUM:
+       case PP_IFSTR:
+           if (istk->conds && !emitting(istk->conds->state))
+               j = COND_NEVER;
+           else
+           {
+               j = if_condition(tline->next, i);
+               tline->next = NULL;     /* it got freed */
+               free_tlist(origline);
+               j = j < 0 ? COND_NEVER : j ? COND_IF_TRUE : COND_IF_FALSE;
+           }
+           cond = nasm_malloc(sizeof(Cond));
+           cond->next = istk->conds;
+           cond->state = j;
+           istk->conds = cond;
+           return DIRECTIVE_FOUND;
+
+       case PP_ELIF:
+       case PP_ELIFCTX:
+       case PP_ELIFDEF:
+       case PP_ELIFID:
+       case PP_ELIFIDN:
+       case PP_ELIFIDNI:
+        case PP_ELIFMACRO:
+       case PP_ELIFNCTX:
+       case PP_ELIFNDEF:
+       case PP_ELIFNID:
+       case PP_ELIFNIDN:
+       case PP_ELIFNIDNI:
+        case PP_ELIFNMACRO:
+       case PP_ELIFNNUM:
+       case PP_ELIFNSTR:
+       case PP_ELIFNUM:
+       case PP_ELIFSTR:
+           if (!istk->conds)
+               error(ERR_FATAL, "`%s': no matching `%%if'", directives[i]);
+           if (emitting(istk->conds->state)
+                   || istk->conds->state == COND_NEVER)
+               istk->conds->state = COND_NEVER;
+           else
+           {
+               /*
+                * IMPORTANT: In the case of %if, we will already have
+                * called expand_mmac_params(); however, if we're
+                * processing an %elif we must have been in a
+                * non-emitting mode, which would have inhibited
+                * the normal invocation of expand_mmac_params().  Therefore,
+                * we have to do it explicitly here.
+                */
+                j = if_condition(expand_mmac_params(tline->next), i);
+                tline->next = NULL; /* it got freed */
+               free_tlist(origline);
+               istk->conds->state =
+                       j < 0 ? COND_NEVER : j ? COND_IF_TRUE : COND_IF_FALSE;
+           }
+           return DIRECTIVE_FOUND;
+
+       case PP_ELSE:
+           if (tline->next)
+               error(ERR_WARNING, "trailing garbage after `%%else' ignored");
+           if (!istk->conds)
+               error(ERR_FATAL, "`%%else': no matching `%%if'");
+           if (emitting(istk->conds->state)
+                   || istk->conds->state == COND_NEVER)
+               istk->conds->state = COND_ELSE_FALSE;
+           else
+               istk->conds->state = COND_ELSE_TRUE;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_ENDIF:
+           if (tline->next)
+               error(ERR_WARNING,
+                       "trailing garbage after `%%endif' ignored");
+           if (!istk->conds)
+               error(ERR_FATAL, "`%%endif': no matching `%%if'");
+           cond = istk->conds;
+           istk->conds = cond->next;
+           nasm_free(cond);
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_MACRO:
+       case PP_IMACRO:
+           if (defining)
+               error(ERR_FATAL,
+                       "`%%%smacro': already defining a macro",
+                       (i == PP_IMACRO ? "i" : ""));
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tok_type_(tline, TOK_ID))
+           {
+               error(ERR_NONFATAL,
+                       "`%%%smacro' expects a macro name",
+                       (i == PP_IMACRO ? "i" : ""));
+               return DIRECTIVE_FOUND;
+           }
+           defining = nasm_malloc(sizeof(MMacro));
+           defining->name = nasm_strdup(tline->text);
+           defining->casesense = (i == PP_MACRO);
+           defining->plus = FALSE;
+           defining->nolist = FALSE;
+           defining->in_progress = FALSE;
+           defining->rep_nest = NULL;
+           tline = expand_smacro(tline->next);
+           skip_white_(tline);
+           if (!tok_type_(tline, TOK_NUMBER))
+           {
+               error(ERR_NONFATAL,
+                       "`%%%smacro' expects a parameter count",
+                       (i == PP_IMACRO ? "i" : ""));
+               defining->nparam_min = defining->nparam_max = 0;
+           }
+           else
+           {
+               defining->nparam_min = defining->nparam_max =
+                       readnum(tline->text, &j);
+               if (j)
+                   error(ERR_NONFATAL,
+                           "unable to parse parameter count `%s'",
+                           tline->text);
+           }
+           if (tline && tok_is_(tline->next, "-"))
+           {
+               tline = tline->next->next;
+               if (tok_is_(tline, "*"))
+                   defining->nparam_max = INT_MAX;
+               else if (!tok_type_(tline, TOK_NUMBER))
+                   error(ERR_NONFATAL,
+                           "`%%%smacro' expects a parameter count after `-'",
+                           (i == PP_IMACRO ? "i" : ""));
+               else
+               {
+                   defining->nparam_max = readnum(tline->text, &j);
+                   if (j)
+                       error(ERR_NONFATAL,
+                               "unable to parse parameter count `%s'",
+                               tline->text);
+                   if (defining->nparam_min > defining->nparam_max)
+                       error(ERR_NONFATAL,
+                               "minimum parameter count exceeds maximum");
+               }
+           }
+           if (tline && tok_is_(tline->next, "+"))
+           {
+               tline = tline->next;
+               defining->plus = TRUE;
+           }
+           if (tline && tok_type_(tline->next, TOK_ID) &&
+                   !nasm_stricmp(tline->next->text, ".nolist"))
+           {
+               tline = tline->next;
+               defining->nolist = TRUE;
+           }
+           mmac = mmacros[hash(defining->name)];
+           while (mmac)
+           {
+               if (!strcmp(mmac->name, defining->name) &&
+                       (mmac->nparam_min <= defining->nparam_max
+                               || defining->plus)
+                       && (defining->nparam_min <= mmac->nparam_max
+                               || mmac->plus))
+               {
+                   error(ERR_WARNING,
+                           "redefining multi-line macro `%s'",
+                           defining->name);
+                   break;
+               }
+               mmac = mmac->next;
+           }
+           /*
+            * Handle default parameters.
+            */
+           if (tline && tline->next)
+           {
+               defining->dlist = tline->next;
+               tline->next = NULL;
+               count_mmac_params(defining->dlist, &defining->ndefs,
+                       &defining->defaults);
+           }
+           else
+           {
+               defining->dlist = NULL;
+               defining->defaults = NULL;
+           }
+           defining->expansion = NULL;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_ENDM:
+       case PP_ENDMACRO:
+           if (!defining)
+           {
+               error(ERR_NONFATAL, "`%s': not defining a macro",
+                       tline->text);
+               return DIRECTIVE_FOUND;
+           }
+           k = hash(defining->name);
+           defining->next = mmacros[k];
+           mmacros[k] = defining;
+           defining = NULL;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_ROTATE:
+           if (tline->next && tline->next->type == TOK_WHITESPACE)
+               tline = tline->next;
+           if (tline->next == NULL)
+           {
+               free_tlist(origline);
+               error(ERR_NONFATAL, "`%%rotate' missing rotate count");
+               return DIRECTIVE_FOUND;
+           }
+           t = expand_smacro(tline->next);
+           tline->next = NULL;
+           free_tlist(origline);
+           tline = t;
+           tptr = &t;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult =
+                   evaluate(ppscan, tptr, &tokval, NULL, pass, error, NULL);
+           free_tlist(tline);
+           if (!evalresult)
+               return DIRECTIVE_FOUND;
+           if (tokval.t_type)
+               error(ERR_WARNING,
+                       "trailing garbage after expression ignored");
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL, "non-constant value given to `%%rotate'");
+               return DIRECTIVE_FOUND;
+           }
+           mmac = istk->mstk;
+           while (mmac && !mmac->name) /* avoid mistaking %reps for macros */
+               mmac = mmac->next_active;
+           if (!mmac)
+           {
+               error(ERR_NONFATAL,
+                       "`%%rotate' invoked outside a macro call");
+           } 
+           else if (mmac->nparam == 0)
+           {
+               error(ERR_NONFATAL,
+                       "`%%rotate' invoked within macro without parameters");
+           }
+           else
+           {
+               mmac->rotate = mmac->rotate + reloc_value(evalresult);
+               
+               if (mmac->rotate < 0)
+                   mmac->rotate = 
+                       mmac->nparam - (-mmac->rotate) % mmac->nparam;
+               mmac->rotate %= mmac->nparam;
+           }
+           return DIRECTIVE_FOUND;
+
+       case PP_REP:
+           nolist = FALSE;
+           tline = tline->next;
+           if (tline->next && tline->next->type == TOK_WHITESPACE)
+               tline = tline->next;
+           if (tline->next && tline->next->type == TOK_ID &&
+                   !nasm_stricmp(tline->next->text, ".nolist"))
+           {
+               tline = tline->next;
+               nolist = TRUE;
+           }
+           t = expand_smacro(tline->next);
+           tline->next = NULL;
+           free_tlist(origline);
+           tline = t;
+           tptr = &t;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult =
+                   evaluate(ppscan, tptr, &tokval, NULL, pass, error, NULL);
+           free_tlist(tline);
+           if (!evalresult)
+               return DIRECTIVE_FOUND;
+           if (tokval.t_type)
+               error(ERR_WARNING,
+                       "trailing garbage after expression ignored");
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL, "non-constant value given to `%%rep'");
+               return DIRECTIVE_FOUND;
+           }
+           tmp_defining = defining;
+           defining = nasm_malloc(sizeof(MMacro));
+           defining->name = NULL;      /* flags this macro as a %rep block */
+           defining->casesense = 0;
+           defining->plus = FALSE;
+           defining->nolist = nolist;
+           defining->in_progress = reloc_value(evalresult) + 1;
+           defining->nparam_min = defining->nparam_max = 0;
+           defining->defaults = NULL;
+           defining->dlist = NULL;
+           defining->expansion = NULL;
+           defining->next_active = istk->mstk;
+           defining->rep_nest = tmp_defining;
+           return DIRECTIVE_FOUND;
+
+       case PP_ENDREP:
+           if (!defining || defining->name)
+           {
+               error(ERR_NONFATAL, "`%%endrep': no matching `%%rep'");
+               return DIRECTIVE_FOUND;
+           }
+
+           /*
+            * Now we have a "macro" defined - although it has no name
+            * and we won't be entering it in the hash tables - we must
+            * push a macro-end marker for it on to istk->expansion.
+            * After that, it will take care of propagating itself (a
+            * macro-end marker line for a macro which is really a %rep
+            * block will cause the macro to be re-expanded, complete
+            * with another macro-end marker to ensure the process
+            * continues) until the whole expansion is forcibly removed
+            * from istk->expansion by a %exitrep.
+            */
+           l = nasm_malloc(sizeof(Line));
+           l->next = istk->expansion;
+           l->finishes = defining;
+           l->first = NULL;
+           istk->expansion = l;
+
+           istk->mstk = defining;
+
+           list->uplevel(defining->nolist ? LIST_MACRO_NOLIST : LIST_MACRO);
+           tmp_defining = defining;
+           defining = defining->rep_nest;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_EXITREP:
+           /*
+            * We must search along istk->expansion until we hit a
+            * macro-end marker for a macro with no name. Then we set
+            * its `in_progress' flag to 0.
+            */
+           for (l = istk->expansion; l; l = l->next)
+               if (l->finishes && !l->finishes->name)
+                   break;
+
+           if (l)
+               l->finishes->in_progress = 0;
+           else
+               error(ERR_NONFATAL, "`%%exitrep' not within `%%rep' block");
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_XDEFINE:
+       case PP_IXDEFINE:
+       case PP_DEFINE:
+       case PP_IDEFINE:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL,
+                       "`%%%s%sdefine' expects a macro identifier",
+                       ((i == PP_IDEFINE || i == PP_IXDEFINE) ? "i" : ""),
+                       ((i == PP_XDEFINE || i == PP_IXDEFINE) ? "x" : ""));
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+           mname = tline->text;
+           last = tline;
+           param_start = tline = tline->next;
+           nparam = 0;
+
+           /* Expand the macro definition now for %xdefine and %ixdefine */
+           if ((i == PP_XDEFINE) || (i == PP_IXDEFINE))
+               tline = expand_smacro(tline);
+
+           if (tok_is_(tline, "("))
+           {
+               /*
+                * This macro has parameters.
+                */
+
+               tline = tline->next;
+               while (1)
+               {
+                   skip_white_(tline);
+                   if (!tline)
+                   {
+                       error(ERR_NONFATAL, "parameter identifier expected");
+                       free_tlist(origline);
+                       return DIRECTIVE_FOUND;
+                   }
+                   if (tline->type != TOK_ID)
+                   {
+                       error(ERR_NONFATAL,
+                               "`%s': parameter identifier expected",
+                               tline->text);
+                       free_tlist(origline);
+                       return DIRECTIVE_FOUND;
+                   }
+                   tline->type = TOK_SMAC_PARAM + nparam++;
+                   tline = tline->next;
+                   skip_white_(tline);
+                   if (tok_is_(tline, ","))
+                   {
+                       tline = tline->next;
+                       continue;
+                   }
+                   if (!tok_is_(tline, ")"))
+                   {
+                       error(ERR_NONFATAL,
+                               "`)' expected to terminate macro template");
+                       free_tlist(origline);
+                       return DIRECTIVE_FOUND;
+                   }
+                   break;
+               }
+               last = tline;
+               tline = tline->next;
+           }
+           if (tok_type_(tline, TOK_WHITESPACE))
+               last = tline, tline = tline->next;
+           macro_start = NULL;
+           last->next = NULL;
+           t = tline;
+           while (t)
+           {
+               if (t->type == TOK_ID)
+               {
+                   for (tt = param_start; tt; tt = tt->next)
+                       if (tt->type >= TOK_SMAC_PARAM &&
+                               !strcmp(tt->text, t->text))
+                           t->type = tt->type;
+               }
+               tt = t->next;
+               t->next = macro_start;
+               macro_start = t;
+               t = tt;
+           }
+           /*
+            * Good. We now have a macro name, a parameter count, and a
+            * token list (in reverse order) for an expansion. We ought
+            * to be OK just to create an SMacro, store it, and let
+            * free_tlist have the rest of the line (which we have
+            * carefully re-terminated after chopping off the expansion
+            * from the end).
+            */
+           if (smacro_defined(ctx, mname, nparam, &smac, i == PP_DEFINE))
+           {
+               if (!smac)
+               {
+                   error(ERR_WARNING,
+                           "single-line macro `%s' defined both with and"
+                           " without parameters", mname);
+                   free_tlist(origline);
+                   free_tlist(macro_start);
+                   return DIRECTIVE_FOUND;
+               }
+               else
+               {
+                   /*
+                    * We're redefining, so we have to take over an
+                    * existing SMacro structure. This means freeing
+                    * what was already in it.
+                    */
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+               }
+           }
+           else
+           {
+               smac = nasm_malloc(sizeof(SMacro));
+               smac->next = *smhead;
+               *smhead = smac;
+           }
+           smac->name = nasm_strdup(mname);
+           smac->casesense = ((i == PP_DEFINE) || (i == PP_XDEFINE));
+           smac->nparam = nparam;
+           smac->expansion = macro_start;
+           smac->in_progress = FALSE;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_UNDEF:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL, "`%%undef' expects a macro identifier");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           if (tline->next)
+           {
+               error(ERR_WARNING,
+                       "trailing garbage after macro name ignored");
+           }
+
+           /* Find the context that symbol belongs to */
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+
+           mname = tline->text;
+           last = tline;
+           last->next = NULL;
+
+           /*
+            * We now have a macro name... go hunt for it.
+            */
+           while (smacro_defined(ctx, mname, -1, &smac, 1))
+           {
+               /* Defined, so we need to find its predecessor and nuke it */
+               SMacro **s;
+               for (s = smhead; *s && *s != smac; s = &(*s)->next);
+               if (*s)
+               {
+                   *s = smac->next;
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+                   nasm_free(smac);
+               }
+           }
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_STRLEN:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL,
+                       "`%%strlen' expects a macro identifier as first parameter");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+           mname = tline->text;
+           last = tline;
+           tline = expand_smacro(tline->next);
+           last->next = NULL;
+
+           t = tline;
+           while (tok_type_(t, TOK_WHITESPACE))
+               t = t->next;
+           /* t should now point to the string */
+           if (t->type != TOK_STRING)
+           {
+               error(ERR_NONFATAL,
+                       "`%%strlen` requires string as second parameter");
+               free_tlist(tline);
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           macro_start = nasm_malloc(sizeof(*macro_start));
+           macro_start->next = NULL;
+           make_tok_num(macro_start, strlen(t->text) - 2);
+           macro_start->mac = NULL;
+
+           /*
+            * We now have a macro name, an implicit parameter count of
+            * zero, and a numeric token to use as an expansion. Create
+            * and store an SMacro.
+            */
+           if (smacro_defined(ctx, mname, 0, &smac, i == PP_STRLEN))
+           {
+               if (!smac)
+                   error(ERR_WARNING,
+                           "single-line macro `%s' defined both with and"
+                           " without parameters", mname);
+               else
+               {
+                   /*
+                    * We're redefining, so we have to take over an
+                    * existing SMacro structure. This means freeing
+                    * what was already in it.
+                    */
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+               }
+           }
+           else
+           {
+               smac = nasm_malloc(sizeof(SMacro));
+               smac->next = *smhead;
+               *smhead = smac;
+           }
+           smac->name = nasm_strdup(mname);
+           smac->casesense = (i == PP_STRLEN);
+           smac->nparam = 0;
+           smac->expansion = macro_start;
+           smac->in_progress = FALSE;
+           free_tlist(tline);
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_SUBSTR:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL,
+                       "`%%substr' expects a macro identifier as first parameter");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+           mname = tline->text;
+           last = tline;
+           tline = expand_smacro(tline->next);
+           last->next = NULL;
+
+           t = tline->next;
+           while (tok_type_(t, TOK_WHITESPACE))
+               t = t->next;
+
+           /* t should now point to the string */
+           if (t->type != TOK_STRING)
+           {
+               error(ERR_NONFATAL,
+                       "`%%substr` requires string as second parameter");
+               free_tlist(tline);
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           tt = t->next;
+           tptr = &tt;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult =
+                   evaluate(ppscan, tptr, &tokval, NULL, pass, error, NULL);
+           if (!evalresult)
+           {
+               free_tlist(tline);
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL, "non-constant value given to `%%substr`");
+               free_tlist(tline);
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           macro_start = nasm_malloc(sizeof(*macro_start));
+           macro_start->next = NULL;
+           macro_start->text = nasm_strdup("'''");
+           if (evalresult->value > 0
+                   && evalresult->value < strlen(t->text) - 1)
+           {
+               macro_start->text[1] = t->text[evalresult->value];
+           }
+           else
+           {
+               macro_start->text[2] = '\0';
+           }
+           macro_start->type = TOK_STRING;
+           macro_start->mac = NULL;
+
+           /*
+            * We now have a macro name, an implicit parameter count of
+            * zero, and a numeric token to use as an expansion. Create
+            * and store an SMacro.
+            */
+           if (smacro_defined(ctx, mname, 0, &smac, i == PP_SUBSTR))
+           {
+               if (!smac)
+                   error(ERR_WARNING,
+                           "single-line macro `%s' defined both with and"
+                           " without parameters", mname);
+               else
+               {
+                   /*
+                    * We're redefining, so we have to take over an
+                    * existing SMacro structure. This means freeing
+                    * what was already in it.
+                    */
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+               }
+           }
+           else
+           {
+               smac = nasm_malloc(sizeof(SMacro));
+               smac->next = *smhead;
+               *smhead = smac;
+           }
+           smac->name = nasm_strdup(mname);
+           smac->casesense = (i == PP_SUBSTR);
+           smac->nparam = 0;
+           smac->expansion = macro_start;
+           smac->in_progress = FALSE;
+           free_tlist(tline);
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+
+       case PP_ASSIGN:
+       case PP_IASSIGN:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL,
+                       "`%%%sassign' expects a macro identifier",
+                       (i == PP_IASSIGN ? "i" : ""));
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+           mname = tline->text;
+           last = tline;
+           tline = expand_smacro(tline->next);
+           last->next = NULL;
+
+           t = tline;
+           tptr = &t;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult =
+                   evaluate(ppscan, tptr, &tokval, NULL, pass, error, NULL);
+           free_tlist(tline);
+           if (!evalresult)
+           {
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           if (tokval.t_type)
+               error(ERR_WARNING,
+                       "trailing garbage after expression ignored");
+
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL,
+                       "non-constant value given to `%%%sassign'",
+                       (i == PP_IASSIGN ? "i" : ""));
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           macro_start = nasm_malloc(sizeof(*macro_start));
+           macro_start->next = NULL;
+           make_tok_num(macro_start, reloc_value(evalresult));
+           macro_start->mac = NULL;
+
+           /*
+            * We now have a macro name, an implicit parameter count of
+            * zero, and a numeric token to use as an expansion. Create
+            * and store an SMacro.
+            */
+           if (smacro_defined(ctx, mname, 0, &smac, i == PP_ASSIGN))
+           {
+               if (!smac)
+                   error(ERR_WARNING,
+                           "single-line macro `%s' defined both with and"
+                           " without parameters", mname);
+               else
+               {
+                   /*
+                    * We're redefining, so we have to take over an
+                    * existing SMacro structure. This means freeing
+                    * what was already in it.
+                    */
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+               }
+           }
+           else
+           {
+               smac = nasm_malloc(sizeof(SMacro));
+               smac->next = *smhead;
+               *smhead = smac;
+           }
+           smac->name = nasm_strdup(mname);
+           smac->casesense = (i == PP_ASSIGN);
+           smac->nparam = 0;
+           smac->expansion = macro_start;
+           smac->in_progress = FALSE;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_LINE:
+           /*
+            * Syntax is `%line nnn[+mmm] [filename]'
+            */
+           tline = tline->next;
+           skip_white_(tline);
+           if (!tok_type_(tline, TOK_NUMBER))
+           {
+               error(ERR_NONFATAL, "`%%line' expects line number");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           k = readnum(tline->text, &j);
+           m = 1;
+           tline = tline->next;
+           if (tok_is_(tline, "+"))
+           {
+               tline = tline->next;
+               if (!tok_type_(tline, TOK_NUMBER))
+               {
+                   error(ERR_NONFATAL, "`%%line' expects line increment");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               m = readnum(tline->text, &j);
+               tline = tline->next;
+           }
+           skip_white_(tline);
+           src_set_linnum(k);
+           istk->lineinc = m;
+           if (tline)
+           {
+               nasm_free(src_set_fname(detoken(tline, FALSE)));
+           }
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       default:
+           error(ERR_FATAL,
+                   "preprocessor directive `%s' not yet implemented",
+                   directives[i]);
+           break;
+    }
+    return DIRECTIVE_FOUND;
+}
+
+/*
+ * Ensure that a macro parameter contains a condition code and
+ * nothing else. Return the condition code index if so, or -1
+ * otherwise.
+ */
+static int
+find_cc(Token * t)
+{
+    Token *tt;
+    int i, j, k, m;
+
+    skip_white_(t);
+    if (t->type != TOK_ID)
+       return -1;
+    tt = t->next;
+    skip_white_(tt);
+    if (tt && (tt->type != TOK_OTHER || strcmp(tt->text, ",")))
+       return -1;
+
+    i = -1;
+    j = elements(conditions);
+    while (j - i > 1)
+    {
+       k = (j + i) / 2;
+       m = nasm_stricmp(t->text, conditions[k]);
+       if (m == 0)
+       {
+           i = k;
+           j = -2;
+           break;
+       }
+       else if (m < 0)
+       {
+           j = k;
+       }
+       else
+           i = k;
+    }
+    if (j != -2)
+       return -1;
+    return i;
+}
+
+/*
+ * Expand MMacro-local things: parameter references (%0, %n, %+n,
+ * %-n) and MMacro-local identifiers (%%foo).
+ */
+static Token *
+expand_mmac_params(Token * tline)
+{
+    Token *t, *tt, **tail, *thead;
+
+    tail = &thead;
+    thead = NULL;
+
+    while (tline)
+    {
+       if (tline->type == TOK_PREPROC_ID &&
+               (((tline->text[1] == '+' || tline->text[1] == '-')
+                               && tline->text[2]) || tline->text[1] == '%'
+                       || (tline->text[1] >= '0' && tline->text[1] <= '9')))
+       {
+           char *text = NULL;
+           int type = 0, cc;   /* type = 0 to placate optimisers */
+           char tmpbuf[30];
+           int n, i;
+           MMacro *mac;
+
+           t = tline;
+           tline = tline->next;
+
+           mac = istk->mstk;
+           while (mac && !mac->name)   /* avoid mistaking %reps for macros */
+               mac = mac->next_active;
+           if (!mac)
+               error(ERR_NONFATAL, "`%s': not in a macro call", t->text);
+           else
+               switch (t->text[1])
+               {
+                       /*
+                        * We have to make a substitution of one of the
+                        * forms %1, %-1, %+1, %%foo, %0.
+                        */
+                   case '0':
+                       type = TOK_NUMBER;
+                       sprintf(tmpbuf, "%d", mac->nparam);
+                       text = nasm_strdup(tmpbuf);
+                       break;
+                   case '%':
+                       type = TOK_ID;
+                       sprintf(tmpbuf, "..@%lu.", mac->unique);
+                       text = nasm_strcat(tmpbuf, t->text + 2);
+                       break;
+                   case '-':
+                       n = atoi(t->text + 2) - 1;
+                       if (n >= mac->nparam)
+                           tt = NULL;
+                       else
+                       {
+                           if (mac->nparam > 1)
+                               n = (n + mac->rotate) % mac->nparam;
+                           tt = mac->params[n];
+                       }
+                       cc = find_cc(tt);
+                       if (cc == -1)
+                       {
+                           error(ERR_NONFATAL,
+                                   "macro parameter %d is not a condition code",
+                                   n + 1);
+                           text = NULL;
+                       }
+                       else
+                       {
+                           type = TOK_ID;
+                           if (inverse_ccs[cc] == -1)
+                           {
+                               error(ERR_NONFATAL,
+                                       "condition code `%s' is not invertible",
+                                       conditions[cc]);
+                               text = NULL;
+                           }
+                           else
+                               text =
+                                       nasm_strdup(conditions[inverse_ccs
+                                                [cc]]);
+                       }
+                       break;
+                   case '+':
+                       n = atoi(t->text + 2) - 1;
+                       if (n >= mac->nparam)
+                           tt = NULL;
+                       else
+                       {
+                           if (mac->nparam > 1)
+                               n = (n + mac->rotate) % mac->nparam;
+                           tt = mac->params[n];
+                       }
+                       cc = find_cc(tt);
+                       if (cc == -1)
+                       {
+                           error(ERR_NONFATAL,
+                                   "macro parameter %d is not a condition code",
+                                   n + 1);
+                           text = NULL;
+                       }
+                       else
+                       {
+                           type = TOK_ID;
+                           text = nasm_strdup(conditions[cc]);
+                       }
+                       break;
+                   default:
+                       n = atoi(t->text + 1) - 1;
+                       if (n >= mac->nparam)
+                           tt = NULL;
+                       else
+                       {
+                           if (mac->nparam > 1)
+                               n = (n + mac->rotate) % mac->nparam;
+                           tt = mac->params[n];
+                       }
+                       if (tt)
+                       {
+                           for (i = 0; i < mac->paramlen[n]; i++)
+                           {
+                               *tail =
+                                       new_Token(NULL, tt->type, tt->text,
+                                       0);
+                               tail = &(*tail)->next;
+                               tt = tt->next;
+                           }
+                       }
+                       text = NULL;    /* we've done it here */
+                       break;
+               }
+           if (!text)
+           {
+               delete_Token(t);
+           }
+           else
+           {
+               *tail = t;
+               tail = &t->next;
+               t->type = type;
+               nasm_free(t->text);
+               t->text = text;
+               t->mac = NULL;
+           }
+           continue;
+       }
+       else
+       {
+           t = *tail = tline;
+           tline = tline->next;
+           t->mac = NULL;
+           tail = &t->next;
+       }
+    }
+    *tail = NULL;
+    t = thead;
+    for (; t && (tt = t->next) != NULL; t = t->next)
+       switch (t->type)
+       {
+           case TOK_WHITESPACE:
+               if (tt->type == TOK_WHITESPACE)
+               {
+                   t->next = delete_Token(tt);
+               }
+               break;
+           case TOK_ID:
+               if (tt->type == TOK_ID || tt->type == TOK_NUMBER)
+               {
+                   char *tmp = nasm_strcat(t->text, tt->text);
+                   nasm_free(t->text);
+                   t->text = tmp;
+                   t->next = delete_Token(tt);
+               }
+               break;
+           case TOK_NUMBER:
+               if (tt->type == TOK_NUMBER)
+               {
+                   char *tmp = nasm_strcat(t->text, tt->text);
+                   nasm_free(t->text);
+                   t->text = tmp;
+                   t->next = delete_Token(tt);
+               }
+               break;
+       }
+
+    return thead;
+}
+
+/*
+ * Expand all single-line macro calls made in the given line.
+ * Return the expanded version of the line. The original is deemed
+ * to be destroyed in the process. (In reality we'll just move
+ * Tokens from input to output a lot of the time, rather than
+ * actually bothering to destroy and replicate.)
+ */
+static Token *
+expand_smacro(Token * tline)
+{
+    Token *t, *tt, *mstart, **tail, *thead;
+    SMacro *head = NULL, *m;
+    Token **params;
+    int *paramsize;
+    int nparam, sparam, brackets, rescan;
+    Token *org_tline = tline;
+    Context *ctx;
+    char *mname;
+
+    /*
+     * Trick: we should avoid changing the start token pointer since it can
+     * be contained in "next" field of other token. Because of this
+     * we allocate a copy of first token and work with it; at the end of
+     * routine we copy it back
+     */
+    if (org_tline)
+    {
+       tline =
+               new_Token(org_tline->next, org_tline->type, org_tline->text,
+               0);
+       tline->mac = org_tline->mac;
+       nasm_free(org_tline->text);
+       org_tline->text = NULL;
+    }
+
+  again:
+    tail = &thead;
+    thead = NULL;
+
+    while (tline)
+    {                          /* main token loop */
+       if ((mname = tline->text))
+       {
+           /* if this token is a local macro, look in local context */
+           if (tline->type == TOK_ID || tline->type == TOK_PREPROC_ID)
+               ctx = get_ctx(mname, TRUE);
+           else
+               ctx = NULL;
+           if (!ctx)
+               head = smacros[hash(mname)];
+           else
+               head = ctx->localmac;
+           /*
+            * We've hit an identifier. As in is_mmacro below, we first
+            * check whether the identifier is a single-line macro at
+            * all, then think about checking for parameters if
+            * necessary.
+            */
+           for (m = head; m; m = m->next)
+               if (!mstrcmp(m->name, mname, m->casesense))
+                   break;
+           if (m)
+           {
+               mstart = tline;
+               params = NULL;
+               paramsize = NULL;
+               if (m->nparam == 0)
+               {
+                   /*
+                    * Simple case: the macro is parameterless. Discard the
+                    * one token that the macro call took, and push the
+                    * expansion back on the to-do stack.
+                    */
+                   if (!m->expansion)
+                   {
+                       if (!strcmp("__FILE__", m->name))
+                       {
+                           long num = 0;
+                           src_get(&num, &(tline->text));
+                           nasm_quote(&(tline->text));
+                           tline->type = TOK_STRING;
+                           continue;
+                       }
+                       if (!strcmp("__LINE__", m->name))
+                       {
+                           nasm_free(tline->text);
+                           make_tok_num(tline, src_get_linnum());
+                           continue;
+                       }
+                       tline = delete_Token(tline);
+                       continue;
+                   }
+               }
+               else
+               {
+                   /*
+                    * Complicated case: at least one macro with this name
+                    * exists and takes parameters. We must find the
+                    * parameters in the call, count them, find the SMacro
+                    * that corresponds to that form of the macro call, and
+                    * substitute for the parameters when we expand. What a
+                    * pain.
+                    */
+                   tline = tline->next;
+                   skip_white_(tline);
+                   if (!tok_is_(tline, "("))
+                   {
+                       /*
+                        * This macro wasn't called with parameters: ignore
+                        * the call. (Behaviour borrowed from gnu cpp.)
+                        */
+                       tline = mstart;
+                       m = NULL;
+                   }
+                   else
+                   {
+                       int paren = 0;
+                       int white = 0;
+                       brackets = 0;
+                       nparam = 0;
+                       tline = tline->next;
+                       sparam = PARAM_DELTA;
+                       params = nasm_malloc(sparam * sizeof(Token *));
+                       params[0] = tline;
+                       paramsize = nasm_malloc(sparam * sizeof(int));
+                       paramsize[0] = 0;
+                       for (;; tline = tline->next)
+                       {       /* parameter loop */
+                           if (!tline)
+                           {
+                               error(ERR_NONFATAL,
+                                       "macro call expects terminating `)'");
+                               break;
+                           }
+                           if (tline->type == TOK_WHITESPACE
+                                   && brackets <= 0)
+                           {
+                               if (paramsize[nparam])
+                                   white++;
+                               else
+                                   params[nparam] = tline->next;
+                               continue;       /* parameter loop */
+                           }
+                           if (tline->type == TOK_OTHER
+                                   && tline->text[1] == 0)
+                           {
+                               char ch = tline->text[0];
+                               if (ch == ',' && !paren && brackets <= 0)
+                               {
+                                   if (++nparam >= sparam)
+                                   {
+                                       sparam += PARAM_DELTA;
+                                       params = nasm_realloc(params,
+                                               sparam * sizeof(Token *));
+                                       paramsize = nasm_realloc(paramsize,
+                                               sparam * sizeof(int));
+                                   }
+                                   params[nparam] = tline->next;
+                                   paramsize[nparam] = 0;
+                                   white = 0;
+                                   continue;   /* parameter loop */
+                               }
+                               if (ch == '{' &&
+                                       (brackets > 0 || (brackets == 0 &&
+                                                       !paramsize[nparam])))
+                               {
+                                   if (!(brackets++))
+                                   {
+                                       params[nparam] = tline->next;
+                                       continue;       /* parameter loop */
+                                   }
+                               }
+                               if (ch == '}' && brackets > 0)
+                                   if (--brackets == 0)
+                                   {
+                                       brackets = -1;
+                                       continue;       /* parameter loop */
+                                   }
+                               if (ch == '(' && !brackets)
+                                   paren++;
+                               if (ch == ')' && brackets <= 0)
+                                   if (--paren < 0)
+                                       break;
+                           }
+                           if (brackets < 0)
+                           {
+                               brackets = 0;
+                               error(ERR_NONFATAL, "braces do not "
+                                       "enclose all of macro parameter");
+                           }
+                           paramsize[nparam] += white + 1;
+                           white = 0;
+                       }       /* parameter loop */
+                       nparam++;
+                       while (m && (m->nparam != nparam ||
+                                       mstrcmp(m->name, mname,
+                                               m->casesense)))
+                           m = m->next;
+                       if (!m)
+                           error(ERR_WARNING | ERR_WARN_MNP,
+                                   "macro `%s' exists, "
+                                   "but not taking %d parameters",
+                                   mstart->text, nparam);
+                   }
+               }
+               if (m && m->in_progress)
+                   m = NULL;
+               if (!m)         /* in progess or didn't find '(' or wrong nparam */
+               {
+                   /* 
+                    * Design question: should we handle !tline, which
+                    * indicates missing ')' here, or expand those
+                    * macros anyway, which requires the (t) test a few
+                    * lines down?  
+                    */
+                   nasm_free(params);
+                   nasm_free(paramsize);
+                   tline = mstart;
+               }
+               else
+               {
+                   /*
+                    * Expand the macro: we are placed on the last token of the
+                    * call, so that we can easily split the call from the
+                    * following tokens. We also start by pushing an SMAC_END
+                    * token for the cycle removal.
+                    */
+                   t = tline;
+                   if (t)
+                   {
+                       tline = t->next;
+                       t->next = NULL;
+                   }
+                   tt = new_Token(tline, TOK_SMAC_END, NULL, 0);
+                   tt->mac = m;
+                   m->in_progress = TRUE;
+                   tline = tt;
+                   for (t = m->expansion; t; t = t->next)
+                   {
+                       if (t->type >= TOK_SMAC_PARAM)
+                       {
+                           Token *pcopy = tline, **ptail = &pcopy;
+                           Token *ttt, *pt;
+                           int i;
+
+                           ttt = params[t->type - TOK_SMAC_PARAM];
+                           for (i = paramsize[t->type - TOK_SMAC_PARAM];
+                                   --i >= 0;)
+                           {
+                               pt = *ptail =
+                                       new_Token(tline, ttt->type, ttt->text,
+                                       0);
+                               ptail = &pt->next;
+                               ttt = ttt->next;
+                           }
+                           tline = pcopy;
+                       }
+                       else
+                       {
+                           tt = new_Token(tline, t->type, t->text, 0);
+                           tline = tt;
+                       }
+                   }
+
+                   /*
+                    * Having done that, get rid of the macro call, and clean
+                    * up the parameters.
+                    */
+                   nasm_free(params);
+                   nasm_free(paramsize);
+                   free_tlist(mstart);
+                   continue;   /* main token loop */
+               }
+           }
+       }
+
+       if (tline->type == TOK_SMAC_END)
+       {
+           tline->mac->in_progress = FALSE;
+           tline = delete_Token(tline);
+       }
+       else
+       {
+           t = *tail = tline;
+           tline = tline->next;
+           t->mac = NULL;
+           t->next = NULL;
+           tail = &t->next;
+       }
+    }
+
+    /*
+     * Now scan the entire line and look for successive TOK_IDs that resulted
+     * after expansion (they can't be produced by tokenise()). The successive
+     * TOK_IDs should be concatenated.
+     * Also we look for %+ tokens and concatenate the tokens before and after
+     * them (without white spaces in between).
+     */
+    t = thead;
+    rescan = 0;
+    while (t)
+    {
+       while (t && t->type != TOK_ID && t->type != TOK_PREPROC_ID)
+           t = t->next;
+       if (!t || !t->next)
+           break;
+       if (t->next->type == TOK_ID ||
+               t->next->type == TOK_PREPROC_ID ||
+               t->next->type == TOK_NUMBER)
+       {
+           char *p = nasm_strcat(t->text, t->next->text);
+           nasm_free(t->text);
+           t->next = delete_Token(t->next);
+           t->text = p;
+           rescan = 1;
+       }
+       else if (t->next->type == TOK_WHITESPACE && t->next->next &&
+               t->next->next->type == TOK_PREPROC_ID &&
+               strcmp(t->next->next->text, "%+") == 0)
+       {
+           /* free the next whitespace, the %+ token and next whitespace */
+           int i;
+           for (i = 1; i <= 3; i++)
+           {
+               if (!t->next || (i != 2 && t->next->type != TOK_WHITESPACE))
+                   break;
+               t->next = delete_Token(t->next);
+           }                   /* endfor */
+       }
+       else
+           t = t->next;
+    }
+    /* If we concatenaded something, re-scan the line for macros */
+    if (rescan)
+    {
+       tline = thead;
+       goto again;
+    }
+
+    if (org_tline)
+    {
+       if (thead)
+       {
+           *org_tline = *thead;
+           /* since we just gave text to org_line, don't free it */
+           thead->text = NULL;
+           delete_Token(thead);
+       }
+       else
+       {
+           /* the expression expanded to empty line;
+              we can't return NULL for some reasons
+              we just set the line to a single WHITESPACE token. */
+           memset(org_tline, 0, sizeof(*org_tline));
+           org_tline->text = NULL;
+           org_tline->type = TOK_WHITESPACE;
+       }
+       thead = org_tline;
+    }
+
+    return thead;
+}
+
+/*
+ * Similar to expand_smacro but used exclusively with macro identifiers
+ * right before they are fetched in. The reason is that there can be
+ * identifiers consisting of several subparts. We consider that if there
+ * are more than one element forming the name, user wants a expansion,
+ * otherwise it will be left as-is. Example:
+ *
+ *     %define %$abc cde
+ *
+ * the identifier %$abc will be left as-is so that the handler for %define
+ * will suck it and define the corresponding value. Other case:
+ *
+ *     %define _%$abc cde
+ *
+ * In this case user wants name to be expanded *before* %define starts
+ * working, so we'll expand %$abc into something (if it has a value;
+ * otherwise it will be left as-is) then concatenate all successive
+ * PP_IDs into one.
+ */
+static Token *
+expand_id(Token * tline)
+{
+    Token *cur, *oldnext = NULL;
+
+    if (!tline || !tline->next)
+       return tline;
+
+    cur = tline;
+    while (cur->next &&
+           (cur->next->type == TOK_ID ||
+       cur->next->type == TOK_PREPROC_ID || cur->next->type == TOK_NUMBER))
+       cur = cur->next;
+
+    /* If identifier consists of just one token, don't expand */
+    if (cur == tline)
+       return tline;
+
+    if (cur)
+    {
+       oldnext = cur->next;    /* Detach the tail past identifier */
+       cur->next = NULL;       /* so that expand_smacro stops here */
+    }
+
+    tline = expand_smacro(tline);
+
+    if (cur)
+    {
+       /* expand_smacro possibly changhed tline; re-scan for EOL */
+       cur = tline;
+       while (cur && cur->next)
+           cur = cur->next;
+       if (cur)
+           cur->next = oldnext;
+    }
+
+    return tline;
+}
+
+/*
+ * Determine whether the given line constitutes a multi-line macro
+ * call, and return the MMacro structure called if so. Doesn't have
+ * to check for an initial label - that's taken care of in
+ * expand_mmacro - but must check numbers of parameters. Guaranteed
+ * to be called with tline->type == TOK_ID, so the putative macro
+ * name is easy to find.
+ */
+static MMacro *
+is_mmacro(Token * tline, Token *** params_array)
+{
+    MMacro *head, *m;
+    Token **params;
+    int nparam;
+
+    head = mmacros[hash(tline->text)];
+
+    /*
+     * Efficiency: first we see if any macro exists with the given
+     * name. If not, we can return NULL immediately. _Then_ we
+     * count the parameters, and then we look further along the
+     * list if necessary to find the proper MMacro.
+     */
+    for (m = head; m; m = m->next)
+       if (!mstrcmp(m->name, tline->text, m->casesense))
+           break;
+    if (!m)
+       return NULL;
+
+    /*
+     * OK, we have a potential macro. Count and demarcate the
+     * parameters.
+     */
+    count_mmac_params(tline->next, &nparam, &params);
+
+    /*
+     * So we know how many parameters we've got. Find the MMacro
+     * structure that handles this number.
+     */
+    while (m)
+    {
+       if (m->nparam_min <= nparam && (m->plus || nparam <= m->nparam_max))
+       {
+           /*
+            * This one is right. Just check if cycle removal
+            * prohibits us using it before we actually celebrate...
+            */
+           if (m->in_progress)
+           {
+#if 0
+               error(ERR_NONFATAL,
+                       "self-reference in multi-line macro `%s'", m->name);
+#endif
+               nasm_free(params);
+               return NULL;
+           }
+           /*
+            * It's right, and we can use it. Add its default
+            * parameters to the end of our list if necessary.
+            */
+           if (m->defaults && nparam < m->nparam_min + m->ndefs)
+           {
+               params =
+                       nasm_realloc(params,
+                       ((m->nparam_min + m->ndefs + 1) * sizeof(*params)));
+               while (nparam < m->nparam_min + m->ndefs)
+               {
+                   params[nparam] = m->defaults[nparam - m->nparam_min];
+                   nparam++;
+               }
+           }
+           /*
+            * If we've gone over the maximum parameter count (and
+            * we're in Plus mode), ignore parameters beyond
+            * nparam_max.
+            */
+           if (m->plus && nparam > m->nparam_max)
+               nparam = m->nparam_max;
+           /*
+            * Then terminate the parameter list, and leave.
+            */
+           if (!params)
+           {                   /* need this special case */
+               params = nasm_malloc(sizeof(*params));
+               nparam = 0;
+           }
+           params[nparam] = NULL;
+           *params_array = params;
+           return m;
+       }
+       /*
+        * This one wasn't right: look for the next one with the
+        * same name.
+        */
+       for (m = m->next; m; m = m->next)
+           if (!mstrcmp(m->name, tline->text, m->casesense))
+               break;
+    }
+
+    /*
+     * After all that, we didn't find one with the right number of
+     * parameters. Issue a warning, and fail to expand the macro.
+     */
+    error(ERR_WARNING | ERR_WARN_MNP,
+           "macro `%s' exists, but not taking %d parameters",
+           tline->text, nparam);
+    nasm_free(params);
+    return NULL;
+}
+
+/*
+ * Expand the multi-line macro call made by the given line, if
+ * there is one to be expanded. If there is, push the expansion on
+ * istk->expansion and return 1. Otherwise return 0.
+ */
+static int
+expand_mmacro(Token * tline)
+{
+    Token *startline = tline;
+    Token *label = NULL;
+    int dont_prepend = 0;
+    Token **params, *t, *tt;
+    MMacro *m;
+    Line *l, *ll;
+    int i, nparam, *paramlen;
+
+    t = tline;
+    skip_white_(t);
+/*    if (!tok_type_(t, TOK_ID))  Lino 02/25/02 */
+    if (!tok_type_(t, TOK_ID) && !tok_type_(t, TOK_PREPROC_ID))
+       return 0;
+    m = is_mmacro(t, &params);
+    if (!m)
+    {
+       Token *last;
+       /*
+        * We have an id which isn't a macro call. We'll assume
+        * it might be a label; we'll also check to see if a
+        * colon follows it. Then, if there's another id after
+        * that lot, we'll check it again for macro-hood.
+        */
+       label = last = t;
+       t = t->next;
+       if (tok_type_(t, TOK_WHITESPACE))
+           last = t, t = t->next;
+       if (tok_is_(t, ":"))
+       {
+           dont_prepend = 1;
+           last = t, t = t->next;
+           if (tok_type_(t, TOK_WHITESPACE))
+               last = t, t = t->next;
+       }
+       if (!tok_type_(t, TOK_ID) || (m = is_mmacro(t, &params)) == NULL)
+           return 0;
+       last->next = NULL;
+       tline = t;
+    }
+
+    /*
+     * Fix up the parameters: this involves stripping leading and
+     * trailing whitespace, then stripping braces if they are
+     * present.
+     */
+    for (nparam = 0; params[nparam]; nparam++)
+       ;
+    paramlen = nparam ? nasm_malloc(nparam * sizeof(*paramlen)) : NULL;
+
+    for (i = 0; params[i]; i++)
+    {
+       int brace = FALSE;
+       int comma = (!m->plus || i < nparam - 1);
+
+       t = params[i];
+       skip_white_(t);
+       if (tok_is_(t, "{"))
+           t = t->next, brace = TRUE, comma = FALSE;
+       params[i] = t;
+       paramlen[i] = 0;
+       while (t)
+       {
+           if (comma && t->type == TOK_OTHER && !strcmp(t->text, ","))
+               break;          /* ... because we have hit a comma */
+           if (comma && t->type == TOK_WHITESPACE && tok_is_(t->next, ","))
+               break;          /* ... or a space then a comma */
+           if (brace && t->type == TOK_OTHER && !strcmp(t->text, "}"))
+               break;          /* ... or a brace */
+           t = t->next;
+           paramlen[i]++;
+       }
+    }
+
+    /*
+     * OK, we have a MMacro structure together with a set of
+     * parameters. We must now go through the expansion and push
+     * copies of each Line on to istk->expansion. Substitution of
+     * parameter tokens and macro-local tokens doesn't get done
+     * until the single-line macro substitution process; this is
+     * because delaying them allows us to change the semantics
+     * later through %rotate.
+     *
+     * First, push an end marker on to istk->expansion, mark this
+     * macro as in progress, and set up its invocation-specific
+     * variables.
+     */
+    ll = nasm_malloc(sizeof(Line));
+    ll->next = istk->expansion;
+    ll->finishes = m;
+    ll->first = NULL;
+    istk->expansion = ll;
+
+    m->in_progress = TRUE;
+    m->params = params;
+    m->iline = tline;
+    m->nparam = nparam;
+    m->rotate = 0;
+    m->paramlen = paramlen;
+    m->unique = unique++;
+    m->lineno = 0;
+
+    m->next_active = istk->mstk;
+    istk->mstk = m;
+
+    for (l = m->expansion; l; l = l->next)
+    {
+       Token **tail;
+
+       ll = nasm_malloc(sizeof(Line));
+       ll->finishes = NULL;
+       ll->next = istk->expansion;
+       istk->expansion = ll;
+       tail = &ll->first;
+
+       for (t = l->first; t; t = t->next)
+       {
+           Token *x = t;
+           if (t->type == TOK_PREPROC_ID &&
+                   t->text[1] == '0' && t->text[2] == '0')
+           {
+               dont_prepend = -1;
+               x = label;
+               if (!x)
+                   continue;
+           }
+           tt = *tail = new_Token(NULL, x->type, x->text, 0);
+           tail = &tt->next;
+       }
+       *tail = NULL;
+    }
+
+    /*
+     * If we had a label, push it on as the first line of
+     * the macro expansion.
+     */
+    if (label)
+    {
+       if (dont_prepend < 0)
+           free_tlist(startline);
+       else
+       {
+           ll = nasm_malloc(sizeof(Line));
+           ll->finishes = NULL;
+           ll->next = istk->expansion;
+           istk->expansion = ll;
+           ll->first = startline;
+           if (!dont_prepend)
+           {
+               while (label->next)
+                   label = label->next;
+               label->next = tt = new_Token(NULL, TOK_OTHER, ":", 0);
+           }
+       }
+    }
+
+    list->uplevel(m->nolist ? LIST_MACRO_NOLIST : LIST_MACRO);
+
+    return 1;
+}
+
+/*
+ * Since preprocessor always operate only on the line that didn't
+ * arrived yet, we should always use ERR_OFFBY1. Also since user
+ * won't want to see same error twice (preprocessing is done once
+ * per pass) we will want to show errors only during pass one.
+ */
+static void
+error(int severity, const char *fmt, ...)
+{
+    va_list arg;
+    char buff[1024];
+
+    /* If we're in a dead branch of IF or something like it, ignore the error */
+    if (istk && istk->conds && !emitting(istk->conds->state))
+       return;
+
+    va_start(arg, fmt);
+    vsprintf(buff, fmt, arg);
+    va_end(arg);
+
+    if (istk && istk->mstk && istk->mstk->name)
+       _error(severity | ERR_PASS1, "(%s:%d) %s", istk->mstk->name,
+               istk->mstk->lineno, buff);
+    else 
+       _error(severity | ERR_PASS1, "%s", buff);
+}
+
+static void
+pp_reset(char *file, int apass, efunc errfunc, evalfunc eval,
+       ListGen * listgen)
+{
+    int h;
+
+    _error = errfunc;
+    cstk = NULL;
+    istk = nasm_malloc(sizeof(Include));
+    istk->next = NULL;
+    istk->conds = NULL;
+    istk->expansion = NULL;
+    istk->mstk = NULL;
+    istk->fp = fopen(file, "r");
+    istk->fname = NULL;
+    src_set_fname(nasm_strdup(file));
+    src_set_linnum(0);
+    istk->lineinc = 1;
+    if (!istk->fp)
+       error(ERR_FATAL | ERR_NOFILE, "unable to open input file `%s'", file);
+    defining = NULL;
+    for (h = 0; h < NHASH; h++)
+    {
+       mmacros[h] = NULL;
+       smacros[h] = NULL;
+    }
+    unique = 0;
+       if (tasm_compatible_mode) {
+           stdmacpos = stdmac;
+       } else {
+               stdmacpos = &stdmac[TASM_MACRO_COUNT];
+       }
+    any_extrastdmac = (extrastdmac != NULL);
+    list = listgen;
+    evaluate = eval;
+    pass = apass;
+}
+
+static char *
+pp_getline(void)
+{
+    char *line;
+    Token *tline;
+
+    while (1)
+    {
+       /*
+        * Fetch a tokenised line, either from the macro-expansion
+        * buffer or from the input file.
+        */
+       tline = NULL;
+       while (istk->expansion && istk->expansion->finishes)
+       {
+           Line *l = istk->expansion;
+           if (!l->finishes->name && l->finishes->in_progress > 1)
+           {
+               Line *ll;
+
+               /*
+                * This is a macro-end marker for a macro with no
+                * name, which means it's not really a macro at all
+                * but a %rep block, and the `in_progress' field is
+                * more than 1, meaning that we still need to
+                * repeat. (1 means the natural last repetition; 0
+                * means termination by %exitrep.) We have
+                * therefore expanded up to the %endrep, and must
+                * push the whole block on to the expansion buffer
+                * again. We don't bother to remove the macro-end
+                * marker: we'd only have to generate another one
+                * if we did.
+                */
+               l->finishes->in_progress--;
+               for (l = l->finishes->expansion; l; l = l->next)
+               {
+                   Token *t, *tt, **tail;
+
+                   ll = nasm_malloc(sizeof(Line));
+                   ll->next = istk->expansion;
+                   ll->finishes = NULL;
+                   ll->first = NULL;
+                   tail = &ll->first;
+
+                   for (t = l->first; t; t = t->next)
+                   {
+                       if (t->text || t->type == TOK_WHITESPACE)
+                       {
+                           tt = *tail = new_Token(NULL, t->type, t->text, 0);
+                           tail = &tt->next;
+                       }
+                   }
+
+                   istk->expansion = ll;
+               }
+           }
+           else
+           {
+               /*
+                * Check whether a `%rep' was started and not ended
+                * within this macro expansion. This can happen and
+                * should be detected. It's a fatal error because
+                * I'm too confused to work out how to recover
+                * sensibly from it.
+                */
+               if (defining)
+               {
+                   if (defining->name)
+                       error(ERR_PANIC, "defining with name in expansion");
+                   else if (istk->mstk->name)
+                       error(ERR_FATAL, "`%%rep' without `%%endrep' within"
+                               " expansion of macro `%s'", istk->mstk->name);
+               }
+
+               /*
+                * FIXME:  investigate the relationship at this point between
+                * istk->mstk and l->finishes
+                */
+               {
+                   MMacro *m = istk->mstk;
+                   istk->mstk = m->next_active;
+                   if (m->name)
+                   {
+                       /*
+                        * This was a real macro call, not a %rep, and
+                        * therefore the parameter information needs to
+                        * be freed.
+                        */
+                       nasm_free(m->params);
+                       free_tlist(m->iline);
+                       nasm_free(m->paramlen);
+                       l->finishes->in_progress = FALSE;
+                   }
+                   else
+                       free_mmacro(m);
+               }
+               istk->expansion = l->next;
+               nasm_free(l);
+               list->downlevel(LIST_MACRO);
+           }
+       }
+       while (1)
+       {                       /* until we get a line we can use */
+
+           if (istk->expansion)
+           {                   /* from a macro expansion */
+               char *p;
+               Line *l = istk->expansion;
+               if (istk->mstk)
+                   istk->mstk->lineno++;
+               tline = l->first;
+               istk->expansion = l->next;
+               nasm_free(l);
+               p = detoken(tline, FALSE);
+               list->line(LIST_MACRO, p);
+               nasm_free(p);
+               break;
+           }
+           line = read_line();
+           if (line)
+           {                   /* from the current input file */
+               line = prepreproc(line);
+               tline = tokenise(line);
+               nasm_free(line);
+               break;
+           }
+           /*
+            * The current file has ended; work down the istk
+            */
+           {
+               Include *i = istk;
+               fclose(i->fp);
+               if (i->conds)
+                   error(ERR_FATAL, "expected `%%endif' before end of file");
+               /* only set line and file name if there's a next node */
+               if (i->next) 
+               {
+                   src_set_linnum(i->lineno);
+                   nasm_free(src_set_fname(i->fname));
+               }
+               istk = i->next;
+               list->downlevel(LIST_INCLUDE);
+               nasm_free(i);
+               if (!istk)
+                   return NULL;
+           }
+       }
+
+       /*
+        * We must expand MMacro parameters and MMacro-local labels
+        * _before_ we plunge into directive processing, to cope
+        * with things like `%define something %1' such as STRUC
+        * uses. Unless we're _defining_ a MMacro, in which case
+        * those tokens should be left alone to go into the
+        * definition; and unless we're in a non-emitting
+        * condition, in which case we don't want to meddle with
+        * anything.
+        */
+       if (!defining && !(istk->conds && !emitting(istk->conds->state)))
+           tline = expand_mmac_params(tline);
+
+       /*
+        * Check the line to see if it's a preprocessor directive.
+        */
+       if (do_directive(tline) == DIRECTIVE_FOUND)
+       {
+           continue;
+       }
+       else if (defining)
+       {
+           /*
+            * We're defining a multi-line macro. We emit nothing
+            * at all, and just
+            * shove the tokenised line on to the macro definition.
+            */
+           Line *l = nasm_malloc(sizeof(Line));
+           l->next = defining->expansion;
+           l->first = tline;
+           l->finishes = FALSE;
+           defining->expansion = l;
+           continue;
+       }
+       else if (istk->conds && !emitting(istk->conds->state))
+       {
+           /*
+            * We're in a non-emitting branch of a condition block.
+            * Emit nothing at all, not even a blank line: when we
+            * emerge from the condition we'll give a line-number
+            * directive so we keep our place correctly.
+            */
+           free_tlist(tline);
+           continue;
+       }
+       else if (istk->mstk && !istk->mstk->in_progress)
+       {
+           /*
+            * We're in a %rep block which has been terminated, so
+            * we're walking through to the %endrep without
+            * emitting anything. Emit nothing at all, not even a
+            * blank line: when we emerge from the %rep block we'll
+            * give a line-number directive so we keep our place
+            * correctly.
+            */
+           free_tlist(tline);
+           continue;
+       }
+       else
+       {
+           tline = expand_smacro(tline);
+           if (!expand_mmacro(tline))
+           {
+               /*
+                * De-tokenise the line again, and emit it.
+                */
+               line = detoken(tline, TRUE);
+               free_tlist(tline);
+               break;
+           }
+           else
+           {
+               continue;       /* expand_mmacro calls free_tlist */
+           }
+       }
+    }
+
+    return line;
+}
+
+static void
+pp_cleanup(int pass)
+{
+    int h;
+
+    if (defining)
+    {
+       error(ERR_NONFATAL, "end of file while still defining macro `%s'",
+               defining->name);
+       free_mmacro(defining);
+    }
+    while (cstk)
+       ctx_pop();
+    for (h = 0; h < NHASH; h++)
+    {
+       while (mmacros[h])
+       {
+           MMacro *m = mmacros[h];
+           mmacros[h] = mmacros[h]->next;
+           free_mmacro(m);
+       }
+       while (smacros[h])
+       {
+           SMacro *s = smacros[h];
+           smacros[h] = smacros[h]->next;
+           nasm_free(s->name);
+           free_tlist(s->expansion);
+           nasm_free(s);
+       }
+    }
+    while (istk)
+    {
+       Include *i = istk;
+       istk = istk->next;
+       fclose(i->fp);
+       nasm_free(i->fname);
+       nasm_free(i);
+    }
+    while (cstk)
+       ctx_pop();
+    if (pass == 0)
+       {
+               free_llist(predef);
+               delete_Blocks();
+       }
+}
+
+void
+pp_include_path(char *path)
+{
+    IncPath *i;
+
+    i = nasm_malloc(sizeof(IncPath));
+    i->path = nasm_strdup(path);
+    i->next = ipath;
+    ipath = i;
+}
+
+void
+pp_pre_include(char *fname)
+{
+    Token *inc, *space, *name;
+    Line *l;
+
+    name = new_Token(NULL, TOK_INTERNAL_STRING, fname, 0);
+    space = new_Token(name, TOK_WHITESPACE, NULL, 0);
+    inc = new_Token(space, TOK_PREPROC_ID, "%include", 0);
+
+    l = nasm_malloc(sizeof(Line));
+    l->next = predef;
+    l->first = inc;
+    l->finishes = FALSE;
+    predef = l;
+}
+
+void
+pp_pre_define(char *definition)
+{
+    Token *def, *space;
+    Line *l;
+    char *equals;
+
+    equals = strchr(definition, '=');
+    space = new_Token(NULL, TOK_WHITESPACE, NULL, 0);
+    def = new_Token(space, TOK_PREPROC_ID, "%define", 0);
+    if (equals)
+       *equals = ' ';
+    space->next = tokenise(definition);
+    if (equals)
+       *equals = '=';
+
+    l = nasm_malloc(sizeof(Line));
+    l->next = predef;
+    l->first = def;
+    l->finishes = FALSE;
+    predef = l;
+}
+
+void
+pp_pre_undefine(char *definition)
+{
+    Token *def, *space;
+    Line *l;
+
+    space = new_Token(NULL, TOK_WHITESPACE, NULL, 0);
+    def = new_Token(space, TOK_PREPROC_ID, "%undef", 0);
+
+    l = nasm_malloc(sizeof(Line));
+    l->next = predef;
+    l->first = def;
+    l->finishes = FALSE;
+    predef = l;
+}
+
+void
+pp_extra_stdmac(const char **macros)
+{
+    extrastdmac = macros;
+}
+
+static void
+make_tok_num(Token * tok, long val)
+{
+    char numbuf[20];
+    sprintf(numbuf, "%ld", val);
+    tok->text = nasm_strdup(numbuf);
+    tok->type = TOK_NUMBER;
+}
+
+Preproc nasmpp = {
+    pp_reset,
+    pp_getline,
+    pp_cleanup
+};
diff --git a/modules/preprocs/nasm/nasm-pp.h b/modules/preprocs/nasm/nasm-pp.h
new file mode 100644 (file)
index 0000000..0b7df11
--- /dev/null
@@ -0,0 +1,20 @@
+/* preproc.h  header file for preproc.c
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ */
+
+#ifndef NASM_PREPROC_H
+#define NASM_PREPROC_H
+
+void pp_include_path (char *);
+void pp_pre_include (char *);
+void pp_pre_define (char *);
+void pp_pre_undefine (char *);
+void pp_extra_stdmac (const char **);
+
+extern Preproc nasmpp;
+
+#endif
diff --git a/modules/preprocs/nasm/nasm.h b/modules/preprocs/nasm/nasm.h
new file mode 100644 (file)
index 0000000..8f4f293
--- /dev/null
@@ -0,0 +1,850 @@
+/* nasm.h   main header file for the Netwide Assembler: inter-module interface
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ *
+ * initial version: 27/iii/95 by Simon Tatham
+ */
+
+#ifndef NASM_NASM_H
+#define NASM_NASM_H
+
+#include <stdio.h>
+#include "version.h"                  /* generated NASM version macros */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef FALSE
+#define FALSE 0                               /* comes in handy */
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define NO_SEG -1L                    /* null segment value */
+#define SEG_ABS 0x40000000L           /* mask for far-absolute segments */
+
+#ifndef FILENAME_MAX
+#define FILENAME_MAX 256
+#endif
+
+#ifndef PREFIX_MAX
+#define PREFIX_MAX 10
+#endif
+
+#ifndef POSTFIX_MAX
+#define POSTFIX_MAX 10
+#endif
+
+
+
+/*
+ * Name pollution problems: <time.h> on Digital UNIX pulls in some
+ * strange hardware header file which sees fit to define R_SP. We
+ * undefine it here so as not to break the enum below.
+ */
+#ifdef R_SP
+#undef R_SP
+#endif
+
+/*
+ * We must declare the existence of this structure type up here,
+ * since we have to reference it before we define it...
+ */
+struct ofmt;
+
+/*
+ * -------------------------
+ * Error reporting functions
+ * -------------------------
+ */
+
+/*
+ * An error reporting function should look like this.
+ */
+typedef void (*efunc) (int severity, const char *fmt, ...);
+
+/*
+ * These are the error severity codes which get passed as the first
+ * argument to an efunc.
+ */
+
+#define ERR_DEBUG      0x00000008      /* put out debugging message */
+#define ERR_WARNING    0x00000000      /* warn only: no further action */
+#define ERR_NONFATAL   0x00000001      /* terminate assembly after phase */
+#define ERR_FATAL      0x00000002      /* instantly fatal: exit with error */
+#define ERR_PANIC      0x00000003      /* internal error: panic instantly
+                                       * and dump core for reference */
+#define ERR_MASK       0x0000000F      /* mask off the above codes */
+#define ERR_NOFILE     0x00000010      /* don't give source file name/line */
+#define ERR_USAGE      0x00000020      /* print a usage message */
+#define ERR_PASS1      0x00000040      /* only print this error on pass one */
+
+/*
+ * These codes define specific types of suppressible warning.
+ */
+
+#define ERR_WARN_MASK  0x0000FF00      /* the mask for this feature */
+#define ERR_WARN_SHR  8                       /* how far to shift right */
+
+#define ERR_WARN_MNP   0x00000100      /* macro-num-parameters warning */
+#define ERR_WARN_MSR   0x00000200      /* macro self-reference */
+#define ERR_WARN_OL    0x00000300      /* orphan label (no colon, and
+                                       * alone on line) */
+#define ERR_WARN_NOV   0x00000400      /* numeric overflow */
+#define ERR_WARN_GNUELF        0x00000500      /* using GNU ELF extensions */
+#define ERR_WARN_MAX   5               /* the highest numbered one */
+
+/*
+ * -----------------------
+ * Other function typedefs
+ * -----------------------
+ */
+
+/*
+ * A label-lookup function should look like this.
+ */
+typedef int (*lfunc) (char *label, long *segment, long *offset);
+
+/*
+ * And a label-definition function like this. The boolean parameter
+ * `is_norm' states whether the label is a `normal' label (which
+ * should affect the local-label system), or something odder like
+ * an EQU or a segment-base symbol, which shouldn't.
+ */
+typedef void (*ldfunc) (char *label, long segment, long offset, char *special,
+                       int is_norm, int isextrn, struct ofmt *ofmt,
+                       efunc error);
+
+/*
+ * List-file generators should look like this:
+ */
+typedef struct {
+    /*
+     * Called to initialise the listing file generator. Before this
+     * is called, the other routines will silently do nothing when
+     * called. The `char *' parameter is the file name to write the
+     * listing to.
+     */
+    void (*init) (char *, efunc);
+
+    /*
+     * Called to clear stuff up and close the listing file.
+     */
+    void (*cleanup) (void);
+
+    /*
+     * Called to output binary data. Parameters are: the offset;
+     * the data; the data type. Data types are similar to the
+     * output-format interface, only OUT_ADDRESS will _always_ be
+     * displayed as if it's relocatable, so ensure that any non-
+     * relocatable address has been converted to OUT_RAWDATA by
+     * then. Note that OUT_RAWDATA+0 is a valid data type, and is a
+     * dummy call used to give the listing generator an offset to
+     * work with when doing things like uplevel(LIST_TIMES) or
+     * uplevel(LIST_INCBIN).
+     */
+    void (*output) (long, const void *, unsigned long);
+
+    /*
+     * Called to send a text line to the listing generator. The
+     * `int' parameter is LIST_READ or LIST_MACRO depending on
+     * whether the line came directly from an input file or is the
+     * result of a multi-line macro expansion.
+     */
+    void (*line) (int, char *);
+
+    /*
+     * Called to change one of the various levelled mechanisms in
+     * the listing generator. LIST_INCLUDE and LIST_MACRO can be
+     * used to increase the nesting level of include files and
+     * macro expansions; LIST_TIMES and LIST_INCBIN switch on the
+     * two binary-output-suppression mechanisms for large-scale
+     * pseudo-instructions.
+     *
+     * LIST_MACRO_NOLIST is synonymous with LIST_MACRO except that
+     * it indicates the beginning of the expansion of a `nolist'
+     * macro, so anything under that level won't be expanded unless
+     * it includes another file.
+     */
+    void (*uplevel) (int);
+
+    /*
+     * Reverse the effects of uplevel.
+     */
+    void (*downlevel) (int);
+} ListGen;
+
+/*
+ * The expression evaluator must be passed a scanner function; a
+ * standard scanner is provided as part of nasmlib.c. The
+ * preprocessor will use a different one. Scanners, and the
+ * token-value structures they return, look like this.
+ *
+ * The return value from the scanner is always a copy of the
+ * `t_type' field in the structure.
+ */
+struct tokenval {
+    int t_type;
+    long t_integer, t_inttwo;
+    char *t_charptr;
+};
+typedef int (*scanner) (void *private_data, struct tokenval *tv);
+
+/*
+ * Token types returned by the scanner, in addition to ordinary
+ * ASCII character values, and zero for end-of-string.
+ */
+enum {                                /* token types, other than chars */
+    TOKEN_INVALID = -1,                       /* a placeholder value */
+    TOKEN_EOS = 0,                    /* end of string */
+    TOKEN_EQ = '=', TOKEN_GT = '>', TOKEN_LT = '<',   /* aliases */
+    TOKEN_ID = 256, TOKEN_NUM, TOKEN_REG, TOKEN_INSN,  /* major token types */
+    TOKEN_ERRNUM,                     /* numeric constant with error in */
+    TOKEN_HERE, TOKEN_BASE,           /* $ and $$ */
+    TOKEN_SPECIAL,                    /* BYTE, WORD, DWORD, FAR, NEAR, etc */
+    TOKEN_PREFIX,                     /* A32, O16, LOCK, REPNZ, TIMES, etc */
+    TOKEN_SHL, TOKEN_SHR,             /* << and >> */
+    TOKEN_SDIV, TOKEN_SMOD,           /* // and %% */
+    TOKEN_GE, TOKEN_LE, TOKEN_NE,      /* >=, <= and <> (!= is same as <>) */
+    TOKEN_DBL_AND, TOKEN_DBL_OR, TOKEN_DBL_XOR,   /* &&, || and ^^ */
+    TOKEN_SEG, TOKEN_WRT,             /* SEG and WRT */
+    TOKEN_FLOAT                               /* floating-point constant */
+};
+
+typedef struct {
+    long segment;
+    long offset;
+    int  known;
+} loc_t;
+
+/*
+ * Expression-evaluator datatype. Expressions, within the
+ * evaluator, are stored as an array of these beasts, terminated by
+ * a record with type==0. Mostly, it's a vector type: each type
+ * denotes some kind of a component, and the value denotes the
+ * multiple of that component present in the expression. The
+ * exception is the WRT type, whose `value' field denotes the
+ * segment to which the expression is relative. These segments will
+ * be segment-base types, i.e. either odd segment values or SEG_ABS
+ * types. So it is still valid to assume that anything with a
+ * `value' field of zero is insignificant.
+ */
+typedef struct {
+    long type;                        /* a register, or EXPR_xxx */
+    long value;                               /* must be >= 32 bits */
+} expr;
+
+/*
+ * The evaluator can also return hints about which of two registers
+ * used in an expression should be the base register. See also the
+ * `operand' structure.
+ */
+struct eval_hints {
+    int base;
+    int type;
+};
+
+/*
+ * The actual expression evaluator function looks like this. When
+ * called, it expects the first token of its expression to already
+ * be in `*tv'; if it is not, set tv->t_type to TOKEN_INVALID and
+ * it will start by calling the scanner.
+ *
+ * If a forward reference happens during evaluation, the evaluator
+ * must set `*fwref' to TRUE if `fwref' is non-NULL.
+ *
+ * `critical' is non-zero if the expression may not contain forward
+ * references. The evaluator will report its own error if this
+ * occurs; if `critical' is 1, the error will be "symbol not
+ * defined before use", whereas if `critical' is 2, the error will
+ * be "symbol undefined".
+ *
+ * If `critical' has bit 8 set (in addition to its main value: 0x101
+ * and 0x102 correspond to 1 and 2) then an extended expression
+ * syntax is recognised, in which relational operators such as =, <
+ * and >= are accepted, as well as low-precedence logical operators
+ * &&, ^^ and ||.
+ *
+ * If `hints' is non-NULL, it gets filled in with some hints as to
+ * the base register in complex effective addresses.
+ */
+#define CRITICAL 0x100
+typedef expr *(*evalfunc) (scanner sc, void *scprivate, struct tokenval *tv,
+                          int *fwref, int critical, efunc error,
+                          struct eval_hints *hints);
+
+/*
+ * Special values for expr->type. ASSUMPTION MADE HERE: the number
+ * of distinct register names (i.e. possible "type" fields for an
+ * expr structure) does not exceed 124 (EXPR_REG_START through
+ * EXPR_REG_END).
+ */
+#define EXPR_REG_START 1
+#define EXPR_REG_END 124
+#define EXPR_UNKNOWN 125L             /* for forward references */
+#define EXPR_SIMPLE 126L
+#define EXPR_WRT 127L
+#define EXPR_SEGBASE 128L
+
+/*
+ * Preprocessors ought to look like this:
+ */
+typedef struct {
+    /*
+     * Called at the start of a pass; given a file name, the number
+     * of the pass, an error reporting function, an evaluator
+     * function, and a listing generator to talk to.
+     */
+    void (*reset) (char *, int, efunc, evalfunc, ListGen *);
+
+    /*
+     * Called to fetch a line of preprocessed source. The line
+     * returned has been malloc'ed, and so should be freed after
+     * use.
+     */
+    char *(*getline) (void);
+
+    /*
+     * Called at the end of a pass.
+     */
+    void (*cleanup) (int);
+} Preproc;
+
+/*
+ * ----------------------------------------------------------------
+ * Some lexical properties of the NASM source language, included
+ * here because they are shared between the parser and preprocessor
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * isidstart matches any character that may start an identifier, and isidchar
+ * matches any character that may appear at places other than the start of an
+ * identifier. E.g. a period may only appear at the start of an identifier
+ * (for local labels), whereas a number may appear anywhere *but* at the
+ * start. 
+ */
+
+#define isidstart(c) ( isalpha(c) || (c)=='_' || (c)=='.' || (c)=='?' \
+                                  || (c)=='@' )
+#define isidchar(c)  ( isidstart(c) || isdigit(c) || (c)=='$' || (c)=='#' \
+                                                  || (c)=='~' )
+
+/* Ditto for numeric constants. */
+
+#define isnumstart(c)  ( isdigit(c) || (c)=='$' )
+#define isnumchar(c)   ( isalnum(c) )
+
+/* This returns the numeric value of a given 'digit'. */
+
+#define numvalue(c)  ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0')
+
+/*
+ * Data-type flags that get passed to listing-file routines.
+ */
+enum {
+    LIST_READ, LIST_MACRO, LIST_MACRO_NOLIST, LIST_INCLUDE,
+    LIST_INCBIN, LIST_TIMES
+};
+
+/*
+ * -----------------------------------------------------------
+ * Format of the `insn' structure returned from `parser.c' and
+ * passed into `assemble.c'
+ * -----------------------------------------------------------
+ */
+
+/*
+ * Here we define the operand types. These are implemented as bit
+ * masks, since some are subsets of others; e.g. AX in a MOV
+ * instruction is a special operand type, whereas AX in other
+ * contexts is just another 16-bit register. (Also, consider CL in
+ * shift instructions, DX in OUT, etc.)
+ */
+
+/* size, and other attributes, of the operand */
+#define BITS8     0x00000001L
+#define BITS16    0x00000002L
+#define BITS32    0x00000004L
+#define BITS64    0x00000008L         /* FPU only */
+#define BITS80    0x00000010L         /* FPU only */
+#define FAR       0x00000020L         /* grotty: this means 16:16 or */
+                                      /* 16:32, like in CALL/JMP */
+#define NEAR      0x00000040L
+#define SHORT     0x00000080L         /* and this means what it says :) */
+
+#define SIZE_MASK 0x000000FFL         /* all the size attributes */
+#define NON_SIZE  (~SIZE_MASK)
+
+#define TO        0x00000100L          /* reverse effect in FADD, FSUB &c */
+#define COLON     0x00000200L         /* operand is followed by a colon */
+#define STRICT    0x00000400L         /* do not optimize this operand */
+
+/* type of operand: memory reference, register, etc. */
+#define MEMORY    0x00204000L
+#define REGISTER  0x00001000L         /* register number in 'basereg' */
+#define IMMEDIATE 0x00002000L
+
+#define REGMEM    0x00200000L         /* for r/m, ie EA, operands */
+#define REGNORM   0x00201000L         /* 'normal' reg, qualifies as EA */
+#define REG8      0x00201001L
+#define REG16     0x00201002L
+#define REG32     0x00201004L
+#define MMXREG    0x00201008L         /* MMX registers */
+#define XMMREG    0x00201010L          /* XMM Katmai reg */
+#define FPUREG    0x01000000L         /* floating point stack registers */
+#define FPU0      0x01000800L         /* FPU stack register zero */
+
+/* special register operands: these may be treated differently */
+#define REG_SMASK 0x00070000L         /* a mask for the following */
+#define REG_ACCUM 0x00211000L         /* accumulator: AL, AX or EAX */
+#define REG_AL    0x00211001L         /* REG_ACCUM | BITSxx */
+#define REG_AX    0x00211002L         /* ditto */
+#define REG_EAX   0x00211004L         /* and again */
+#define REG_COUNT 0x00221000L         /* counter: CL, CX or ECX */
+#define REG_CL    0x00221001L         /* REG_COUNT | BITSxx */
+#define REG_CX    0x00221002L         /* ditto */
+#define REG_ECX   0x00221004L         /* another one */
+#define REG_DX    0x00241002L
+#define REG_SREG  0x00081002L         /* any segment register */
+#define REG_CS    0x01081002L         /* CS */
+#define REG_DESS  0x02081002L         /* DS, ES, SS (non-CS 86 registers) */
+#define REG_FSGS  0x04081002L         /* FS, GS (386 extended registers) */
+#define REG_SEG67 0x08081002L          /* Non-implemented segment registers */
+#define REG_CDT   0x00101004L         /* CRn, DRn and TRn */
+#define REG_CREG  0x08101004L         /* CRn */
+#define REG_DREG  0x10101004L         /* DRn */
+#define REG_TREG  0x20101004L         /* TRn */
+
+/* special type of EA */
+#define MEM_OFFS  0x00604000L         /* simple [address] offset */
+
+/* special type of immediate operand */
+#define ONENESS   0x00800000L          /* so UNITY == IMMEDIATE | ONENESS */
+#define UNITY     0x00802000L         /* for shift/rotate instructions */
+#define BYTENESS  0x40000000L          /* so SBYTE == IMMEDIATE | BYTENESS */
+#define SBYTE    0x40002000L          /* for op r16/32,immediate instrs. */
+               
+/* Register names automatically generated from regs.dat */
+#include "regs.h"
+
+enum {                                /* condition code names */
+    C_A, C_AE, C_B, C_BE, C_C, C_E, C_G, C_GE, C_L, C_LE, C_NA, C_NAE,
+    C_NB, C_NBE, C_NC, C_NE, C_NG, C_NGE, C_NL, C_NLE, C_NO, C_NP,
+    C_NS, C_NZ, C_O, C_P, C_PE, C_PO, C_S, C_Z
+};
+
+/*
+ * Note that because segment registers may be used as instruction
+ * prefixes, we must ensure the enumerations for prefixes and
+ * register names do not overlap.
+ */
+enum {                                /* instruction prefixes */
+    PREFIX_ENUM_START = REG_ENUM_LIMIT,
+    P_A16 = PREFIX_ENUM_START, P_A32, P_LOCK, P_O16, P_O32, P_REP, P_REPE,
+    P_REPNE, P_REPNZ, P_REPZ, P_TIMES
+};
+
+enum {                                /* extended operand types */
+    EOT_NOTHING, EOT_DB_STRING, EOT_DB_NUMBER
+};
+
+enum {                                /* special EA flags */
+    EAF_BYTEOFFS = 1,                 /* force offset part to byte size */
+    EAF_WORDOFFS = 2,                 /* force offset part to [d]word size */
+    EAF_TIMESTWO = 4                  /* really do EAX*2 not EAX+EAX */
+};
+
+enum {                                /* values for `hinttype' */
+    EAH_NOHINT = 0,                   /* no hint at all - our discretion */
+    EAH_MAKEBASE = 1,                 /* try to make given reg the base */
+    EAH_NOTBASE = 2                   /* try _not_ to make reg the base */
+};
+
+typedef struct {                      /* operand to an instruction */
+    long type;                        /* type of operand */
+    int addr_size;                    /* 0 means default; 16; 32 */
+    int basereg, indexreg, scale;      /* registers and scale involved */
+    int hintbase, hinttype;           /* hint as to real base register */
+    long segment;                     /* immediate segment, if needed */
+    long offset;                      /* any immediate number */
+    long wrt;                         /* segment base it's relative to */
+    int eaflags;                      /* special EA flags */
+    int opflags;                      /* see OPFLAG_* defines below */
+} operand;
+
+#define OPFLAG_FORWARD         1      /* operand is a forward reference */
+#define OPFLAG_EXTERN          2      /* operand is an external reference */
+
+typedef struct extop {                /* extended operand */
+    struct extop *next;                       /* linked list */
+    long type;                        /* defined above */
+    char *stringval;                  /* if it's a string, then here it is */
+    int stringlen;                    /* ... and here's how long it is */
+    long segment;                     /* if it's a number/address, then... */
+    long offset;                      /* ... it's given here ... */
+    long wrt;                         /* ... and here */
+} extop;
+
+#define MAXPREFIX 4
+
+typedef struct {                      /* an instruction itself */
+    char *label;                      /* the label defined, or NULL */
+    int prefixes[MAXPREFIX];          /* instruction prefixes, if any */
+    int nprefix;                      /* number of entries in above */
+    int opcode;                               /* the opcode - not just the string */
+    int condition;                    /* the condition code, if Jcc/SETcc */
+    int operands;                     /* how many operands? 0-3 
+                                        * (more if db et al) */
+    operand oprs[3];                  /* the operands, defined as above */
+    extop *eops;                      /* extended operands */
+    int eops_float;                    /* true if DD and floating */
+    long times;                               /* repeat count (TIMES prefix) */
+    int forw_ref;                     /* is there a forward reference? */
+} insn;
+
+enum geninfo { GI_SWITCH };
+/*
+ * ------------------------------------------------------------
+ * The data structure defining an output format driver, and the
+ * interfaces to the functions therein.
+ * ------------------------------------------------------------
+ */
+
+struct ofmt {
+    /*
+     * This is a short (one-liner) description of the type of
+     * output generated by the driver.
+     */
+    const char *fullname;
+
+    /*
+     * This is a single keyword used to select the driver.
+     */
+    const char *shortname;
+
+    /*
+     * this is reserved for out module specific help.
+     * It is set to NULL in all the out modules but is not implemented
+     * in the main program
+     */
+    const char *helpstring;
+
+    /*
+     * this is a pointer to the first element of the debug information
+     */
+    struct dfmt **debug_formats;
+
+    /*
+     * and a pointer to the element that is being used
+     * note: this is set to the default at compile time and changed if the
+     * -F option is selected.  If developing a set of new debug formats for
+     * an output format, be sure to set this to whatever default you want
+     *
+     */
+    struct dfmt *current_dfmt;
+
+    /*
+     * This, if non-NULL, is a NULL-terminated list of `char *'s
+     * pointing to extra standard macros supplied by the object
+     * format (e.g. a sensible initial default value of __SECT__,
+     * and user-level equivalents for any format-specific
+     * directives).
+     */
+    const char **stdmac;
+
+    /*
+     * This procedure is called at the start of an output session.
+     * It tells the output format what file it will be writing to,
+     * what routine to report errors through, and how to interface
+     * to the label manager and expression evaluator if necessary.
+     * It also gives it a chance to do other initialisation.
+     */
+    void (*init) (FILE *fp, efunc error, ldfunc ldef, evalfunc eval);
+
+    /*
+     * This procedure is called to pass generic information to the
+     * object file.  The first parameter gives the information type
+     * (currently only command line switches)
+     * and the second parameter gives the value.  This function returns
+     * 1 if recognized, 0 if unrecognized
+     */
+    int (*setinfo)(enum geninfo type, char **string);
+
+    /*
+     * This procedure is called by assemble() to write actual
+     * generated code or data to the object file. Typically it
+     * doesn't have to actually _write_ it, just store it for
+     * later.
+     *
+     * The `type' argument specifies the type of output data, and
+     * usually the size as well: its contents are described below.
+     */
+    void (*output) (long segto, const void *data, unsigned long type,
+                   long segment, long wrt);
+
+    /*
+     * This procedure is called once for every symbol defined in
+     * the module being assembled. It gives the name and value of
+     * the symbol, in NASM's terms, and indicates whether it has
+     * been declared to be global. Note that the parameter "name",
+     * when passed, will point to a piece of static storage
+     * allocated inside the label manager - it's safe to keep using
+     * that pointer, because the label manager doesn't clean up
+     * until after the output driver has.
+     *
+     * Values of `is_global' are: 0 means the symbol is local; 1
+     * means the symbol is global; 2 means the symbol is common (in
+     * which case `offset' holds the _size_ of the variable).
+     * Anything else is available for the output driver to use
+     * internally.
+     *
+     * This routine explicitly _is_ allowed to call the label
+     * manager to define further symbols, if it wants to, even
+     * though it's been called _from_ the label manager. That much
+     * re-entrancy is guaranteed in the label manager. However, the
+     * label manager will in turn call this routine, so it should
+     * be prepared to be re-entrant itself.
+     *
+     * The `special' parameter contains special information passed
+     * through from the command that defined the label: it may have
+     * been an EXTERN, a COMMON or a GLOBAL. The distinction should
+     * be obvious to the output format from the other parameters.
+     */
+    void (*symdef) (char *name, long segment, long offset, int is_global,
+                   char *special);
+
+    /*
+     * This procedure is called when the source code requests a
+     * segment change. It should return the corresponding segment
+     * _number_ for the name, or NO_SEG if the name is not a valid
+     * segment name.
+     *
+     * It may also be called with NULL, in which case it is to
+     * return the _default_ section number for starting assembly in.
+     *
+     * It is allowed to modify the string it is given a pointer to.
+     *
+     * It is also allowed to specify a default instruction size for
+     * the segment, by setting `*bits' to 16 or 32. Or, if it
+     * doesn't wish to define a default, it can leave `bits' alone.
+     */
+    long (*section) (char *name, int pass, int *bits);
+
+    /*
+     * This procedure is called to modify the segment base values
+     * returned from the SEG operator. It is given a segment base
+     * value (i.e. a segment value with the low bit set), and is
+     * required to produce in return a segment value which may be
+     * different. It can map segment bases to absolute numbers by
+     * means of returning SEG_ABS types.
+     *
+     * It should return NO_SEG if the segment base cannot be
+     * determined; the evaluator (which calls this routine) is
+     * responsible for throwing an error condition if that occurs
+     * in pass two or in a critical expression.
+     */
+    long (*segbase) (long segment);
+
+    /*
+     * This procedure is called to allow the output driver to
+     * process its own specific directives. When called, it has the
+     * directive word in `directive' and the parameter string in
+     * `value'. It is called in both assembly passes, and `pass'
+     * will be either 1 or 2.
+     *
+     * This procedure should return zero if it does not _recognise_
+     * the directive, so that the main program can report an error.
+     * If it recognises the directive but then has its own errors,
+     * it should report them itself and then return non-zero. It
+     * should also return non-zero if it correctly processes the
+     * directive.
+     */
+    int (*directive) (char *directive, char *value, int pass);
+
+    /*
+     * This procedure is called before anything else - even before
+     * the "init" routine - and is passed the name of the input
+     * file from which this output file is being generated. It
+     * should return its preferred name for the output file in
+     * `outname', if outname[0] is not '\0', and do nothing to
+     * `outname' otherwise. Since it is called before the driver is
+     * properly initialised, it has to be passed its error handler
+     * separately.
+     *
+     * This procedure may also take its own copy of the input file
+     * name for use in writing the output file: it is _guaranteed_
+     * that it will be called before the "init" routine.
+     *
+     * The parameter `outname' points to an area of storage
+     * guaranteed to be at least FILENAME_MAX in size.
+     */
+    void (*filename) (char *inname, char *outname, efunc error);
+
+    /*
+     * This procedure is called after assembly finishes, to allow
+     * the output driver to clean itself up and free its memory.
+     * Typically, it will also be the point at which the object
+     * file actually gets _written_.
+     *
+     * One thing the cleanup routine should always do is to close
+     * the output file pointer.
+     */
+    void (*cleanup) (int debuginfo);
+};
+
+/*
+ * values for the `type' parameter to an output function. Each one
+ * must have the actual number of _bytes_ added to it.
+ *
+ * Exceptions are OUT_RELxADR, which denote an x-byte relocation
+ * which will be a relative jump. For this we need to know the
+ * distance in bytes from the start of the relocated record until
+ * the end of the containing instruction. _This_ is what is stored
+ * in the size part of the parameter, in this case.
+ *
+ * Also OUT_RESERVE denotes reservation of N bytes of BSS space,
+ * and the contents of the "data" parameter is irrelevant.
+ *
+ * The "data" parameter for the output function points to a "long",
+ * containing the address in question, unless the type is
+ * OUT_RAWDATA, in which case it points to an "unsigned char"
+ * array.
+ */
+#define OUT_RAWDATA 0x00000000UL
+#define OUT_ADDRESS 0x10000000UL
+#define OUT_REL2ADR 0x20000000UL
+#define OUT_REL4ADR 0x30000000UL
+#define OUT_RESERVE 0x40000000UL
+#define OUT_TYPMASK 0xF0000000UL
+#define OUT_SIZMASK 0x0FFFFFFFUL
+
+/*
+ * ------------------------------------------------------------
+ * The data structure defining a debug format driver, and the
+ * interfaces to the functions therein.
+ * ------------------------------------------------------------
+ */
+
+struct dfmt {
+    
+    /*
+     * This is a short (one-liner) description of the type of
+     * output generated by the driver.
+     */
+    const char *fullname;
+
+    /*
+     * This is a single keyword used to select the driver.
+     */
+    const char *shortname;
+
+
+    /*
+     * init - called initially to set up local pointer to object format, 
+     * void pointer to implementation defined data, file pointer (which
+     * probably won't be used, but who knows?), and error function.
+     */
+    void (*init) (struct ofmt * of, void * id, FILE * fp, efunc error);
+
+    /*
+     * linenum - called any time there is output with a change of
+     * line number or file.
+     */
+    void (*linenum) (const char * filename, long linenumber, long segto);
+
+    /*
+     * debug_deflabel - called whenever a label is defined. Parameters
+     * are the same as to 'symdef()' in the output format. This function
+     * would be called before the output format version.
+     */
+
+    void (*debug_deflabel) (char * name, long segment, long offset,
+                            int is_global, char * special);
+    /*
+     * debug_directive - called whenever a DEBUG directive other than 'LINE'
+     * is encountered. 'directive' contains the first parameter to the
+     * DEBUG directive, and params contains the rest. For example,
+     * 'DEBUG VAR _somevar:int' would translate to a call to this
+     * function with 'directive' equal to "VAR" and 'params' equal to 
+     * "_somevar:int".
+     */
+    void (*debug_directive) (const char * directive, const char * params);
+
+    /*
+     * typevalue - called whenever the assembler wishes to register a type
+     * for the last defined label.  This routine MUST detect if a type was
+     * already registered and not re-register it.
+     */
+    void (*debug_typevalue) (long type);
+
+    /*
+     * debug_output - called whenever output is required
+     * 'type' is the type of info required, and this is format-specific
+     */
+    void (*debug_output) (int type, void *param);
+
+    /*
+     * cleanup - called after processing of file is complete
+     */
+    void (*cleanup) (void);
+
+};
+/*
+ * The type definition macros
+ * for debugging
+ *
+ * low 3 bits: reserved
+ * next 5 bits: type
+ * next 24 bits: number of elements for arrays (0 for labels)
+ */
+
+#define TY_UNKNOWN 0x00
+#define TY_LABEL   0x08
+#define TY_BYTE    0x10
+#define TY_WORD    0x18
+#define TY_DWORD   0x20
+#define TY_FLOAT   0x28
+#define TY_QWORD   0x30
+#define TY_TBYTE   0x38
+#define TY_COMMON  0xE0
+#define TY_SEG     0xE8
+#define TY_EXTERN  0xF0
+#define TY_EQU     0xF8
+
+#define TYM_TYPE(x) ((x) & 0xF8)
+#define TYM_ELEMENTS(x) (((x) & 0xFFFFFF00) >> 8)
+
+#define TYS_ELEMENTS(x)  ((x) << 8)
+/*
+ * -----
+ * Other
+ * -----
+ */
+
+/*
+ * This is a useful #define which I keep meaning to use more often:
+ * the number of elements of a statically defined array.
+ */
+
+#define elements(x)     ( sizeof(x) / sizeof(*(x)) )
+
+extern int tasm_compatible_mode;
+
+/*
+ * This declaration passes the "pass" number to all other modules
+ * "pass0" assumes the values: 0, 0, ..., 0, 1, 2
+ * where 0 = optimizing pass
+ *       1 = pass 1
+ *       2 = pass 2
+ */
+
+extern int pass0;      /* this is globally known */
+extern int optimizing;
+
+#endif
diff --git a/modules/preprocs/nasm/nasmlib.c b/modules/preprocs/nasm/nasmlib.c
new file mode 100644 (file)
index 0000000..bb0ab29
--- /dev/null
@@ -0,0 +1,1116 @@
+/* nasmlib.c   library routines for the Netwide Assembler
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "nasm.h"
+#include "nasmlib.h"
+#include "insns.h"             /* For MAX_KEYWORD */
+
+static efunc nasm_malloc_error;
+
+#ifdef LOGALLOC
+static FILE *logfp;
+#endif
+
+void nasm_set_malloc_error (efunc error) 
+{
+    nasm_malloc_error = error;
+#ifdef LOGALLOC
+    logfp = fopen ("malloc.log", "w");
+    setvbuf (logfp, NULL, _IOLBF, BUFSIZ);
+    fprintf (logfp, "null pointer is %p\n", NULL);
+#endif
+}
+
+#ifdef LOGALLOC
+void *nasm_malloc_log (char *file, int line, size_t size)
+#else
+void *nasm_malloc (size_t size)
+#endif
+{
+    void *p = malloc(size);
+    if (!p)
+       nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
+#ifdef LOGALLOC
+    else
+       fprintf(logfp, "%s %d malloc(%ld) returns %p\n",
+               file, line, (long)size, p);
+#endif
+    return p;
+}
+
+#ifdef LOGALLOC
+void *nasm_realloc_log (char *file, int line, void *q, size_t size)
+#else
+void *nasm_realloc (void *q, size_t size)
+#endif
+{
+    void *p = q ? realloc(q, size) : malloc(size);
+    if (!p)
+       nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
+#ifdef LOGALLOC
+    else if (q)
+       fprintf(logfp, "%s %d realloc(%p,%ld) returns %p\n",
+               file, line, q, (long)size, p);
+    else
+       fprintf(logfp, "%s %d malloc(%ld) returns %p\n",
+               file, line, (long)size, p);
+#endif
+    return p;
+}
+
+#ifdef LOGALLOC
+void nasm_free_log (char *file, int line, void *q)
+#else
+void nasm_free (void *q)
+#endif
+{
+    if (q) {
+       free (q);
+#ifdef LOGALLOC
+       fprintf(logfp, "%s %d free(%p)\n",
+               file, line, q);
+#endif
+    }
+}
+
+#ifdef LOGALLOC
+char *nasm_strdup_log (char *file, int line, const char *s)
+#else
+char *nasm_strdup (const char *s)
+#endif
+{
+    char *p;
+    int size = strlen(s)+1;
+
+    p = malloc(size);
+    if (!p)
+       nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
+#ifdef LOGALLOC
+    else
+       fprintf(logfp, "%s %d strdup(%ld) returns %p\n",
+               file, line, (long)size, p);
+#endif
+    strcpy (p, s);
+    return p;
+}
+
+#ifdef LOGALLOC
+char *nasm_strndup_log (char *file, int line, char *s, size_t len)
+#else
+char *nasm_strndup (char *s, size_t len)
+#endif
+{
+    char *p;
+    int size = len+1;
+
+    p = malloc(size);
+    if (!p)
+       nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
+#ifdef LOGALLOC
+    else
+       fprintf(logfp, "%s %d strndup(%ld) returns %p\n",
+               file, line, (long)size, p);
+#endif
+    strncpy (p, s, len);
+    p[len] = '\0';
+    return p;
+}
+
+#if !defined(stricmp) && !defined(strcasecmp)
+int nasm_stricmp (const char *s1, const char *s2) 
+{
+    while (*s1 && tolower(*s1) == tolower(*s2))
+       s1++, s2++;
+    if (!*s1 && !*s2)
+       return 0;
+    else if (tolower(*s1) < tolower(*s2))
+       return -1;
+    else
+       return 1;
+}
+#endif
+
+#if !defined(strnicmp) && !defined(strncasecmp)
+int nasm_strnicmp (const char *s1, const char *s2, int n) 
+{
+    while (n > 0 && *s1 && tolower(*s1) == tolower(*s2))
+       s1++, s2++, n--;
+    if ((!*s1 && !*s2) || n==0)
+       return 0;
+    else if (tolower(*s1) < tolower(*s2))
+       return -1;
+    else
+       return 1;
+}
+#endif
+
+#define lib_isnumchar(c)   ( isalnum(c) || (c) == '$')
+#define numvalue(c)  ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0')
+
+long readnum (char *str, int *error) 
+{
+    char *r = str, *q;
+    long radix;
+    unsigned long result, checklimit;
+    int digit, last;
+    int warn = FALSE;
+    int sign = 1;
+
+    *error = FALSE;
+
+    while (isspace(*r)) r++;          /* find start of number */
+
+    /*
+     * If the number came from make_tok_num (as a result of an %assign), it
+     * might have a '-' built into it (rather than in a preceeding token).
+     */
+    if (*r == '-')
+    {
+       r++;
+       sign = -1;
+    }
+
+    q = r;
+
+    while (lib_isnumchar(*q)) q++;     /* find end of number */
+
+    /*
+     * If it begins 0x, 0X or $, or ends in H, it's in hex. if it
+     * ends in Q, it's octal. if it ends in B, it's binary.
+     * Otherwise, it's ordinary decimal.
+     */
+    if (*r=='0' && (r[1]=='x' || r[1]=='X'))
+       radix = 16, r += 2;
+    else if (*r=='$')
+       radix = 16, r++;
+    else if (q[-1]=='H' || q[-1]=='h')
+       radix = 16 , q--;
+    else if (q[-1]=='Q' || q[-1]=='q')
+       radix = 8 , q--;
+    else if (q[-1]=='B' || q[-1]=='b')
+       radix = 2 , q--;
+    else
+       radix = 10;
+
+    /*
+     * If this number has been found for us by something other than
+     * the ordinary scanners, then it might be malformed by having
+     * nothing between the prefix and the suffix. Check this case
+     * now.
+     */
+    if (r >= q) {
+       *error = TRUE;
+       return 0;
+    }
+
+    /*
+     * `checklimit' must be 2**32 / radix. We can't do that in
+     * 32-bit arithmetic, which we're (probably) using, so we
+     * cheat: since we know that all radices we use are even, we
+     * can divide 2**31 by radix/2 instead.
+     */
+    checklimit = 0x80000000UL / (radix>>1);
+
+    /*
+     * Calculate the highest allowable value for the last digit
+     * of a 32 bit constant... in radix 10, it is 6, otherwise it is 0
+     */
+    last = (radix == 10 ? 6 : 0);
+
+    result = 0;
+    while (*r && r < q) {
+       if (*r<'0' || (*r>'9' && *r<'A') || (digit = numvalue(*r)) >= radix) 
+       {
+           *error = TRUE;
+           return 0;
+       }
+       if (result > checklimit ||
+           (result == checklimit && digit >= last))
+       {
+           warn = TRUE;
+       }
+
+       result = radix * result + digit;
+       r++;
+    }
+
+    if (warn)
+       nasm_malloc_error (ERR_WARNING | ERR_PASS1 | ERR_WARN_NOV,
+                          "numeric constant %s does not fit in 32 bits",
+                          str);
+
+    return result*sign;
+}
+
+long readstrnum (char *str, int length, int *warn) 
+{
+    long charconst = 0;
+    int i;
+
+    *warn = FALSE;
+
+    str += length;
+    for (i=0; i<length; i++) {
+       if (charconst & 0xff000000UL) {
+           *warn = TRUE;
+       }
+       charconst = (charconst<<8) + (unsigned char) *--str;
+    }
+    return charconst;
+}
+
+static long next_seg;
+
+void seg_init(void) 
+{
+    next_seg = 0;
+}
+
+long seg_alloc(void) 
+{
+    return (next_seg += 2) - 2;
+}
+
+void fwriteshort (int data, FILE *fp) 
+{
+    fputc ((int) (data & 255), fp);
+    fputc ((int) ((data >> 8) & 255), fp);
+}
+
+void fwritelong (long data, FILE *fp) 
+{
+    fputc ((int) (data & 255), fp);
+    fputc ((int) ((data >> 8) & 255), fp);
+    fputc ((int) ((data >> 16) & 255), fp);
+    fputc ((int) ((data >> 24) & 255), fp);
+}
+
+void standard_extension (char *inname, char *outname, char *extension,
+                        efunc error) 
+{
+    char *p, *q;
+
+    if (*outname)                     /* file name already exists, */
+       return;                        /* so do nothing */
+    q = inname;
+    p = outname;
+    while (*q) *p++ = *q++;           /* copy, and find end of string */
+    *p = '\0';                        /* terminate it */
+    while (p > outname && *--p != '.');/* find final period (or whatever) */
+    if (*p != '.') while (*p) p++;     /* go back to end if none found */
+    if (!strcmp(p, extension)) {       /* is the extension already there? */
+       if (*extension)
+           error(ERR_WARNING | ERR_NOFILE,
+                 "file name already ends in `%s': "
+                 "output will be in `nasm.out'",
+                 extension);
+       else
+           error(ERR_WARNING | ERR_NOFILE,
+                 "file name already has no extension: "
+                 "output will be in `nasm.out'");
+       strcpy(outname, "nasm.out");
+    } else
+       strcpy(p, extension);
+}
+
+#define LEAFSIZ (sizeof(RAA)-sizeof(RAA_UNION)+sizeof(RAA_LEAF))
+#define BRANCHSIZ (sizeof(RAA)-sizeof(RAA_UNION)+sizeof(RAA_BRANCH))
+
+#define LAYERSIZ(r) ( (r)->layers==0 ? RAA_BLKSIZE : RAA_LAYERSIZE )
+
+static struct RAA *real_raa_init (int layers) 
+{
+    struct RAA *r;
+    int i;
+
+    if (layers == 0) {
+       r = nasm_malloc (LEAFSIZ);
+       r->layers = 0;
+       memset (r->u.l.data, 0, sizeof(r->u.l.data));
+       r->stepsize = 1L;
+    } else {
+       r = nasm_malloc (BRANCHSIZ);
+       r->layers = layers;
+       for ( i = 0 ; i < RAA_LAYERSIZE ; i++ )
+         r->u.b.data[i] = NULL;
+       r->stepsize = RAA_BLKSIZE;
+       while (--layers)
+           r->stepsize *= RAA_LAYERSIZE;
+    }
+    return r;
+}
+
+struct RAA *raa_init (void) 
+{
+    return real_raa_init (0);
+}
+
+void raa_free (struct RAA *r) 
+{
+    if (r->layers == 0)
+       nasm_free (r);
+    else {
+       struct RAA **p;
+       for (p = r->u.b.data; p - r->u.b.data < RAA_LAYERSIZE; p++)
+           if (*p)
+               raa_free (*p);
+    }
+}
+
+long raa_read (struct RAA *r, long posn) 
+{
+    if (posn >= r->stepsize * LAYERSIZ(r))
+       return 0;               /* Return 0 for undefined entries */
+    while (r->layers > 0) {
+       ldiv_t l;
+       l = ldiv (posn, r->stepsize);
+       r = r->u.b.data[l.quot];
+       posn = l.rem;
+       if (!r)
+           return 0;           /* Return 0 for undefined entries */
+    }
+    return r->u.l.data[posn];
+}
+
+struct RAA *raa_write (struct RAA *r, long posn, long value) 
+{
+    struct RAA *result;
+
+    if (posn < 0)
+       nasm_malloc_error (ERR_PANIC, "negative position in raa_write");
+
+    while (r->stepsize * LAYERSIZ(r) <= posn) {
+       /*
+        * Must add a layer.
+        */
+       struct RAA *s;
+       int i;
+
+       s = nasm_malloc (BRANCHSIZ);
+       for ( i = 0 ; i < RAA_LAYERSIZE ; i++ )
+           s->u.b.data[i] = NULL;
+       s->layers = r->layers + 1;
+       s->stepsize = LAYERSIZ(r) * r->stepsize;
+       s->u.b.data[0] = r;
+       r = s;
+    }
+
+    result = r;
+
+    while (r->layers > 0) {
+       ldiv_t l;
+       struct RAA **s;
+       l = ldiv (posn, r->stepsize);
+       s = &r->u.b.data[l.quot];
+       if (!*s)
+           *s = real_raa_init (r->layers - 1);
+       r = *s;
+       posn = l.rem;
+    }
+
+    r->u.l.data[posn] = value;
+
+    return result;
+}
+
+#define SAA_MAXLEN 8192
+
+struct SAA *saa_init (long elem_len) 
+{
+    struct SAA *s;
+
+    if (elem_len > SAA_MAXLEN)
+       nasm_malloc_error (ERR_PANIC | ERR_NOFILE, "SAA with huge elements");
+
+    s = nasm_malloc (sizeof(struct SAA));
+    s->posn = s->start = 0L;
+    s->elem_len = elem_len;
+    s->length = SAA_MAXLEN - (SAA_MAXLEN % elem_len);
+    s->data = nasm_malloc (s->length);
+    s->next = NULL;
+    s->end = s;
+
+    return s;
+}
+
+void saa_free (struct SAA *s) 
+{
+    struct SAA *t;
+
+    while (s) {
+       t = s->next;
+       nasm_free (s->data);
+       nasm_free (s);
+       s = t;
+    }
+}
+
+void *saa_wstruct (struct SAA *s) 
+{
+    void *p;
+
+    if (s->end->length - s->end->posn < s->elem_len) {
+       s->end->next = nasm_malloc (sizeof(struct SAA));
+       s->end->next->start = s->end->start + s->end->posn;
+       s->end = s->end->next;
+       s->end->length = s->length;
+       s->end->next = NULL;
+       s->end->posn = 0L;
+       s->end->data = nasm_malloc (s->length);
+    }
+
+    p = s->end->data + s->end->posn;
+    s->end->posn += s->elem_len;
+    return p;
+}
+
+void saa_wbytes (struct SAA *s, const void *data, long len) 
+{
+    const char *d = data;
+
+    while (len > 0) {
+       long l = s->end->length - s->end->posn;
+       if (l > len)
+           l = len;
+       if (l > 0) {
+           if (d) {
+               memcpy (s->end->data + s->end->posn, d, l);
+               d += l;
+           } else
+               memset (s->end->data + s->end->posn, 0, l);
+           s->end->posn += l;
+           len -= l;
+       }
+       if (len > 0) {
+           s->end->next = nasm_malloc (sizeof(struct SAA));
+           s->end->next->start = s->end->start + s->end->posn;
+           s->end = s->end->next;
+           s->end->length = s->length;
+           s->end->next = NULL;
+           s->end->posn = 0L;
+           s->end->data = nasm_malloc (s->length);
+       }
+    }
+}
+
+void saa_rewind (struct SAA *s) 
+{
+    s->rptr = s;
+    s->rpos = 0L;
+}
+
+void *saa_rstruct (struct SAA *s) 
+{
+    void *p;
+
+    if (!s->rptr)
+       return NULL;
+
+    if (s->rptr->posn - s->rpos < s->elem_len) {
+       s->rptr = s->rptr->next;
+       if (!s->rptr)
+           return NULL;               /* end of array */
+       s->rpos = 0L;
+    }
+
+    p = s->rptr->data + s->rpos;
+    s->rpos += s->elem_len;
+    return p;
+}
+
+void *saa_rbytes (struct SAA *s, long *len) 
+{
+    void *p;
+
+    if (!s->rptr)
+       return NULL;
+
+    p = s->rptr->data + s->rpos;
+    *len = s->rptr->posn - s->rpos;
+    s->rptr = s->rptr->next;
+    s->rpos = 0L;
+    return p;
+}
+
+void saa_rnbytes (struct SAA *s, void *data, long len) 
+{
+    char *d = data;
+
+    while (len > 0) {
+       long l;
+
+       if (!s->rptr)
+           return;
+
+       l = s->rptr->posn - s->rpos;
+       if (l > len)
+           l = len;
+       if (l > 0) {
+           memcpy (d, s->rptr->data + s->rpos, l);
+           d += l;
+           s->rpos += l;
+           len -= l;
+       }
+       if (len > 0) {
+           s->rptr = s->rptr->next;
+           s->rpos = 0L;
+       }
+    }
+}
+
+void saa_fread (struct SAA *s, long posn, void *data, long len) 
+{
+    struct SAA *p;
+    long pos;
+    char *cdata = data;
+
+    if (!s->rptr || posn < s->rptr->start)
+       saa_rewind (s);
+    p = s->rptr;
+    while (posn >= p->start + p->posn) {
+       p = p->next;
+       if (!p)
+           return;                    /* what else can we do?! */
+    }
+
+    pos = posn - p->start;
+    while (len) {
+       long l = p->posn - pos;
+       if (l > len)
+           l = len;
+       memcpy (cdata, p->data+pos, l);
+       len -= l;
+       cdata += l;
+       p = p->next;
+       if (!p)
+           return;
+       pos = 0L;
+    }
+    s->rptr = p;
+}
+
+void saa_fwrite (struct SAA *s, long posn, void *data, long len) 
+{
+    struct SAA *p;
+    long pos;
+    char *cdata = data;
+
+    if (!s->rptr || posn < s->rptr->start)
+       saa_rewind (s);
+    p = s->rptr;
+    while (posn >= p->start + p->posn) {
+       p = p->next;
+       if (!p)
+           return;                    /* what else can we do?! */
+    }
+
+    pos = posn - p->start;
+    while (len) {
+       long l = p->posn - pos;
+       if (l > len)
+           l = len;
+       memcpy (p->data+pos, cdata, l);
+       len -= l;
+       cdata += l;
+       p = p->next;
+       if (!p)
+           return;
+       pos = 0L;
+    }
+    s->rptr = p;
+}
+
+void saa_fpwrite (struct SAA *s, FILE *fp) 
+{
+    char *data;
+    long len;
+
+    saa_rewind (s);
+    while ( (data = saa_rbytes (s, &len)) )
+       fwrite (data, 1, len, fp);
+}
+
+/*
+ * Register, instruction, condition-code and prefix keywords used
+ * by the scanner.
+ */
+#include "names.c"
+static const char *special_names[] = {
+    "byte", "dword", "far", "long", "near", "nosplit", "qword",
+    "short", "strict", "to", "tword", "word"
+};
+static const char *prefix_names[] = {
+    "a16", "a32", "lock", "o16", "o32", "rep", "repe", "repne",
+    "repnz", "repz", "times"
+};
+
+
+/*
+ * Standard scanner routine used by parser.c and some output
+ * formats. It keeps a succession of temporary-storage strings in
+ * stdscan_tempstorage, which can be cleared using stdscan_reset.
+ */
+static char **stdscan_tempstorage = NULL;
+static int stdscan_tempsize = 0, stdscan_templen = 0;
+#define STDSCAN_TEMP_DELTA 256
+
+static void stdscan_pop(void) 
+{
+    nasm_free (stdscan_tempstorage[--stdscan_templen]);
+}
+
+void stdscan_reset(void) 
+{
+    while (stdscan_templen > 0)
+       stdscan_pop();
+}
+
+/*
+ * Unimportant cleanup is done to avoid confusing people who are trying
+ * to debug real memory leaks
+ */
+void nasmlib_cleanup (void) 
+{
+    stdscan_reset();
+    nasm_free (stdscan_tempstorage);
+}
+
+static char *stdscan_copy(char *p, int len) 
+{
+    char *text;
+
+    text = nasm_malloc(len+1);
+    strncpy (text, p, len);
+    text[len] = '\0';
+
+    if (stdscan_templen >= stdscan_tempsize) {
+       stdscan_tempsize += STDSCAN_TEMP_DELTA;
+       stdscan_tempstorage = nasm_realloc(stdscan_tempstorage,
+                                          stdscan_tempsize*sizeof(char *));
+    }
+    stdscan_tempstorage[stdscan_templen++] = text;
+
+    return text;
+}
+
+char *stdscan_bufptr = NULL;
+int stdscan (void *private_data, struct tokenval *tv) 
+{
+    char ourcopy[MAX_KEYWORD+1], *r, *s;
+
+    (void) private_data;  /* Don't warn that this parameter is unused */
+
+    while (isspace(*stdscan_bufptr)) stdscan_bufptr++;
+    if (!*stdscan_bufptr)
+       return tv->t_type = 0;
+
+    /* we have a token; either an id, a number or a char */
+    if (isidstart(*stdscan_bufptr) ||
+       (*stdscan_bufptr == '$' && isidstart(stdscan_bufptr[1]))) {
+       /* now we've got an identifier */
+       int i;
+       int is_sym = FALSE;
+
+       if (*stdscan_bufptr == '$') {
+           is_sym = TRUE;
+           stdscan_bufptr++;
+       }
+
+       r = stdscan_bufptr++;
+       while (isidchar(*stdscan_bufptr)) stdscan_bufptr++;
+       tv->t_charptr = stdscan_copy(r, stdscan_bufptr - r);
+
+       if (is_sym || stdscan_bufptr-r > MAX_KEYWORD)
+           return tv->t_type = TOKEN_ID;/* bypass all other checks */
+    
+       for (s=tv->t_charptr, r=ourcopy; *s; s++)
+           *r++ = tolower (*s);
+       *r = '\0';
+       /* right, so we have an identifier sitting in temp storage. now,
+        * is it actually a register or instruction name, or what? */
+       if ((tv->t_integer=bsi(ourcopy, reg_names,
+                              elements(reg_names)))>=0) {
+           tv->t_integer += EXPR_REG_START;
+           return tv->t_type = TOKEN_REG;
+       } else if ((tv->t_integer=bsi(ourcopy, insn_names,
+                                     elements(insn_names)))>=0) {
+           return tv->t_type = TOKEN_INSN;
+       }
+       for (i=0; i<elements(icn); i++)
+           if (!strncmp(ourcopy, icn[i], strlen(icn[i]))) {
+               char *p = ourcopy + strlen(icn[i]);
+               tv->t_integer = ico[i];
+               if ((tv->t_inttwo=bsi(p, conditions,
+                                        elements(conditions)))>=0)
+                   return tv->t_type = TOKEN_INSN;
+           }
+       if ((tv->t_integer=bsi(ourcopy, prefix_names,
+                                 elements(prefix_names)))>=0) {
+           tv->t_integer += PREFIX_ENUM_START;
+           return tv->t_type = TOKEN_PREFIX;
+       }
+       if ((tv->t_integer=bsi(ourcopy, special_names,
+                                 elements(special_names)))>=0)
+           return tv->t_type = TOKEN_SPECIAL;
+       if (!nasm_stricmp(ourcopy, "seg"))
+           return tv->t_type = TOKEN_SEG;
+       if (!nasm_stricmp(ourcopy, "wrt"))
+           return tv->t_type = TOKEN_WRT;
+       return tv->t_type = TOKEN_ID;
+    } else if (*stdscan_bufptr == '$' && !isnumchar(stdscan_bufptr[1])) {
+       /*
+        * It's a $ sign with no following hex number; this must
+        * mean it's a Here token ($), evaluating to the current
+        * assembly location, or a Base token ($$), evaluating to
+        * the base of the current segment.
+        */
+       stdscan_bufptr++;
+       if (*stdscan_bufptr == '$') {
+           stdscan_bufptr++;
+           return tv->t_type = TOKEN_BASE;
+       }
+       return tv->t_type = TOKEN_HERE;
+    } else if (isnumstart(*stdscan_bufptr)) {  /* now we've got a number */
+       int rn_error;
+
+       r = stdscan_bufptr++;
+       while (isnumchar(*stdscan_bufptr))
+           stdscan_bufptr++;
+
+       if (*stdscan_bufptr == '.') {
+           /*
+            * a floating point constant
+            */
+           stdscan_bufptr++;
+           while (isnumchar(*stdscan_bufptr) ||
+                  ((stdscan_bufptr[-1] == 'e' || stdscan_bufptr[-1] == 'E')
+                   && (*stdscan_bufptr == '-' || *stdscan_bufptr == '+')) ) 
+           {
+               stdscan_bufptr++;
+           }
+           tv->t_charptr = stdscan_copy(r, stdscan_bufptr - r);
+           return tv->t_type = TOKEN_FLOAT;
+       }
+       r = stdscan_copy(r, stdscan_bufptr - r);
+       tv->t_integer = readnum(r, &rn_error);
+       stdscan_pop();
+       if (rn_error)
+           return tv->t_type = TOKEN_ERRNUM;/* some malformation occurred */
+       tv->t_charptr = NULL;
+       return tv->t_type = TOKEN_NUM;
+    } else if (*stdscan_bufptr == '\'' ||
+              *stdscan_bufptr == '"') {/* a char constant */
+       char quote = *stdscan_bufptr++, *r;
+       int rn_warn;
+       r = tv->t_charptr = stdscan_bufptr;
+       while (*stdscan_bufptr && *stdscan_bufptr != quote) stdscan_bufptr++;
+       tv->t_inttwo = stdscan_bufptr - r;      /* store full version */
+       if (!*stdscan_bufptr)
+           return tv->t_type = TOKEN_ERRNUM;       /* unmatched quotes */
+       stdscan_bufptr++;                       /* skip over final quote */
+       tv->t_integer = readstrnum(r, tv->t_inttwo, &rn_warn);
+       /* FIXME: rn_warn is not checked! */
+       return tv->t_type = TOKEN_NUM;
+    } else if (*stdscan_bufptr == ';') {  /* a comment has happened - stay */
+       return tv->t_type = 0;
+    } else if (stdscan_bufptr[0] == '>' && stdscan_bufptr[1] == '>') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_SHR;
+    } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '<') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_SHL;
+    } else if (stdscan_bufptr[0] == '/' && stdscan_bufptr[1] == '/') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_SDIV;
+    } else if (stdscan_bufptr[0] == '%' && stdscan_bufptr[1] == '%') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_SMOD;
+    } else if (stdscan_bufptr[0] == '=' && stdscan_bufptr[1] == '=') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_EQ;
+    } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '>') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_NE;
+    } else if (stdscan_bufptr[0] == '!' && stdscan_bufptr[1] == '=') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_NE;
+    } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '=') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_LE;
+    } else if (stdscan_bufptr[0] == '>' && stdscan_bufptr[1] == '=') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_GE;
+    } else if (stdscan_bufptr[0] == '&' && stdscan_bufptr[1] == '&') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_DBL_AND;
+    } else if (stdscan_bufptr[0] == '^' && stdscan_bufptr[1] == '^') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_DBL_XOR;
+    } else if (stdscan_bufptr[0] == '|' && stdscan_bufptr[1] == '|') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_DBL_OR;
+    } else                            /* just an ordinary char */
+       return tv->t_type = (unsigned char) (*stdscan_bufptr++);
+}
+
+/*
+ * Return TRUE if the argument is a simple scalar. (Or a far-
+ * absolute, which counts.)
+ */
+int is_simple (expr *vect) 
+{
+    while (vect->type && !vect->value)
+       vect++;
+    if (!vect->type)
+       return 1;
+    if (vect->type != EXPR_SIMPLE)
+       return 0;
+    do {
+       vect++;
+    } while (vect->type && !vect->value);
+    if (vect->type && vect->type < EXPR_SEGBASE+SEG_ABS) return 0;
+    return 1;
+}
+
+/*
+ * Return TRUE if the argument is a simple scalar, _NOT_ a far-
+ * absolute.
+ */
+int is_really_simple (expr *vect) 
+{
+    while (vect->type && !vect->value)
+       vect++;
+    if (!vect->type)
+       return 1;
+    if (vect->type != EXPR_SIMPLE)
+       return 0;
+    do {
+       vect++;
+    } while (vect->type && !vect->value);
+    if (vect->type) return 0;
+    return 1;
+}
+
+/*
+ * Return TRUE if the argument is relocatable (i.e. a simple
+ * scalar, plus at most one segment-base, plus possibly a WRT).
+ */
+int is_reloc (expr *vect) 
+{
+    while (vect->type && !vect->value) /* skip initial value-0 terms */
+       vect++;
+    if (!vect->type)                  /* trivially return TRUE if nothing */
+       return 1;                      /* is present apart from value-0s */
+    if (vect->type < EXPR_SIMPLE)      /* FALSE if a register is present */
+       return 0;
+    if (vect->type == EXPR_SIMPLE) {   /* skip over a pure number term... */
+       do {
+           vect++;
+       } while (vect->type && !vect->value);
+       if (!vect->type)               /* ...returning TRUE if that's all */
+           return 1;
+    }
+    if (vect->type == EXPR_WRT) {      /* skip over a WRT term... */
+       do {
+           vect++;
+       } while (vect->type && !vect->value);
+       if (!vect->type)               /* ...returning TRUE if that's all */
+           return 1;
+    }
+    if (vect->value != 0 && vect->value != 1)
+       return 0;                      /* segment base multiplier non-unity */
+    do {                              /* skip over _one_ seg-base term... */
+       vect++;
+    } while (vect->type && !vect->value);
+    if (!vect->type)                  /* ...returning TRUE if that's all */
+       return 1;
+    return 0;                         /* And return FALSE if there's more */
+}
+
+/*
+ * Return TRUE if the argument contains an `unknown' part.
+ */
+int is_unknown(expr *vect) 
+{
+    while (vect->type && vect->type < EXPR_UNKNOWN)
+       vect++;
+    return (vect->type == EXPR_UNKNOWN);
+}
+
+/*
+ * Return TRUE if the argument contains nothing but an `unknown'
+ * part.
+ */
+int is_just_unknown(expr *vect) 
+{
+    while (vect->type && !vect->value)
+       vect++;
+    return (vect->type == EXPR_UNKNOWN);
+}
+
+/*
+ * Return the scalar part of a relocatable vector. (Including
+ * simple scalar vectors - those qualify as relocatable.)
+ */
+long reloc_value (expr *vect) 
+{
+    while (vect->type && !vect->value)
+       vect++;
+    if (!vect->type) return 0;
+    if (vect->type == EXPR_SIMPLE)
+       return vect->value;
+    else
+       return 0;
+}
+
+/*
+ * Return the segment number of a relocatable vector, or NO_SEG for
+ * simple scalars.
+ */
+long reloc_seg (expr *vect) 
+{
+    while (vect->type && (vect->type == EXPR_WRT || !vect->value))
+       vect++;
+    if (vect->type == EXPR_SIMPLE) {
+       do {
+           vect++;
+       } while (vect->type && (vect->type == EXPR_WRT || !vect->value));
+    }
+    if (!vect->type)
+       return NO_SEG;
+    else
+       return vect->type - EXPR_SEGBASE;
+}
+
+/*
+ * Return the WRT segment number of a relocatable vector, or NO_SEG
+ * if no WRT part is present.
+ */
+long reloc_wrt (expr *vect) 
+{
+    while (vect->type && vect->type < EXPR_WRT)
+       vect++;
+    if (vect->type == EXPR_WRT) {
+       return vect->value;
+    } else
+       return NO_SEG;
+}
+
+/*
+ * Binary search.
+ */
+int bsi (char *string, const char **array, int size) 
+{
+    int i = -1, j = size;             /* always, i < index < j */
+    while (j-i >= 2) {
+       int k = (i+j)/2;
+       int l = strcmp(string, array[k]);
+       if (l<0)                       /* it's in the first half */
+           j = k;
+       else if (l>0)                  /* it's in the second half */
+           i = k;
+       else                           /* we've got it :) */
+           return k;
+    }
+    return -1;                        /* we haven't got it :( */
+}
+
+static char *file_name = NULL;
+static long line_number = 0;
+
+char *src_set_fname(char *newname) 
+{
+    char *oldname = file_name;
+    file_name = newname;
+    return oldname;
+}
+
+long src_set_linnum(long newline) 
+{
+    long oldline = line_number;
+    line_number = newline;
+    return oldline;
+}
+
+long src_get_linnum(void) 
+{
+    return line_number;
+}
+
+int src_get(long *xline, char **xname) 
+{
+    if (!file_name || !*xname || strcmp(*xname, file_name)) 
+    {
+       nasm_free(*xname);
+       *xname = file_name ? nasm_strdup(file_name) : NULL;
+       *xline = line_number;
+       return -2;
+    }
+    if (*xline != line_number) 
+    {
+       long tmp = line_number - *xline;
+       *xline = line_number;
+       return tmp;
+    }
+    return 0;
+}
+
+void nasm_quote(char **str) 
+{
+    int ln=strlen(*str);
+    char q=(*str)[0];
+    char *p;
+    if (ln>1 && (*str)[ln-1]==q && (q=='"' || q=='\''))
+       return;
+    q = '"';
+    if (strchr(*str,q))
+       q = '\'';
+    p = nasm_malloc(ln+3);
+    strcpy(p+1, *str);
+    nasm_free(*str);
+    p[ln+1] = p[0] = q;
+    p[ln+2] = 0;
+    *str = p;
+}
+    
+char *nasm_strcat(char *one, char *two) 
+{
+    char *rslt;
+    int l1=strlen(one);
+    rslt = nasm_malloc(l1+strlen(two)+1);
+    strcpy(rslt, one);
+    strcpy(rslt+l1, two);
+    return rslt;
+}
+
+void null_debug_init(struct ofmt *of, void *id, FILE *fp, efunc error ) {}
+void null_debug_linenum(const char *filename, long linenumber, long segto) {}
+void null_debug_deflabel(char *name, long segment, long offset, int is_global, char *special) {}
+void null_debug_routine(const char *directive, const char *params) {}
+void null_debug_typevalue(long type) {}
+void null_debug_output(int type, void *param) {}
+void null_debug_cleanup(void){}
+
+struct dfmt null_debug_form = {
+    "Null debug format",
+    "null",
+    null_debug_init,
+    null_debug_linenum,
+    null_debug_deflabel,
+    null_debug_routine,
+    null_debug_typevalue,
+    null_debug_output,
+    null_debug_cleanup
+};
+
+struct dfmt *null_debug_arr[2] = { &null_debug_form, NULL };
diff --git a/modules/preprocs/nasm/nasmlib.h b/modules/preprocs/nasm/nasmlib.h
new file mode 100644 (file)
index 0000000..5496453
--- /dev/null
@@ -0,0 +1,258 @@
+/* nasmlib.h   header file for nasmlib.c
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ */
+
+#ifndef NASM_NASMLIB_H
+#define NASM_NASMLIB_H
+
+/*
+ * If this is defined, the wrappers around malloc et al will
+ * transform into logging variants, which will cause NASM to create
+ * a file called `malloc.log' when run, and spew details of all its
+ * memory management into that. That can then be analysed to detect
+ * memory leaks and potentially other problems too.
+ */
+/* #define LOGALLOC */
+
+/*
+ * Wrappers around malloc, realloc and free. nasm_malloc will
+ * fatal-error and die rather than return NULL; nasm_realloc will
+ * do likewise, and will also guarantee to work right on being
+ * passed a NULL pointer; nasm_free will do nothing if it is passed
+ * a NULL pointer.
+ */
+#ifdef NASM_NASM_H                    /* need efunc defined for this */
+void nasm_set_malloc_error (efunc);
+#ifndef LOGALLOC
+void *nasm_malloc (size_t);
+void *nasm_realloc (void *, size_t);
+void nasm_free (void *);
+char *nasm_strdup (const char *);
+char *nasm_strndup (char *, size_t);
+#else
+void *nasm_malloc_log (char *, int, size_t);
+void *nasm_realloc_log (char *, int, void *, size_t);
+void nasm_free_log (char *, int, void *);
+char *nasm_strdup_log (char *, int, const char *);
+char *nasm_strndup_log (char *, int, char *, size_t);
+#define nasm_malloc(x) nasm_malloc_log(__FILE__,__LINE__,x)
+#define nasm_realloc(x,y) nasm_realloc_log(__FILE__,__LINE__,x,y)
+#define nasm_free(x) nasm_free_log(__FILE__,__LINE__,x)
+#define nasm_strdup(x) nasm_strdup_log(__FILE__,__LINE__,x)
+#define nasm_strndup(x,y) nasm_strndup_log(__FILE__,__LINE__,x,y)
+#endif
+#endif
+
+/*
+ * ANSI doesn't guarantee the presence of `stricmp' or
+ * `strcasecmp'.
+ */
+#if defined(stricmp) || defined(strcasecmp)
+#if defined(stricmp)
+#define nasm_stricmp stricmp
+#else
+#define nasm_stricmp strcasecmp
+#endif
+#else
+int nasm_stricmp (const char *, const char *);
+#endif
+
+#if defined(strnicmp) || defined(strncasecmp)
+#if defined(strnicmp)
+#define nasm_strnicmp strnicmp
+#else
+#define nasm_strnicmp strncasecmp
+#endif
+#else
+int nasm_strnicmp (const char *, const char *, int);
+#endif
+
+/*
+ * Convert a string into a number, using NASM number rules. Sets
+ * `*error' to TRUE if an error occurs, and FALSE otherwise.
+ */
+long readnum(char *str, int *error);
+
+/*
+ * Convert a character constant into a number. Sets
+ * `*warn' to TRUE if an overflow occurs, and FALSE otherwise.
+ * str points to and length covers the middle of the string,
+ * without the quotes.
+ */
+long readstrnum(char *str, int length, int *warn);
+
+/*
+ * seg_init: Initialise the segment-number allocator.
+ * seg_alloc: allocate a hitherto unused segment number.
+ */
+void seg_init(void);
+long seg_alloc(void);
+
+/*
+ * many output formats will be able to make use of this: a standard
+ * function to add an extension to the name of the input file
+ */
+#ifdef NASM_NASM_H
+void standard_extension (char *inname, char *outname, char *extension,
+                        efunc error);
+#endif
+
+/*
+ * some handy macros that will probably be of use in more than one
+ * output format: convert integers into little-endian byte packed
+ * format in memory
+ */
+
+#define WRITELONG(p,v) \
+  do { \
+    *(p)++ = (v) & 0xFF; \
+    *(p)++ = ((v) >> 8) & 0xFF; \
+    *(p)++ = ((v) >> 16) & 0xFF; \
+    *(p)++ = ((v) >> 24) & 0xFF; \
+  } while (0)
+
+#define WRITESHORT(p,v) \
+  do { \
+    *(p)++ = (v) & 0xFF; \
+    *(p)++ = ((v) >> 8) & 0xFF; \
+  } while (0)
+
+/*
+ * and routines to do the same thing to a file
+ */
+void fwriteshort (int data, FILE *fp);
+void fwritelong (long data, FILE *fp);
+
+/*
+ * Routines to manage a dynamic random access array of longs which
+ * may grow in size to be more than the largest single malloc'able
+ * chunk.
+ */
+
+#define RAA_BLKSIZE 4096              /* this many longs allocated at once */
+#define RAA_LAYERSIZE 1024            /* this many _pointers_ allocated */
+
+typedef struct RAA RAA;
+typedef union RAA_UNION RAA_UNION;
+typedef struct RAA_LEAF RAA_LEAF;
+typedef struct RAA_BRANCH RAA_BRANCH;
+
+struct RAA {
+    /*
+     * Number of layers below this one to get to the real data. 0
+     * means this structure is a leaf, holding RAA_BLKSIZE real
+     * data items; 1 and above mean it's a branch, holding
+     * RAA_LAYERSIZE pointers to the next level branch or leaf
+     * structures.
+     */
+    int layers;
+    /*
+     * Number of real data items spanned by one position in the
+     * `data' array at this level. This number is 1, trivially, for
+     * a leaf (level 0): for a level 1 branch it should be
+     * RAA_BLKSIZE, and for a level 2 branch it's
+     * RAA_LAYERSIZE*RAA_BLKSIZE.
+     */
+    long stepsize;
+    union RAA_UNION {
+       struct RAA_LEAF {
+           long data[RAA_BLKSIZE];
+       } l;
+       struct RAA_BRANCH {
+           struct RAA *data[RAA_LAYERSIZE];
+       } b;
+    } u;
+};
+
+
+struct RAA *raa_init (void);
+void raa_free (struct RAA *);
+long raa_read (struct RAA *, long);
+struct RAA *raa_write (struct RAA *r, long posn, long value);
+
+/*
+ * Routines to manage a dynamic sequential-access array, under the
+ * same restriction on maximum mallocable block. This array may be
+ * written to in two ways: a contiguous chunk can be reserved of a
+ * given size, and a pointer returned, or single-byte data may be
+ * written. The array can also be read back in the same two ways:
+ * as a series of big byte-data blocks or as a list of structures
+ * of a given size.
+ */
+
+struct SAA {
+    /*
+     * members `end' and `elem_len' are only valid in first link in
+     * list; `rptr' and `rpos' are used for reading
+     */
+    struct SAA *next, *end, *rptr;
+    long elem_len, length, posn, start, rpos;
+    char *data;
+};
+
+struct SAA *saa_init (long elem_len);  /* 1 == byte */
+void saa_free (struct SAA *);
+void *saa_wstruct (struct SAA *);      /* return a structure of elem_len */
+void saa_wbytes (struct SAA *, const void *, long);  /* write arbitrary bytes */
+void saa_rewind (struct SAA *);               /* for reading from beginning */
+void *saa_rstruct (struct SAA *);      /* return NULL on EOA */
+void *saa_rbytes (struct SAA *, long *);   /* return 0 on EOA */
+void saa_rnbytes (struct SAA *, void *, long); /* read a given no. of bytes */
+void saa_fread (struct SAA *s, long posn, void *p, long len);   /* fixup */
+void saa_fwrite (struct SAA *s, long posn, void *p, long len);   /* fixup */
+void saa_fpwrite (struct SAA *, FILE *);
+
+#ifdef NASM_NASM_H
+/*
+ * Standard scanner.
+ */
+extern char *stdscan_bufptr;
+void stdscan_reset(void);
+int stdscan (void *private_data, struct tokenval *tv);
+#endif
+
+#ifdef NASM_NASM_H
+/*
+ * Library routines to manipulate expression data types.
+ */
+int is_reloc(expr *);
+int is_simple(expr *);
+int is_really_simple (expr *);
+int is_unknown(expr *);
+int is_just_unknown(expr *);
+long reloc_value(expr *);
+long reloc_seg(expr *);
+long reloc_wrt(expr *);
+#endif
+
+/*
+ * Binary search routine. Returns index into `array' of an entry
+ * matching `string', or <0 if no match. `array' is taken to
+ * contain `size' elements.
+ */
+int bsi (char *string, const char **array, int size);
+
+
+char *src_set_fname(char *newname);
+long src_set_linnum(long newline);
+long src_get_linnum(void);
+/*
+ * src_get may be used if you simply want to know the source file and line.
+ * It is also used if you maintain private status about the source location
+ * It return 0 if the information was the same as the last time you
+ * checked, -1 if the name changed and (new-old) if just the line changed.
+ */
+int src_get(long *xline, char **xname);
+
+void nasm_quote(char **str);
+char *nasm_strcat(char *one, char *two);
+void nasmlib_cleanup(void);
+
+void null_debug_routine(const char *directive, const char *params);
+extern struct dfmt null_debug_form;
+extern struct dfmt *null_debug_arr[2];
+#endif
diff --git a/modules/preprocs/nasm/standard.mac b/modules/preprocs/nasm/standard.mac
new file mode 100644 (file)
index 0000000..bbbf90d
--- /dev/null
@@ -0,0 +1,110 @@
+; Standard macro set for NASM -*- nasm -*-
+
+; Macros to make NASM ignore some TASM directives before the first include
+; directive.
+
+    %idefine IDEAL
+    %idefine JUMPS
+    %idefine P386
+    %idefine P486
+    %idefine P586
+    %idefine END
+
+; This is a magic token which indicates the end of the TASM macros
+*END*TASM*MACROS*
+
+; Note that although some user-level forms of directives are defined
+; here, not all of them are: the user-level form of a format-specific
+; directive should be defined in the module for that directive.
+
+; These two need to be defined, though the actual definitions will
+; be constantly updated during preprocessing.
+%define __FILE__
+%define __LINE__
+
+%define __SECT__               ; it ought to be defined, even if as nothing
+
+%imacro section 1+.nolist
+%define __SECT__ [section %1]
+         __SECT__
+%endmacro
+%imacro segment 1+.nolist
+%define __SECT__ [segment %1]
+         __SECT__
+%endmacro
+
+%imacro absolute 1+.nolist
+%define __SECT__ [absolute %1]
+         __SECT__
+%endmacro
+
+%imacro struc 1.nolist
+%push struc
+%define %$strucname %1
+[absolute 0]
+%$strucname:                   ; allow definition of `.member' to work sanely
+%endmacro 
+%imacro endstruc 0.nolist
+%{$strucname}_size:
+%pop
+__SECT__
+%endmacro
+
+%imacro istruc 1.nolist
+%push istruc
+%define %$strucname %1
+%$strucstart:
+%endmacro
+%imacro at 1-2+.nolist
+         times %1-($-%$strucstart) db 0
+         %2
+%endmacro
+%imacro iend 0.nolist
+         times %{$strucname}_size-($-%$strucstart) db 0
+%pop
+%endmacro
+
+%imacro align 1-2+.nolist nop
+         times ($$-$) & ((%1)-1) %2
+%endmacro
+%imacro alignb 1-2+.nolist resb 1
+         times ($$-$) & ((%1)-1) %2
+%endmacro
+
+%imacro extern 1-*.nolist
+%rep %0
+[extern %1]
+%rotate 1
+%endrep
+%endmacro
+
+%imacro bits 1+.nolist
+[bits %1]
+%endmacro
+
+%imacro use16 0.nolist
+[bits 16]
+%endmacro
+%imacro use32 0.nolist
+[bits 32]
+%endmacro
+
+%imacro global 1-*.nolist
+%rep %0
+[global %1]
+%rotate 1
+%endrep
+%endmacro
+
+%imacro common 1-*.nolist
+%rep %0
+[common %1]
+%rotate 1
+%endrep
+%endmacro
+
+%imacro cpu 1+.nolist
+[cpu %1]
+%endmacro
+
+
diff --git a/src/preprocs/nasm/macros.pl b/src/preprocs/nasm/macros.pl
new file mode 100644 (file)
index 0000000..0934d17
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/perl -w
+# 
+# macros.pl   produce macros.c from standard.mac
+#
+# The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+# Julian Hall. All rights reserved. The software is
+# redistributable under the licence given in the file "Licence"
+# distributed in the NASM archive.
+
+use strict;
+
+my $fname;
+my $line = 0;
+my $index      = 0;
+my $tasm_count;
+
+undef $tasm_count;
+
+open(OUTPUT,">macros.c") or die "unable to open macros.c\n";
+    
+print OUTPUT "/* This file auto-generated from standard.mac by macros.pl" .
+" - don't edit it */\n\n#include <stddef.h>\n\nstatic const char *stdmac[] = {\n";
+    
+foreach $fname ( @ARGV ) {
+    open(INPUT,$fname) or die "unable to open $fname\n";
+    while (<INPUT>) {
+       $line++;
+       chomp;
+       if (m/^\s*\*END\*TASM\*MACROS\*\s*$/) {
+           $tasm_count = $index;
+       } elsif (m/^\s*((\s*([^\"\';\s]+|\"[^\"]*\"|\'[^\']*\'))*)\s*(;.*)?$/) {
+           $_ = $1;
+           s/\\/\\\\/g;
+           s/"/\\"/g;
+           if (length > 0) {
+               print OUTPUT "    \"$_\",\n";
+               $index++;
+           } 
+       } else {
+           die "$fname:$line:  error unterminated quote";
+       }
+    }
+    close(INPUT);
+}
+print OUTPUT "    NULL\n};\n";
+$tasm_count = $index unless ( defined($tasm_count) );
+print OUTPUT "#define TASM_MACRO_COUNT $tasm_count\n";
+close(OUTPUT);
diff --git a/src/preprocs/nasm/nasm-eval.c b/src/preprocs/nasm/nasm-eval.c
new file mode 100644 (file)
index 0000000..28aca64
--- /dev/null
@@ -0,0 +1,825 @@
+/* eval.c    expression evaluator for the Netwide Assembler
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ *
+ * initial version 27/iii/95 by Simon Tatham
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "nasm.h"
+#include "nasmlib.h"
+#include "eval.h"
+#include "labels.h"
+
+#define TEMPEXPRS_DELTA 128
+#define TEMPEXPR_DELTA 8
+
+static scanner scan;   /* Address of scanner routine */
+static efunc error;    /* Address of error reporting routine */
+static lfunc labelfunc;        /* Address of label routine */
+
+static struct ofmt *outfmt;  /* Structure of addresses of output routines */
+
+static expr **tempexprs = NULL;
+static int    ntempexprs;
+static int    tempexprs_size = 0;
+
+static expr  *tempexpr;
+static int   ntempexpr;
+static int   tempexpr_size;
+
+static struct tokenval *tokval;          /* The current token */
+static int i;                    /* The t_type of tokval */
+
+static void *scpriv;
+static loc_t *location;                /* Pointer to current line's segment,offset */
+static int *opflags;
+
+static struct eval_hints *hint;
+
+extern int  in_abs_seg;                /* ABSOLUTE segment flag */
+extern long abs_seg;           /* ABSOLUTE segment */
+extern long abs_offset;                /* ABSOLUTE segment offset */
+
+/*
+ * Unimportant cleanup is done to avoid confusing people who are trying
+ * to debug real memory leaks
+ */
+void eval_cleanup(void) 
+{
+    while (ntempexprs)
+       nasm_free (tempexprs[--ntempexprs]);
+    nasm_free (tempexprs);
+}
+
+/*
+ * Construct a temporary expression.
+ */
+static void begintemp(void) 
+{
+    tempexpr = NULL;
+    tempexpr_size = ntempexpr = 0;
+}
+
+static void addtotemp(long type, long value) 
+{
+    while (ntempexpr >= tempexpr_size) {
+       tempexpr_size += TEMPEXPR_DELTA;
+       tempexpr = nasm_realloc(tempexpr,
+                                tempexpr_size*sizeof(*tempexpr));
+    }
+    tempexpr[ntempexpr].type = type;
+    tempexpr[ntempexpr++].value = value;
+}
+
+static expr *finishtemp(void) 
+{
+    addtotemp (0L, 0L);                       /* terminate */
+    while (ntempexprs >= tempexprs_size) {
+       tempexprs_size += TEMPEXPRS_DELTA;
+       tempexprs = nasm_realloc(tempexprs,
+                                tempexprs_size*sizeof(*tempexprs));
+    }
+    return tempexprs[ntempexprs++] = tempexpr;
+}
+
+/*
+ * Add two vector datatypes. We have some bizarre behaviour on far-
+ * absolute segment types: we preserve them during addition _only_
+ * if one of the segments is a truly pure scalar.
+ */
+static expr *add_vectors(expr *p, expr *q) 
+{
+    int preserve;
+
+    preserve = is_really_simple(p) || is_really_simple(q);
+
+    begintemp();
+
+    while (p->type && q->type &&
+          p->type < EXPR_SEGBASE+SEG_ABS &&
+          q->type < EXPR_SEGBASE+SEG_ABS)
+    {
+       int lasttype;
+
+       if (p->type > q->type) {
+           addtotemp(q->type, q->value);
+           lasttype = q++->type;
+       } else if (p->type < q->type) {
+           addtotemp(p->type, p->value);
+           lasttype = p++->type;
+       } else {                       /* *p and *q have same type */
+           long sum = p->value + q->value;
+           if (sum)
+               addtotemp(p->type, sum);
+           lasttype = p->type;
+           p++, q++;
+       }
+       if (lasttype == EXPR_UNKNOWN) {
+           return finishtemp();
+       }
+    }
+    while (p->type &&
+          (preserve || p->type < EXPR_SEGBASE+SEG_ABS)) 
+    {
+       addtotemp(p->type, p->value);
+       p++;
+    }
+    while (q->type &&
+          (preserve || q->type < EXPR_SEGBASE+SEG_ABS)) 
+    {
+       addtotemp(q->type, q->value);
+       q++;
+    }
+
+    return finishtemp();
+}
+
+/*
+ * Multiply a vector by a scalar. Strip far-absolute segment part
+ * if present.
+ *
+ * Explicit treatment of UNKNOWN is not required in this routine,
+ * since it will silently do the Right Thing anyway.
+ *
+ * If `affect_hints' is set, we also change the hint type to
+ * NOTBASE if a MAKEBASE hint points at a register being
+ * multiplied. This allows [eax*1+ebx] to hint EBX rather than EAX
+ * as the base register.
+ */
+static expr *scalar_mult(expr *vect, long scalar, int affect_hints) 
+{
+    expr *p = vect;
+
+    while (p->type && p->type < EXPR_SEGBASE+SEG_ABS) {
+       p->value = scalar * (p->value);
+       if (hint && hint->type == EAH_MAKEBASE &&
+           p->type == hint->base && affect_hints)
+           hint->type = EAH_NOTBASE;
+       p++;
+    }
+    p->type = 0;
+
+    return vect;
+}
+
+static expr *scalarvect (long scalar) 
+{
+    begintemp();
+    addtotemp(EXPR_SIMPLE, scalar);
+    return finishtemp();
+}
+
+static expr *unknown_expr (void) 
+{
+    begintemp();
+    addtotemp(EXPR_UNKNOWN, 1L);
+    return finishtemp();
+}
+
+/*
+ * The SEG operator: calculate the segment part of a relocatable
+ * value. Return NULL, as usual, if an error occurs. Report the
+ * error too.
+ */
+static expr *segment_part (expr *e) 
+{
+    long seg;
+
+    if (is_unknown(e))
+       return unknown_expr();
+
+    if (!is_reloc(e)) {
+       error(ERR_NONFATAL, "cannot apply SEG to a non-relocatable value");
+       return NULL;
+    }
+
+    seg = reloc_seg(e);
+    if (seg == NO_SEG) {
+       error(ERR_NONFATAL, "cannot apply SEG to a non-relocatable value");
+       return NULL;
+    } else if (seg & SEG_ABS) {
+       return scalarvect(seg & ~SEG_ABS);
+    } else if (seg & 1) {
+       error(ERR_NONFATAL, "SEG applied to something which"
+             " is already a segment base");
+       return NULL;
+    }
+    else {
+       long base = outfmt->segbase(seg+1);
+
+       begintemp();
+       addtotemp((base == NO_SEG ? EXPR_UNKNOWN : EXPR_SEGBASE+base), 1L);
+       return finishtemp();
+    }
+}
+
+/*
+ * Recursive-descent parser. Called with a single boolean operand,
+ * which is TRUE if the evaluation is critical (i.e. unresolved
+ * symbols are an error condition). Must update the global `i' to
+ * reflect the token after the parsed string. May return NULL.
+ *
+ * evaluate() should report its own errors: on return it is assumed
+ * that if NULL has been returned, the error has already been
+ * reported.
+ */
+
+/*
+ * Grammar parsed is:
+ *
+ * expr  : bexpr [ WRT expr6 ]
+ * bexpr : rexp0 or expr0 depending on relative-mode setting
+ * rexp0 : rexp1 [ {||} rexp1...]
+ * rexp1 : rexp2 [ {^^} rexp2...]
+ * rexp2 : rexp3 [ {&&} rexp3...]
+ * rexp3 : expr0 [ {=,==,<>,!=,<,>,<=,>=} expr0 ]
+ * expr0 : expr1 [ {|} expr1...]
+ * expr1 : expr2 [ {^} expr2...]
+ * expr2 : expr3 [ {&} expr3...]
+ * expr3 : expr4 [ {<<,>>} expr4...]
+ * expr4 : expr5 [ {+,-} expr5...]
+ * expr5 : expr6 [ {*,/,%,//,%%} expr6...]
+ * expr6 : { ~,+,-,SEG } expr6
+ *       | (bexpr)
+ *       | symbol
+ *       | $
+ *       | number
+ */
+
+static expr *rexp0(int), *rexp1(int), *rexp2(int), *rexp3(int);
+
+static expr *expr0(int), *expr1(int), *expr2(int), *expr3(int);
+static expr *expr4(int), *expr5(int), *expr6(int);
+
+static expr *(*bexpr)(int);
+
+static expr *rexp0(int critical) 
+{
+    expr *e, *f;
+
+    e = rexp1(critical);
+    if (!e)
+       return NULL;
+
+    while (i == TOKEN_DBL_OR) 
+    {  
+       i = scan(scpriv, tokval);
+       f = rexp1(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`|' operator may only be applied to"
+                 " scalar values");
+       }
+
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect ((long) (reloc_value(e) || reloc_value(f)));
+    }
+    return e;
+}
+
+static expr *rexp1(int critical) 
+{
+    expr *e, *f;
+
+    e = rexp2(critical);
+    if (!e)
+       return NULL;
+    
+    while (i == TOKEN_DBL_XOR) 
+    {
+       i = scan(scpriv, tokval);
+       f = rexp2(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`^' operator may only be applied to"
+                 " scalar values");
+       }
+
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect ((long) (!reloc_value(e) ^ !reloc_value(f)));
+    }
+    return e;
+}
+
+static expr *rexp2(int critical) 
+{
+    expr *e, *f;
+
+    e = rexp3(critical);
+    if (!e)
+       return NULL;
+    while (i == TOKEN_DBL_AND) 
+    {
+       i = scan(scpriv, tokval);
+       f = rexp3(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`&' operator may only be applied to"
+                 " scalar values");
+       }
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect ((long) (reloc_value(e) && reloc_value(f)));
+    }
+    return e;
+}
+
+static expr *rexp3(int critical) 
+{
+    expr *e, *f;
+    long v;
+
+    e = expr0(critical);
+    if (!e)
+       return NULL;
+
+    while (i == TOKEN_EQ || i == TOKEN_LT || i == TOKEN_GT ||
+          i == TOKEN_NE || i == TOKEN_LE || i == TOKEN_GE) 
+    {
+       int j = i;
+       i = scan(scpriv, tokval);
+       f = expr0(critical);
+       if (!f)
+           return NULL;
+
+       e = add_vectors (e, scalar_mult(f, -1L, FALSE));
+
+       switch (j) 
+       {
+         case TOKEN_EQ: case TOKEN_NE:
+           if (is_unknown(e))
+               v = -1;                /* means unknown */
+           else if (!is_really_simple(e) || reloc_value(e) != 0)
+               v = (j == TOKEN_NE);   /* unequal, so return TRUE if NE */
+           else
+               v = (j == TOKEN_EQ);   /* equal, so return TRUE if EQ */
+           break;
+         default:
+           if (is_unknown(e))
+               v = -1;                /* means unknown */
+           else if (!is_really_simple(e)) {
+               error(ERR_NONFATAL, "`%s': operands differ by a non-scalar",
+                     (j == TOKEN_LE ? "<=" : j == TOKEN_LT ? "<" :
+                      j == TOKEN_GE ? ">=" : ">"));
+               v = 0;                 /* must set it to _something_ */
+           } else {
+               int vv = reloc_value(e);
+               if (vv == 0)
+                   v = (j == TOKEN_LE || j == TOKEN_GE);
+               else if (vv > 0)
+                   v = (j == TOKEN_GE || j == TOKEN_GT);
+               else /* vv < 0 */
+                   v = (j == TOKEN_LE || j == TOKEN_LT);
+           }
+           break;
+       }
+
+       if (v == -1)
+           e = unknown_expr();
+       else
+           e = scalarvect(v);
+    }
+    return e;
+}
+
+static expr *expr0(int critical) 
+{
+    expr *e, *f;
+
+    e = expr1(critical);
+    if (!e)
+       return NULL;
+
+    while (i == '|') 
+    {
+       i = scan(scpriv, tokval);
+       f = expr1(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`|' operator may only be applied to"
+                 " scalar values");
+       }
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect (reloc_value(e) | reloc_value(f));
+    }
+    return e;
+}
+
+static expr *expr1(int critical) 
+{
+    expr *e, *f;
+
+    e = expr2(critical);
+    if (!e)
+       return NULL;
+
+    while (i == '^') {
+       i = scan(scpriv, tokval);
+       f = expr2(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`^' operator may only be applied to"
+                 " scalar values");
+       }
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect (reloc_value(e) ^ reloc_value(f));
+    }
+    return e;
+}
+
+static expr *expr2(int critical) 
+{
+    expr *e, *f;
+
+    e = expr3(critical);
+    if (!e)
+       return NULL;
+
+    while (i == '&') {
+       i = scan(scpriv, tokval);
+       f = expr3(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "`&' operator may only be applied to"
+                 " scalar values");
+       }
+       if (is_just_unknown(e) || is_just_unknown(f))
+           e = unknown_expr();
+       else
+           e = scalarvect (reloc_value(e) & reloc_value(f));
+    }
+    return e;
+}
+
+static expr *expr3(int critical) 
+{
+    expr *e, *f;
+
+    e = expr4(critical);
+    if (!e)
+       return NULL;
+
+    while (i == TOKEN_SHL || i == TOKEN_SHR) 
+    {
+       int j = i;
+       i = scan(scpriv, tokval);
+       f = expr4(critical);
+       if (!f)
+           return NULL;
+       if (!(is_simple(e) || is_just_unknown(e)) ||
+           !(is_simple(f) || is_just_unknown(f))) 
+       {
+           error(ERR_NONFATAL, "shift operator may only be applied to"
+                 " scalar values");
+       } else if (is_just_unknown(e) || is_just_unknown(f)) {
+           e = unknown_expr();
+       } else switch (j) {
+         case TOKEN_SHL:
+           e = scalarvect (reloc_value(e) << reloc_value(f));
+           break;
+         case TOKEN_SHR:
+           e = scalarvect (((unsigned long)reloc_value(e)) >>
+                           reloc_value(f));
+           break;
+       }
+    }
+    return e;
+}
+
+static expr *expr4(int critical) 
+{
+    expr *e, *f;
+
+    e = expr5(critical);
+    if (!e)
+       return NULL;
+    while (i == '+' || i == '-') 
+    {
+       int j = i;
+       i = scan(scpriv, tokval);
+       f = expr5(critical);
+       if (!f)
+           return NULL;
+       switch (j) {
+         case '+':
+           e = add_vectors (e, f);
+           break;
+         case '-':
+           e = add_vectors (e, scalar_mult(f, -1L, FALSE));
+           break;
+       }
+    }
+    return e;
+}
+
+static expr *expr5(int critical) 
+{
+    expr *e, *f;
+
+    e = expr6(critical);
+    if (!e)
+       return NULL;
+    while (i == '*' || i == '/' || i == '%' ||
+          i == TOKEN_SDIV || i == TOKEN_SMOD) 
+    {
+       int j = i;
+       i = scan(scpriv, tokval);
+       f = expr6(critical);
+       if (!f)
+           return NULL;
+       if (j != '*' && (!(is_simple(e) || is_just_unknown(e)) ||
+                        !(is_simple(f) || is_just_unknown(f)))) 
+       {
+           error(ERR_NONFATAL, "division operator may only be applied to"
+                 " scalar values");
+           return NULL;
+       }
+       if (j != '*' && !is_unknown(f) && reloc_value(f) == 0) {
+           error(ERR_NONFATAL, "division by zero");
+           return NULL;
+       }
+       switch (j) {
+         case '*':
+           if (is_simple(e))
+               e = scalar_mult (f, reloc_value(e), TRUE);
+           else if (is_simple(f))
+               e = scalar_mult (e, reloc_value(f), TRUE);
+           else if (is_just_unknown(e) && is_just_unknown(f))
+               e = unknown_expr();
+           else {
+               error(ERR_NONFATAL, "unable to multiply two "
+                     "non-scalar objects");
+               return NULL;
+           }
+           break;
+         case '/':
+           if (is_just_unknown(e) || is_just_unknown(f))
+               e = unknown_expr();
+           else
+               e = scalarvect (((unsigned long)reloc_value(e)) /
+                               ((unsigned long)reloc_value(f)));
+           break;
+         case '%':
+           if (is_just_unknown(e) || is_just_unknown(f))
+               e = unknown_expr();
+           else
+               e = scalarvect (((unsigned long)reloc_value(e)) %
+                               ((unsigned long)reloc_value(f)));
+           break;
+         case TOKEN_SDIV:
+           if (is_just_unknown(e) || is_just_unknown(f))
+               e = unknown_expr();
+           else
+               e = scalarvect (((signed long)reloc_value(e)) /
+                               ((signed long)reloc_value(f)));
+           break;
+         case TOKEN_SMOD:
+           if (is_just_unknown(e) || is_just_unknown(f))
+               e = unknown_expr();
+           else
+               e = scalarvect (((signed long)reloc_value(e)) %
+                               ((signed long)reloc_value(f)));
+           break;
+       }
+    }
+    return e;
+}
+
+static expr *expr6(int critical) 
+{
+    long type;
+    expr *e;
+    long label_seg, label_ofs;
+
+    if (i == '-') {
+       i = scan(scpriv, tokval);
+       e = expr6(critical);
+       if (!e)
+           return NULL;
+       return scalar_mult (e, -1L, FALSE);
+    } else if (i == '+') {
+       i = scan(scpriv, tokval);
+       return expr6(critical);
+    } else if (i == '~') {
+       i = scan(scpriv, tokval);
+       e = expr6(critical);
+       if (!e)
+           return NULL;
+       if (is_just_unknown(e))
+           return unknown_expr();
+       else if (!is_simple(e)) {
+           error(ERR_NONFATAL, "`~' operator may only be applied to"
+                 " scalar values");
+           return NULL;
+       }
+       return scalarvect(~reloc_value(e));
+    } else if (i == TOKEN_SEG) {
+       i = scan(scpriv, tokval);
+       e = expr6(critical);
+       if (!e)
+           return NULL;
+       e = segment_part(e);
+       if (!e)
+           return NULL;
+       if (is_unknown(e) && critical) {
+           error(ERR_NONFATAL, "unable to determine segment base");
+           return NULL;
+       }
+       return e;
+    } else if (i == '(') {
+       i = scan(scpriv, tokval);
+       e = bexpr(critical);
+       if (!e)
+           return NULL;
+       if (i != ')') {
+           error(ERR_NONFATAL, "expecting `)'");
+           return NULL;
+       }
+       i = scan(scpriv, tokval);
+       return e;
+    } 
+    else if (i == TOKEN_NUM || i == TOKEN_REG || i == TOKEN_ID ||
+            i == TOKEN_HERE || i == TOKEN_BASE) 
+    {
+       begintemp();
+       switch (i) {
+         case TOKEN_NUM:
+           addtotemp(EXPR_SIMPLE, tokval->t_integer);
+           break;
+         case TOKEN_REG:
+           addtotemp(tokval->t_integer, 1L);
+           if (hint && hint->type == EAH_NOHINT)
+               hint->base = tokval->t_integer, hint->type = EAH_MAKEBASE;
+           break;
+         case TOKEN_ID:
+         case TOKEN_HERE:
+         case TOKEN_BASE:
+           /*
+            * If !location->known, this indicates that no
+            * symbol, Here or Base references are valid because we
+            * are in preprocess-only mode.
+            */
+           if (!location->known) {
+               error(ERR_NONFATAL,
+                     "%s not supported in preprocess-only mode",
+                     (i == TOKEN_ID ? "symbol references" :
+                      i == TOKEN_HERE ? "`$'" : "`$$'"));
+               addtotemp(EXPR_UNKNOWN, 1L);
+               break;
+           }
+
+           type = EXPR_SIMPLE;        /* might get overridden by UNKNOWN */
+           if (i == TOKEN_BASE)
+           {
+               label_seg = in_abs_seg ? abs_seg : location->segment;
+               label_ofs = 0;
+           } else if (i == TOKEN_HERE) {
+               label_seg = in_abs_seg ? abs_seg : location->segment;
+               label_ofs = in_abs_seg ? abs_offset : location->offset;
+           } else {
+               if (!labelfunc(tokval->t_charptr,&label_seg,&label_ofs))
+               {
+               if (critical == 2) {
+                   error (ERR_NONFATAL, "symbol `%s' undefined",
+                          tokval->t_charptr);
+                   return NULL;
+               } else if (critical == 1) {
+                       error (ERR_NONFATAL,
+                               "symbol `%s' not defined before use",
+                          tokval->t_charptr);
+                   return NULL;
+               } else {
+                   if (opflags)
+                       *opflags |= 1;
+                   type = EXPR_UNKNOWN;
+                   label_seg = NO_SEG;
+                   label_ofs = 1;
+               }
+           }
+               if (opflags && is_extern (tokval->t_charptr))
+                   *opflags |= OPFLAG_EXTERN;
+           }
+           addtotemp(type, label_ofs);
+           if (label_seg!=NO_SEG)
+               addtotemp(EXPR_SEGBASE + label_seg, 1L);
+           break;
+       }
+       i = scan(scpriv, tokval);
+       return finishtemp();
+    } else {
+       error(ERR_NONFATAL, "expression syntax error");
+       return NULL;
+    }
+}
+
+void eval_global_info (struct ofmt *output, lfunc lookup_label, loc_t *locp) 
+{
+    outfmt = output;
+    labelfunc = lookup_label;
+    location = locp;
+}
+
+expr *evaluate (scanner sc, void *scprivate, struct tokenval *tv,
+               int *fwref, int critical, efunc report_error,
+               struct eval_hints *hints) 
+{
+    expr *e;
+    expr *f = NULL;
+
+    hint = hints;
+    if (hint)
+       hint->type = EAH_NOHINT;
+
+    if (critical & CRITICAL) {
+       critical &= ~CRITICAL;
+       bexpr = rexp0;
+    } else
+       bexpr = expr0;
+
+    scan = sc;
+    scpriv = scprivate;
+    tokval = tv;
+    error = report_error;
+    opflags = fwref;
+
+    if (tokval->t_type == TOKEN_INVALID)
+       i = scan(scpriv, tokval);
+    else
+       i = tokval->t_type;
+
+    while (ntempexprs)                /* initialise temporary storage */
+       nasm_free (tempexprs[--ntempexprs]);
+
+    e = bexpr (critical);
+    if (!e)
+       return NULL;
+
+    if (i == TOKEN_WRT) {
+       i = scan(scpriv, tokval);      /* eat the WRT */
+       f = expr6 (critical);
+       if (!f)
+           return NULL;
+    }
+    e = scalar_mult (e, 1L, FALSE);    /* strip far-absolute segment part */
+    if (f) {
+       expr *g;
+       if (is_just_unknown(f))
+           g = unknown_expr();
+       else {
+           long value;
+           begintemp();
+           if (!is_reloc(f)) {
+               error(ERR_NONFATAL, "invalid right-hand operand to WRT");
+               return NULL;
+           }
+           value = reloc_seg(f);
+           if (value == NO_SEG)
+               value = reloc_value(f) | SEG_ABS;
+           else if (!(value & SEG_ABS) && !(value % 2) && critical) 
+           {
+               error(ERR_NONFATAL, "invalid right-hand operand to WRT");
+               return NULL;
+           }
+           addtotemp(EXPR_WRT, value);
+           g = finishtemp();
+       }
+       e = add_vectors (e, g);
+    }
+    return e;
+}
diff --git a/src/preprocs/nasm/nasm-eval.h b/src/preprocs/nasm/nasm-eval.h
new file mode 100644 (file)
index 0000000..a933cbf
--- /dev/null
@@ -0,0 +1,28 @@
+/* eval.h   header file for eval.c
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ */
+
+#ifndef NASM_EVAL_H
+#define NASM_EVAL_H
+
+/*
+ * Called once to tell the evaluator what output format is
+ * providing segment-base details, and what function can be used to
+ * look labels up.
+ */
+void eval_global_info (struct ofmt *output, lfunc lookup_label, loc_t *locp);
+
+/*
+ * The evaluator itself.
+ */
+expr *evaluate (scanner sc, void *scprivate, struct tokenval *tv,
+               int *fwref, int critical, efunc report_error,
+               struct eval_hints *hints);
+
+void eval_cleanup(void);
+
+#endif
diff --git a/src/preprocs/nasm/nasm-pp.c b/src/preprocs/nasm/nasm-pp.c
new file mode 100644 (file)
index 0000000..0770812
--- /dev/null
@@ -0,0 +1,4459 @@
+/* -*- mode: c; c-file-style: "bsd" -*- */
+/* preproc.c   macro preprocessor for the Netwide Assembler
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ *
+ * initial version 18/iii/97 by Simon Tatham
+ */
+
+/* Typical flow of text through preproc
+ *
+ * pp_getline gets tokenised lines, either
+ *
+ *   from a macro expansion
+ *
+ * or
+ *   {
+ *   read_line  gets raw text from stdmacpos, or predef, or current input file
+ *   tokenise   converts to tokens
+ *   }
+ *
+ * expand_mmac_params is used to expand %1 etc., unless a macro is being
+ * defined or a false conditional is being processed
+ * (%0, %1, %+1, %-1, %%foo
+ *
+ * do_directive checks for directives
+ *
+ * expand_smacro is used to expand single line macros
+ *
+ * expand_mmacro is used to expand multi-line macros
+ *
+ * detoken is used to convert the line back to text
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include "nasm.h"
+#include "nasmlib.h"
+
+typedef struct SMacro SMacro;
+typedef struct MMacro MMacro;
+typedef struct Context Context;
+typedef struct Token Token;
+typedef struct Blocks Blocks;
+typedef struct Line Line;
+typedef struct Include Include;
+typedef struct Cond Cond;
+typedef struct IncPath IncPath;
+
+/*
+ * Store the definition of a single-line macro.
+ */
+struct SMacro
+{
+    SMacro *next;
+    char *name;
+    int casesense;
+    int nparam;
+    int in_progress;
+    Token *expansion;
+};
+
+/*
+ * Store the definition of a multi-line macro. This is also used to
+ * store the interiors of `%rep...%endrep' blocks, which are
+ * effectively self-re-invoking multi-line macros which simply
+ * don't have a name or bother to appear in the hash tables. %rep
+ * blocks are signified by having a NULL `name' field.
+ *
+ * In a MMacro describing a `%rep' block, the `in_progress' field
+ * isn't merely boolean, but gives the number of repeats left to
+ * run.
+ *
+ * The `next' field is used for storing MMacros in hash tables; the
+ * `next_active' field is for stacking them on istk entries.
+ *
+ * When a MMacro is being expanded, `params', `iline', `nparam',
+ * `paramlen', `rotate' and `unique' are local to the invocation.
+ */
+struct MMacro
+{
+    MMacro *next;
+    char *name;
+    int casesense;
+    int nparam_min, nparam_max;
+    int plus;                  /* is the last parameter greedy? */
+    int nolist;                        /* is this macro listing-inhibited? */
+    int in_progress;
+    Token *dlist;              /* All defaults as one list */
+    Token **defaults;          /* Parameter default pointers */
+    int ndefs;                 /* number of default parameters */
+    Line *expansion;
+
+    MMacro *next_active;
+    MMacro *rep_nest;          /* used for nesting %rep */
+    Token **params;            /* actual parameters */
+    Token *iline;              /* invocation line */
+    int nparam, rotate, *paramlen;
+    unsigned long unique;
+    int lineno;                        /* Current line number on expansion */
+};
+
+/*
+ * The context stack is composed of a linked list of these.
+ */
+struct Context
+{
+    Context *next;
+    SMacro *localmac;
+    char *name;
+    unsigned long number;
+};
+
+/*
+ * This is the internal form which we break input lines up into.
+ * Typically stored in linked lists.
+ *
+ * Note that `type' serves a double meaning: TOK_SMAC_PARAM is not
+ * necessarily used as-is, but is intended to denote the number of
+ * the substituted parameter. So in the definition
+ *
+ *     %define a(x,y) ( (x) & ~(y) )
+ * 
+ * the token representing `x' will have its type changed to
+ * TOK_SMAC_PARAM, but the one representing `y' will be
+ * TOK_SMAC_PARAM+1.
+ *
+ * TOK_INTERNAL_STRING is a dirty hack: it's a single string token
+ * which doesn't need quotes around it. Used in the pre-include
+ * mechanism as an alternative to trying to find a sensible type of
+ * quote to use on the filename we were passed.
+ */
+struct Token
+{
+    Token *next;
+    char *text;
+    SMacro *mac;               /* associated macro for TOK_SMAC_END */
+    int type;
+};
+enum
+{
+    TOK_WHITESPACE = 1, TOK_COMMENT, TOK_ID, TOK_PREPROC_ID, TOK_STRING,
+    TOK_NUMBER, TOK_SMAC_END, TOK_OTHER, TOK_SMAC_PARAM,
+    TOK_INTERNAL_STRING
+};
+
+/*
+ * Multi-line macro definitions are stored as a linked list of
+ * these, which is essentially a container to allow several linked
+ * lists of Tokens.
+ * 
+ * Note that in this module, linked lists are treated as stacks
+ * wherever possible. For this reason, Lines are _pushed_ on to the
+ * `expansion' field in MMacro structures, so that the linked list,
+ * if walked, would give the macro lines in reverse order; this
+ * means that we can walk the list when expanding a macro, and thus
+ * push the lines on to the `expansion' field in _istk_ in reverse
+ * order (so that when popped back off they are in the right
+ * order). It may seem cockeyed, and it relies on my design having
+ * an even number of steps in, but it works...
+ *
+ * Some of these structures, rather than being actual lines, are
+ * markers delimiting the end of the expansion of a given macro.
+ * This is for use in the cycle-tracking and %rep-handling code.
+ * Such structures have `finishes' non-NULL, and `first' NULL. All
+ * others have `finishes' NULL, but `first' may still be NULL if
+ * the line is blank.
+ */
+struct Line
+{
+    Line *next;
+    MMacro *finishes;
+    Token *first;
+};
+
+/*
+ * To handle an arbitrary level of file inclusion, we maintain a
+ * stack (ie linked list) of these things.
+ */
+struct Include
+{
+    Include *next;
+    FILE *fp;
+    Cond *conds;
+    Line *expansion;
+    char *fname;
+    int lineno, lineinc;
+    MMacro *mstk;              /* stack of active macros/reps */
+};
+
+/*
+ * Include search path. This is simply a list of strings which get
+ * prepended, in turn, to the name of an include file, in an
+ * attempt to find the file if it's not in the current directory.
+ */
+struct IncPath
+{
+    IncPath *next;
+    char *path;
+};
+
+/*
+ * Conditional assembly: we maintain a separate stack of these for
+ * each level of file inclusion. (The only reason we keep the
+ * stacks separate is to ensure that a stray `%endif' in a file
+ * included from within the true branch of a `%if' won't terminate
+ * it and cause confusion: instead, rightly, it'll cause an error.)
+ */
+struct Cond
+{
+    Cond *next;
+    int state;
+};
+enum
+{
+    /*
+     * These states are for use just after %if or %elif: IF_TRUE
+     * means the condition has evaluated to truth so we are
+     * currently emitting, whereas IF_FALSE means we are not
+     * currently emitting but will start doing so if a %else comes
+     * up. In these states, all directives are admissible: %elif,
+     * %else and %endif. (And of course %if.)
+     */
+    COND_IF_TRUE, COND_IF_FALSE,
+    /*
+     * These states come up after a %else: ELSE_TRUE means we're
+     * emitting, and ELSE_FALSE means we're not. In ELSE_* states,
+     * any %elif or %else will cause an error.
+     */
+    COND_ELSE_TRUE, COND_ELSE_FALSE,
+    /*
+     * This state means that we're not emitting now, and also that
+     * nothing until %endif will be emitted at all. It's for use in
+     * two circumstances: (i) when we've had our moment of emission
+     * and have now started seeing %elifs, and (ii) when the
+     * condition construct in question is contained within a
+     * non-emitting branch of a larger condition construct.
+     */
+    COND_NEVER
+};
+#define emitting(x) ( (x) == COND_IF_TRUE || (x) == COND_ELSE_TRUE )
+
+/* 
+ * These defines are used as the possible return values for do_directive
+ */
+#define NO_DIRECTIVE_FOUND  0
+#define DIRECTIVE_FOUND            1
+
+/*
+ * Condition codes. Note that we use c_ prefix not C_ because C_ is
+ * used in nasm.h for the "real" condition codes. At _this_ level,
+ * we treat CXZ and ECXZ as condition codes, albeit non-invertible
+ * ones, so we need a different enum...
+ */
+static const char *conditions[] = {
+    "a", "ae", "b", "be", "c", "cxz", "e", "ecxz", "g", "ge", "l", "le",
+    "na", "nae", "nb", "nbe", "nc", "ne", "ng", "nge", "nl", "nle", "no",
+    "np", "ns", "nz", "o", "p", "pe", "po", "s", "z"
+};
+enum
+{
+    c_A, c_AE, c_B, c_BE, c_C, c_CXZ, c_E, c_ECXZ, c_G, c_GE, c_L, c_LE,
+    c_NA, c_NAE, c_NB, c_NBE, c_NC, c_NE, c_NG, c_NGE, c_NL, c_NLE, c_NO,
+    c_NP, c_NS, c_NZ, c_O, c_P, c_PE, c_PO, c_S, c_Z
+};
+static int inverse_ccs[] = {
+    c_NA, c_NAE, c_NB, c_NBE, c_NC, -1, c_NE, -1, c_NG, c_NGE, c_NL, c_NLE,
+    c_A, c_AE, c_B, c_BE, c_C, c_E, c_G, c_GE, c_L, c_LE, c_O, c_P, c_S,
+    c_Z, c_NO, c_NP, c_PO, c_PE, c_NS, c_NZ
+};
+
+/*
+ * Directive names.
+ */
+static const char *directives[] = {
+    "%arg",
+    "%assign", "%clear", "%define", "%elif", "%elifctx", "%elifdef",
+    "%elifid", "%elifidn", "%elifidni", "%elifmacro", "%elifnctx", "%elifndef",
+    "%elifnid", "%elifnidn", "%elifnidni", "%elifnmacro", "%elifnnum", "%elifnstr",
+    "%elifnum", "%elifstr", "%else", "%endif", "%endm", "%endmacro",
+    "%endrep", "%error", "%exitrep", "%iassign", "%idefine", "%if",
+    "%ifctx", "%ifdef", "%ifid", "%ifidn", "%ifidni", "%ifmacro", "%ifnctx",
+    "%ifndef", "%ifnid", "%ifnidn", "%ifnidni", "%ifnmacro", "%ifnnum",
+    "%ifnstr", "%ifnum", "%ifstr", "%imacro", "%include",
+    "%ixdefine", "%line",
+    "%local",
+    "%macro", "%pop", "%push", "%rep", "%repl", "%rotate",
+    "%stacksize",
+    "%strlen", "%substr", "%undef", "%xdefine"
+};
+enum
+{
+    PP_ARG,
+    PP_ASSIGN, PP_CLEAR, PP_DEFINE, PP_ELIF, PP_ELIFCTX, PP_ELIFDEF,
+    PP_ELIFID, PP_ELIFIDN, PP_ELIFIDNI, PP_ELIFMACRO, PP_ELIFNCTX, PP_ELIFNDEF,
+    PP_ELIFNID, PP_ELIFNIDN, PP_ELIFNIDNI, PP_ELIFNMACRO, PP_ELIFNNUM, PP_ELIFNSTR,
+    PP_ELIFNUM, PP_ELIFSTR, PP_ELSE, PP_ENDIF, PP_ENDM, PP_ENDMACRO,
+    PP_ENDREP, PP_ERROR, PP_EXITREP, PP_IASSIGN, PP_IDEFINE, PP_IF,
+    PP_IFCTX, PP_IFDEF, PP_IFID, PP_IFIDN, PP_IFIDNI, PP_IFMACRO, PP_IFNCTX,
+    PP_IFNDEF, PP_IFNID, PP_IFNIDN, PP_IFNIDNI, PP_IFNMACRO, PP_IFNNUM,
+    PP_IFNSTR, PP_IFNUM, PP_IFSTR, PP_IMACRO, PP_INCLUDE,
+    PP_IXDEFINE, PP_LINE,
+    PP_LOCAL,
+    PP_MACRO, PP_POP, PP_PUSH, PP_REP, PP_REPL, PP_ROTATE,
+    PP_STACKSIZE,
+    PP_STRLEN, PP_SUBSTR, PP_UNDEF, PP_XDEFINE
+};
+
+/* If this is a an IF, ELIF, ELSE or ENDIF keyword */
+static int is_condition(int arg)
+{
+    return ((arg >= PP_ELIF) && (arg <= PP_ENDIF)) ||
+       ((arg >= PP_IF) && (arg <= PP_IFSTR));
+}
+
+/* For TASM compatibility we need to be able to recognise TASM compatible
+ * conditional compilation directives. Using the NASM pre-processor does
+ * not work, so we look for them specifically from the following list and
+ * then jam in the equivalent NASM directive into the input stream.
+ */
+
+#ifndef MAX
+#       define MAX(a,b) ( ((a) > (b)) ? (a) : (b))
+#endif
+
+enum
+{
+    TM_ARG, TM_ELIF, TM_ELSE, TM_ENDIF, TM_IF, TM_IFDEF, TM_IFDIFI,
+    TM_IFNDEF, TM_INCLUDE, TM_LOCAL
+};
+
+static const char *tasm_directives[] = {
+    "arg", "elif", "else", "endif", "if", "ifdef", "ifdifi",
+    "ifndef", "include", "local"
+};
+
+static int StackSize = 4;
+static char *StackPointer = "ebp";
+static int ArgOffset = 8;
+static int LocalOffset = 4;
+
+
+static Context *cstk;
+static Include *istk;
+static IncPath *ipath = NULL;
+
+static efunc _error;           /* Pointer to client-provided error reporting function */
+static evalfunc evaluate;
+
+static int pass;               /* HACK: pass 0 = generate dependencies only */
+
+static unsigned long unique;   /* unique identifier numbers */
+
+static Line *predef = NULL;
+
+static ListGen *list;
+
+/*
+ * The number of hash values we use for the macro lookup tables.
+ * FIXME: We should *really* be able to configure this at run time,
+ * or even have the hash table automatically expanding when necessary.
+ */
+#define NHASH 31
+
+/*
+ * The current set of multi-line macros we have defined.
+ */
+static MMacro *mmacros[NHASH];
+
+/*
+ * The current set of single-line macros we have defined.
+ */
+static SMacro *smacros[NHASH];
+
+/*
+ * The multi-line macro we are currently defining, or the %rep
+ * block we are currently reading, if any.
+ */
+static MMacro *defining;
+
+/*
+ * The number of macro parameters to allocate space for at a time.
+ */
+#define PARAM_DELTA 16
+
+/*
+ * The standard macro set: defined as `static char *stdmac[]'. Also
+ * gives our position in the macro set, when we're processing it.
+ */
+#include "macros.c"
+static const char **stdmacpos;
+
+/*
+ * The extra standard macros that come from the object format, if
+ * any.
+ */
+static const char **extrastdmac = NULL;
+int any_extrastdmac;
+
+/*
+ * Tokens are allocated in blocks to improve speed
+ */
+#define TOKEN_BLOCKSIZE 4096
+static Token *freeTokens = NULL;
+struct Blocks {
+       Blocks *next;
+       void *chunk;
+};
+
+static Blocks blocks = { NULL, NULL };
+
+/*
+ * Forward declarations.
+ */
+static Token *expand_mmac_params(Token * tline);
+static Token *expand_smacro(Token * tline);
+static Token *expand_id(Token * tline);
+static Context *get_ctx(char *name, int all_contexts);
+static void make_tok_num(Token * tok, long val);
+static void error(int severity, const char *fmt, ...);
+static void *new_Block(size_t size);
+static void delete_Blocks(void);
+static Token *new_Token(Token * next, int type, char *text, int txtlen);
+static Token *delete_Token(Token * t);
+
+/*
+ * Macros for safe checking of token pointers, avoid *(NULL)
+ */
+#define tok_type_(x,t) ((x) && (x)->type == (t))
+#define skip_white_(x) if (tok_type_((x), TOK_WHITESPACE)) (x)=(x)->next
+#define tok_is_(x,v) (tok_type_((x), TOK_OTHER) && !strcmp((x)->text,(v)))
+#define tok_isnt_(x,v) ((x) && ((x)->type!=TOK_OTHER || strcmp((x)->text,(v))))
+
+/* Handle TASM specific directives, which do not contain a % in
+ * front of them. We do it here because I could not find any other
+ * place to do it for the moment, and it is a hack (ideally it would
+ * be nice to be able to use the NASM pre-processor to do it).
+ */
+static char *
+check_tasm_directive(char *line)
+{
+    int i, j, k, m, len;
+    char *p = line, *oldline, oldchar;
+
+    /* Skip whitespace */
+    while (isspace(*p) && *p != 0)
+       p++;
+
+    /* Binary search for the directive name */
+    i = -1;
+    j = elements(tasm_directives);
+    len = 0;
+    while (!isspace(p[len]) && p[len] != 0)
+       len++;
+    if (len)
+    {
+       oldchar = p[len];
+       p[len] = 0;
+       while (j - i > 1)
+       {
+           k = (j + i) / 2;
+           m = nasm_stricmp(p, tasm_directives[k]);
+           if (m == 0)
+           {
+               /* We have found a directive, so jam a % in front of it
+                * so that NASM will then recognise it as one if it's own.
+                */
+               p[len] = oldchar;
+               len = strlen(p);
+               oldline = line;
+               line = nasm_malloc(len + 2);
+               line[0] = '%';
+               if (k == TM_IFDIFI)
+               {
+                   /* NASM does not recognise IFDIFI, so we convert it to
+                    * %ifdef BOGUS. This is not used in NASM comaptible
+                    * code, but does need to parse for the TASM macro
+                    * package.
+                    */
+                   strcpy(line + 1, "ifdef BOGUS");
+               }
+               else
+               {
+                   memcpy(line + 1, p, len + 1);
+               }
+               nasm_free(oldline);
+               return line;
+           }
+           else if (m < 0)
+           {
+               j = k;
+           }
+           else
+               i = k;
+       }
+       p[len] = oldchar;
+    }
+    return line;
+}
+
+/*
+ * The pre-preprocessing stage... This function translates line
+ * number indications as they emerge from GNU cpp (`# lineno "file"
+ * flags') into NASM preprocessor line number indications (`%line
+ * lineno file').
+ */
+static char *
+prepreproc(char *line)
+{
+    int lineno, fnlen;
+    char *fname, *oldline;
+
+    if (line[0] == '#' && line[1] == ' ')
+    {
+       oldline = line;
+       fname = oldline + 2;
+       lineno = atoi(fname);
+       fname += strspn(fname, "0123456789 ");
+       if (*fname == '"')
+           fname++;
+       fnlen = strcspn(fname, "\"");
+       line = nasm_malloc(20 + fnlen);
+       sprintf(line, "%%line %d %.*s", lineno, fnlen, fname);
+       nasm_free(oldline);
+    }
+    if (tasm_compatible_mode)
+       return check_tasm_directive(line);
+    return line;
+}
+
+/*
+ * The hash function for macro lookups. Note that due to some
+ * macros having case-insensitive names, the hash function must be
+ * invariant under case changes. We implement this by applying a
+ * perfectly normal hash function to the uppercase of the string.
+ */
+static int
+hash(char *s)
+{
+    unsigned int h = 0;
+    int i = 0;
+    /*
+     * Powers of three, mod 31.
+     */
+    static const int multipliers[] = {
+       1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10,
+       30, 28, 22, 4, 12, 5, 15, 14, 11, 2, 6, 18, 23, 7, 21
+    };
+
+
+    while (*s)
+    {
+       h += multipliers[i] * (unsigned char) (toupper(*s));
+       s++;
+       if (++i >= elements(multipliers))
+           i = 0;
+    }
+    h %= NHASH;
+    return h;
+}
+
+/*
+ * Free a linked list of tokens.
+ */
+static void
+free_tlist(Token * list)
+{
+    while (list)
+    {
+       list = delete_Token(list);
+    }
+}
+
+/*
+ * Free a linked list of lines.
+ */
+static void
+free_llist(Line * list)
+{
+    Line *l;
+    while (list)
+    {
+       l = list;
+       list = list->next;
+       free_tlist(l->first);
+       nasm_free(l);
+    }
+}
+
+/*
+ * Free an MMacro
+ */
+static void
+free_mmacro(MMacro * m)
+{
+    nasm_free(m->name);
+    free_tlist(m->dlist);
+    nasm_free(m->defaults);
+    free_llist(m->expansion);
+    nasm_free(m);
+}
+
+/*
+ * Pop the context stack.
+ */
+static void
+ctx_pop(void)
+{
+    Context *c = cstk;
+    SMacro *smac, *s;
+
+    cstk = cstk->next;
+    smac = c->localmac;
+    while (smac)
+    {
+       s = smac;
+       smac = smac->next;
+       nasm_free(s->name);
+       free_tlist(s->expansion);
+       nasm_free(s);
+    }
+    nasm_free(c->name);
+    nasm_free(c);
+}
+
+#define BUF_DELTA 512
+/*
+ * Read a line from the top file in istk, handling multiple CR/LFs
+ * at the end of the line read, and handling spurious ^Zs. Will
+ * return lines from the standard macro set if this has not already
+ * been done.
+ */
+static char *
+read_line(void)
+{
+    char *buffer, *p, *q;
+    int bufsize, continued_count;
+
+    if (stdmacpos)
+    {
+       if (*stdmacpos)
+       {
+           char *ret = nasm_strdup(*stdmacpos++);
+           if (!*stdmacpos && any_extrastdmac)
+           {
+               stdmacpos = extrastdmac;
+               any_extrastdmac = FALSE;
+               return ret;
+           }
+           /*
+            * Nasty hack: here we push the contents of `predef' on
+            * to the top-level expansion stack, since this is the
+            * most convenient way to implement the pre-include and
+            * pre-define features.
+            */
+           if (!*stdmacpos)
+           {
+               Line *pd, *l;
+               Token *head, **tail, *t;
+
+               for (pd = predef; pd; pd = pd->next)
+               {
+                   head = NULL;
+                   tail = &head;
+                   for (t = pd->first; t; t = t->next)
+                   {
+                       *tail = new_Token(NULL, t->type, t->text, 0);
+                       tail = &(*tail)->next;
+                   }
+                   l = nasm_malloc(sizeof(Line));
+                   l->next = istk->expansion;
+                   l->first = head;
+                   l->finishes = FALSE;
+                   istk->expansion = l;
+               }
+           }
+           return ret;
+       }
+       else
+       {
+           stdmacpos = NULL;
+       }
+    }
+
+    bufsize = BUF_DELTA;
+    buffer = nasm_malloc(BUF_DELTA);
+    p = buffer;
+    continued_count = 0;
+    while (1)
+    {
+       q = fgets(p, bufsize - (p - buffer), istk->fp);
+       if (!q)
+           break;
+       p += strlen(p);
+       if (p > buffer && p[-1] == '\n')
+       {
+           /* Convert backslash-CRLF line continuation sequences into
+              nothing at all (for DOS and Windows) */
+           if (((p - 2) > buffer) && (p[-3] == '\\') && (p[-2] == '\r')) {
+               p -= 3;
+               *p = 0;
+               continued_count++;
+           }
+           /* Also convert backslash-LF line continuation sequences into
+              nothing at all (for Unix) */
+           else if (((p - 1) > buffer) && (p[-2] == '\\')) {
+               p -= 2;
+               *p = 0;
+               continued_count++;
+           }
+           else {
+               break;
+           }
+       }
+       if (p - buffer > bufsize - 10)
+       {
+           long offset = p - buffer;
+           bufsize += BUF_DELTA;
+           buffer = nasm_realloc(buffer, bufsize);
+           p = buffer + offset;        /* prevent stale-pointer problems */
+       }
+    }
+
+    if (!q && p == buffer)
+    {
+       nasm_free(buffer);
+       return NULL;
+    }
+
+    src_set_linnum(src_get_linnum() + istk->lineinc + (continued_count * istk->lineinc));
+
+    /*
+     * Play safe: remove CRs as well as LFs, if any of either are
+     * present at the end of the line.
+     */
+    while (--p >= buffer && (*p == '\n' || *p == '\r'))
+       *p = '\0';
+
+    /*
+     * Handle spurious ^Z, which may be inserted into source files
+     * by some file transfer utilities.
+     */
+    buffer[strcspn(buffer, "\032")] = '\0';
+
+    list->line(LIST_READ, buffer);
+
+    return buffer;
+}
+
+/*
+ * Tokenise a line of text. This is a very simple process since we
+ * don't need to parse the value out of e.g. numeric tokens: we
+ * simply split one string into many.
+ */
+static Token *
+tokenise(char *line)
+{
+    char *p = line;
+    int type;
+    Token *list = NULL;
+    Token *t, **tail = &list;
+
+    while (*line)
+    {
+       p = line;
+       if (*p == '%')
+       {
+               p++;
+               if ( isdigit(*p) ||
+                       ((*p == '-' || *p == '+') && isdigit(p[1])) ||
+                       ((*p == '+') && (isspace(p[1]) || !p[1])))
+                               {
+                       do
+                       {
+                       p++;
+                       }
+                       while (isdigit(*p));
+                       type = TOK_PREPROC_ID;
+               }
+               else if (*p == '{')
+               {
+                       p++;
+                       while (*p && *p != '}')
+                       {
+                       p[-1] = *p;
+                       p++;
+                       }
+                       p[-1] = '\0';
+                       if (*p)
+                       p++;
+                       type = TOK_PREPROC_ID;
+               }
+               else if (isidchar(*p) ||
+                               ((*p == '!' || *p == '%' || *p == '$') &&
+                                       isidchar(p[1])))
+               {
+                       do
+                       {
+                       p++;
+                       }
+                       while (isidchar(*p));
+                       type = TOK_PREPROC_ID;
+               }
+               else
+               {
+                       type = TOK_OTHER;
+                       if (*p == '%')
+                               p++;
+               }
+       }
+       else if (isidstart(*p) || (*p == '$' && isidstart(p[1])))
+       {
+           type = TOK_ID;
+           p++;
+           while (*p && isidchar(*p))
+               p++;
+       }
+       else if (*p == '\'' || *p == '"')
+       {
+           /*
+            * A string token.
+            */
+           char c = *p;
+           p++;
+           type = TOK_STRING;
+           while (*p && *p != c)
+               p++;
+           if (*p)
+           {
+               p++;
+           }
+           else
+           {
+               error(ERR_WARNING, "unterminated string");
+           }
+       }
+       else if (isnumstart(*p))
+       {
+           /*
+            * A number token.
+            */
+           type = TOK_NUMBER;
+           p++;
+           while (*p && isnumchar(*p))
+               p++;
+       }
+       else if (isspace(*p))
+       {
+           type = TOK_WHITESPACE;
+           p++;
+           while (*p && isspace(*p))
+               p++;
+           /*
+            * Whitespace just before end-of-line is discarded by
+            * pretending it's a comment; whitespace just before a
+            * comment gets lumped into the comment.
+            */
+           if (!*p || *p == ';')
+           {
+               type = TOK_COMMENT;
+               while (*p)
+                   p++;
+           }
+       }
+       else if (*p == ';')
+       {
+           type = TOK_COMMENT;
+           while (*p)
+               p++;
+       }
+       else
+       {
+           /*
+            * Anything else is an operator of some kind. We check
+            * for all the double-character operators (>>, <<, //,
+            * %%, <=, >=, ==, !=, <>, &&, ||, ^^), but anything
+            * else is a single-character operator.
+            */
+           type = TOK_OTHER;
+           if ((p[0] == '>' && p[1] == '>') ||
+                   (p[0] == '<' && p[1] == '<') ||
+                   (p[0] == '/' && p[1] == '/') ||
+                   (p[0] == '<' && p[1] == '=') ||
+                   (p[0] == '>' && p[1] == '=') ||
+                   (p[0] == '=' && p[1] == '=') ||
+                   (p[0] == '!' && p[1] == '=') ||
+                   (p[0] == '<' && p[1] == '>') ||
+                   (p[0] == '&' && p[1] == '&') ||
+                   (p[0] == '|' && p[1] == '|') ||
+                   (p[0] == '^' && p[1] == '^'))
+           {
+               p++;
+           }
+           p++;
+       }
+       if (type != TOK_COMMENT)
+       {
+           *tail = t = new_Token(NULL, type, line, p - line);
+           tail = &t->next;
+       }
+       line = p;
+    }
+    return list;
+}
+
+/*
+ * this function allocates a new managed block of memory and
+ * returns a pointer to the block.  The managed blocks are 
+ * deleted only all at once by the delete_Blocks function.
+ */
+static void *
+new_Block(size_t size)
+{
+       Blocks *b = &blocks;
+       
+       /* first, get to the end of the linked list      */
+       while (b->next)
+               b = b->next;
+       /* now allocate the requested chunk */
+       b->chunk = nasm_malloc(size);
+       
+       /* now allocate a new block for the next request */
+       b->next = nasm_malloc(sizeof(Blocks));
+       /* and initialize the contents of the new block */
+       b->next->next = NULL;
+       b->next->chunk = NULL;
+       return b->chunk;
+}
+
+/*
+ * this function deletes all managed blocks of memory
+ */
+static void
+delete_Blocks(void)
+{
+       Blocks *a,*b = &blocks;
+
+       /* 
+        * keep in mind that the first block, pointed to by blocks
+        * is a static and not dynamically allocated, so we don't 
+        * free it.
+        */
+       while (b)
+       {
+               if (b->chunk)
+                       nasm_free(b->chunk);
+               a = b;
+               b = b->next;
+                if (a != &blocks)
+                       nasm_free(a);
+       }
+}      
+
+/*
+ *  this function creates a new Token and passes a pointer to it 
+ *  back to the caller.  It sets the type and text elements, and
+ *  also the mac and next elements to NULL.
+ */
+static Token *
+new_Token(Token * next, int type, char *text, int txtlen)
+{
+    Token *t;
+    int i;
+
+    if (freeTokens == NULL)
+    {
+       freeTokens = (Token *)new_Block(TOKEN_BLOCKSIZE * sizeof(Token));
+       for (i = 0; i < TOKEN_BLOCKSIZE - 1; i++)
+           freeTokens[i].next = &freeTokens[i + 1];
+       freeTokens[i].next = NULL;
+    }
+    t = freeTokens;
+    freeTokens = t->next;
+    t->next = next;
+    t->mac = NULL;
+    t->type = type;
+    if (type == TOK_WHITESPACE || text == NULL)
+    {
+       t->text = NULL;
+    }
+    else
+    {
+       if (txtlen == 0)
+           txtlen = strlen(text);
+       t->text = nasm_malloc(1 + txtlen);
+       strncpy(t->text, text, txtlen);
+       t->text[txtlen] = '\0';
+    }
+    return t;
+}
+
+static Token *
+delete_Token(Token * t)
+{
+    Token *next = t->next;
+    nasm_free(t->text);
+    t->next = freeTokens;
+    freeTokens = t;
+    return next;
+}
+
+/*
+ * Convert a line of tokens back into text.
+ * If expand_locals is not zero, identifiers of the form "%$*xxx"
+ * will be transformed into ..@ctxnum.xxx
+ */
+static char *
+detoken(Token * tlist, int expand_locals)
+{
+    Token *t;
+    int len;
+    char *line, *p;
+
+    len = 0;
+    for (t = tlist; t; t = t->next)
+    {
+       if (t->type == TOK_PREPROC_ID && t->text[1] == '!')
+       {
+           char *p = getenv(t->text + 2);
+           nasm_free(t->text);
+           if (p)
+               t->text = nasm_strdup(p);
+           else
+               t->text = NULL;
+       }
+       /* Expand local macros here and not during preprocessing */
+       if (expand_locals &&
+               t->type == TOK_PREPROC_ID && t->text &&
+               t->text[0] == '%' && t->text[1] == '$')
+       {
+           Context *ctx = get_ctx(t->text, FALSE);
+           if (ctx)
+           {
+               char buffer[40];
+               char *p, *q = t->text + 2;
+
+               q += strspn(q, "$");
+               sprintf(buffer, "..@%lu.", ctx->number);
+               p = nasm_strcat(buffer, q);
+               nasm_free(t->text);
+               t->text = p;
+           }
+       }
+       if (t->type == TOK_WHITESPACE)
+       {
+           len++;
+       }
+       else if (t->text)
+       {
+           len += strlen(t->text);
+       }
+    }
+    p = line = nasm_malloc(len + 1);
+    for (t = tlist; t; t = t->next)
+    {
+       if (t->type == TOK_WHITESPACE)
+       {
+           *p = ' ';
+               p++;
+               *p = '\0';
+       }
+       else if (t->text)
+       {
+           strcpy(p, t->text);
+           p += strlen(p);
+       }
+    }
+    *p = '\0';
+    return line;
+}
+
+/*
+ * A scanner, suitable for use by the expression evaluator, which
+ * operates on a line of Tokens. Expects a pointer to a pointer to
+ * the first token in the line to be passed in as its private_data
+ * field.
+ */
+static int
+ppscan(void *private_data, struct tokenval *tokval)
+{
+    Token **tlineptr = private_data;
+    Token *tline;
+
+    do
+    {
+       tline = *tlineptr;
+       *tlineptr = tline ? tline->next : NULL;
+    }
+    while (tline && (tline->type == TOK_WHITESPACE ||
+                   tline->type == TOK_COMMENT));
+
+    if (!tline)
+       return tokval->t_type = TOKEN_EOS;
+
+    if (tline->text[0] == '$' && !tline->text[1])
+       return tokval->t_type = TOKEN_HERE;
+    if (tline->text[0] == '$' && tline->text[1] == '$' && !tline->text[2])
+       return tokval->t_type = TOKEN_BASE;
+
+    if (tline->type == TOK_ID)
+    {
+       tokval->t_charptr = tline->text;
+       if (tline->text[0] == '$')
+       {
+           tokval->t_charptr++;
+           return tokval->t_type = TOKEN_ID;
+       }
+
+       /*
+        * This is the only special case we actually need to worry
+        * about in this restricted context.
+        */
+       if (!nasm_stricmp(tline->text, "seg"))
+           return tokval->t_type = TOKEN_SEG;
+
+       return tokval->t_type = TOKEN_ID;
+    }
+
+    if (tline->type == TOK_NUMBER)
+    {
+       int rn_error;
+
+       tokval->t_integer = readnum(tline->text, &rn_error);
+       if (rn_error)
+           return tokval->t_type = TOKEN_ERRNUM;
+       tokval->t_charptr = NULL;
+       return tokval->t_type = TOKEN_NUM;
+    }
+
+    if (tline->type == TOK_STRING)
+    {
+       int rn_warn;
+       char q, *r;
+       int l;
+
+       r = tline->text;
+       q = *r++;
+       l = strlen(r);
+
+       if (l == 0 || r[l - 1] != q)
+           return tokval->t_type = TOKEN_ERRNUM;
+       tokval->t_integer = readstrnum(r, l - 1, &rn_warn);
+       if (rn_warn)
+           error(ERR_WARNING | ERR_PASS1, "character constant too long");
+       tokval->t_charptr = NULL;
+       return tokval->t_type = TOKEN_NUM;
+    }
+
+    if (tline->type == TOK_OTHER)
+    {
+       if (!strcmp(tline->text, "<<"))
+           return tokval->t_type = TOKEN_SHL;
+       if (!strcmp(tline->text, ">>"))
+           return tokval->t_type = TOKEN_SHR;
+       if (!strcmp(tline->text, "//"))
+           return tokval->t_type = TOKEN_SDIV;
+       if (!strcmp(tline->text, "%%"))
+           return tokval->t_type = TOKEN_SMOD;
+       if (!strcmp(tline->text, "=="))
+           return tokval->t_type = TOKEN_EQ;
+       if (!strcmp(tline->text, "<>"))
+           return tokval->t_type = TOKEN_NE;
+       if (!strcmp(tline->text, "!="))
+           return tokval->t_type = TOKEN_NE;
+       if (!strcmp(tline->text, "<="))
+           return tokval->t_type = TOKEN_LE;
+       if (!strcmp(tline->text, ">="))
+           return tokval->t_type = TOKEN_GE;
+       if (!strcmp(tline->text, "&&"))
+           return tokval->t_type = TOKEN_DBL_AND;
+       if (!strcmp(tline->text, "^^"))
+           return tokval->t_type = TOKEN_DBL_XOR;
+       if (!strcmp(tline->text, "||"))
+           return tokval->t_type = TOKEN_DBL_OR;
+    }
+
+    /*
+     * We have no other options: just return the first character of
+     * the token text.
+     */
+    return tokval->t_type = tline->text[0];
+}
+
+/*
+ * Compare a string to the name of an existing macro; this is a
+ * simple wrapper which calls either strcmp or nasm_stricmp
+ * depending on the value of the `casesense' parameter.
+ */
+static int
+mstrcmp(char *p, char *q, int casesense)
+{
+    return casesense ? strcmp(p, q) : nasm_stricmp(p, q);
+}
+
+/*
+ * Return the Context structure associated with a %$ token. Return
+ * NULL, having _already_ reported an error condition, if the
+ * context stack isn't deep enough for the supplied number of $
+ * signs.
+ * If all_contexts == TRUE, contexts that enclose current are
+ * also scanned for such smacro, until it is found; if not -
+ * only the context that directly results from the number of $'s
+ * in variable's name.
+ */
+static Context *
+get_ctx(char *name, int all_contexts)
+{
+    Context *ctx;
+    SMacro *m;
+    int i;
+
+    if (!name || name[0] != '%' || name[1] != '$')
+       return NULL;
+
+    if (!cstk)
+    {
+       error(ERR_NONFATAL, "`%s': context stack is empty", name);
+       return NULL;
+    }
+
+    for (i = strspn(name + 2, "$"), ctx = cstk; (i > 0) && ctx; i--)
+    {
+       ctx = ctx->next;
+/*        i--;  Lino - 02/25/02 */
+    }
+    if (!ctx)
+    {
+       error(ERR_NONFATAL, "`%s': context stack is only"
+               " %d level%s deep", name, i - 1, (i == 2 ? "" : "s"));
+       return NULL;
+    }
+    if (!all_contexts)
+       return ctx;
+
+    do
+    {
+       /* Search for this smacro in found context */
+       m = ctx->localmac;
+       while (m)
+       {
+           if (!mstrcmp(m->name, name, m->casesense))
+               return ctx;
+           m = m->next;
+       }
+       ctx = ctx->next;
+    }
+    while (ctx);
+    return NULL;
+}
+
+/* Add a slash to the end of a path if it is missing. We use the
+ * forward slash to make it compatible with Unix systems.
+ */
+static void
+backslash(char *s)
+{
+    int pos = strlen(s);
+    if (s[pos - 1] != '\\' && s[pos - 1] != '/')
+    {
+       s[pos] = '/';
+       s[pos + 1] = '\0';
+    }
+}
+
+/*
+ * Open an include file. This routine must always return a valid
+ * file pointer if it returns - it's responsible for throwing an
+ * ERR_FATAL and bombing out completely if not. It should also try
+ * the include path one by one until it finds the file or reaches
+ * the end of the path.
+ */
+static FILE *
+inc_fopen(char *file)
+{
+    FILE *fp;
+    char *prefix = "", *combine;
+    IncPath *ip = ipath;
+    static int namelen = 0;
+    int len = strlen(file);
+
+    while (1)
+    {
+       combine = nasm_malloc(strlen(prefix) + 1 + len + 1);
+       strcpy(combine, prefix);
+       if (prefix[0] != 0)
+           backslash(combine);
+       strcat(combine, file);
+       fp = fopen(combine, "r");
+       if (pass == 0 && fp)
+       {
+           namelen += strlen(combine) + 1;
+           if (namelen > 62)
+           {
+               printf(" \\\n  ");
+               namelen = 2;
+           }
+           printf(" %s", combine);
+       }
+       nasm_free(combine);
+       if (fp)
+           return fp;
+       if (!ip)
+           break;
+       prefix = ip->path;
+       ip = ip->next;
+    }
+
+    error(ERR_FATAL, "unable to open include file `%s'", file);
+    return NULL;               /* never reached - placate compilers */
+}
+
+/*
+ * Determine if we should warn on defining a single-line macro of
+ * name `name', with `nparam' parameters. If nparam is 0 or -1, will
+ * return TRUE if _any_ single-line macro of that name is defined.
+ * Otherwise, will return TRUE if a single-line macro with either
+ * `nparam' or no parameters is defined.
+ *
+ * If a macro with precisely the right number of parameters is
+ * defined, or nparam is -1, the address of the definition structure
+ * will be returned in `defn'; otherwise NULL will be returned. If `defn'
+ * is NULL, no action will be taken regarding its contents, and no
+ * error will occur.
+ *
+ * Note that this is also called with nparam zero to resolve
+ * `ifdef'.
+ *
+ * If you already know which context macro belongs to, you can pass
+ * the context pointer as first parameter; if you won't but name begins
+ * with %$ the context will be automatically computed. If all_contexts
+ * is true, macro will be searched in outer contexts as well.
+ */
+static int
+smacro_defined(Context * ctx, char *name, int nparam, SMacro ** defn,
+       int nocase)
+{
+    SMacro *m;
+
+    if (ctx)
+       m = ctx->localmac;
+    else if (name[0] == '%' && name[1] == '$')
+    {
+       if (cstk)
+           ctx = get_ctx(name, FALSE);
+       if (!ctx)
+           return FALSE;       /* got to return _something_ */
+       m = ctx->localmac;
+    }
+    else
+       m = smacros[hash(name)];
+
+    while (m)
+    {
+       if (!mstrcmp(m->name, name, m->casesense && nocase) &&
+               (nparam <= 0 || m->nparam == 0 || nparam == m->nparam))
+       {
+           if (defn)
+           {
+               if (nparam == m->nparam || nparam == -1)
+                   *defn = m;
+               else
+                   *defn = NULL;
+           }
+           return TRUE;
+       }
+       m = m->next;
+    }
+
+    return FALSE;
+}
+
+/*
+ * Count and mark off the parameters in a multi-line macro call.
+ * This is called both from within the multi-line macro expansion
+ * code, and also to mark off the default parameters when provided
+ * in a %macro definition line.
+ */
+static void
+count_mmac_params(Token * t, int *nparam, Token *** params)
+{
+    int paramsize, brace;
+
+    *nparam = paramsize = 0;
+    *params = NULL;
+    while (t)
+    {
+       if (*nparam >= paramsize)
+       {
+           paramsize += PARAM_DELTA;
+           *params = nasm_realloc(*params, sizeof(**params) * paramsize);
+       }
+       skip_white_(t);
+       brace = FALSE;
+       if (tok_is_(t, "{"))
+           brace = TRUE;
+       (*params)[(*nparam)++] = t;
+       while (tok_isnt_(t, brace ? "}" : ","))
+           t = t->next;
+       if (t)
+       {                       /* got a comma/brace */
+           t = t->next;
+           if (brace)
+           {
+               /*
+                * Now we've found the closing brace, look further
+                * for the comma.
+                */
+               skip_white_(t);
+               if (tok_isnt_(t, ","))
+               {
+                   error(ERR_NONFATAL,
+                           "braces do not enclose all of macro parameter");
+                   while (tok_isnt_(t, ","))
+                       t = t->next;
+               }
+               if (t)
+                   t = t->next;        /* eat the comma */
+           }
+       }
+    }
+}
+
+/*
+ * Determine whether one of the various `if' conditions is true or
+ * not.
+ *
+ * We must free the tline we get passed.
+ */
+static int
+if_condition(Token * tline, int i)
+{
+    int j, casesense;
+    Token *t, *tt, **tptr, *origline;
+    struct tokenval tokval;
+    expr *evalresult;
+
+    origline = tline;
+
+    switch (i)
+    {
+       case PP_IFCTX:
+       case PP_ELIFCTX:
+       case PP_IFNCTX:
+       case PP_ELIFNCTX:
+           j = FALSE;          /* have we matched yet? */
+           while (cstk && tline)
+           {
+               skip_white_(tline);
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL,
+                           "`%s' expects context identifiers",
+                           directives[i]);
+                   free_tlist(origline);
+                   return -1;
+               }
+               if (!nasm_stricmp(tline->text, cstk->name))
+                   j = TRUE;
+               tline = tline->next;
+           }
+           if (i == PP_IFNCTX || i == PP_ELIFNCTX)
+               j = !j;
+           free_tlist(origline);
+           return j;
+
+       case PP_IFDEF:
+       case PP_ELIFDEF:
+       case PP_IFNDEF:
+       case PP_ELIFNDEF:
+           j = FALSE;          /* have we matched yet? */
+           while (tline)
+           {
+               skip_white_(tline);
+               if (!tline || (tline->type != TOK_ID &&
+                               (tline->type != TOK_PREPROC_ID ||
+                                       tline->text[1] != '$')))
+               {
+                   error(ERR_NONFATAL,
+                         "`%s' expects macro identifiers",
+                         directives[i]);
+                   free_tlist(origline);
+                   return -1;
+               }
+               if (smacro_defined(NULL, tline->text, 0, NULL, 1))
+                   j = TRUE;
+               tline = tline->next;
+           }
+           if (i == PP_IFNDEF || i == PP_ELIFNDEF)
+               j = !j;
+           free_tlist(origline);
+           return j;
+
+       case PP_IFIDN:
+       case PP_ELIFIDN:
+       case PP_IFNIDN:
+       case PP_ELIFNIDN:
+       case PP_IFIDNI:
+       case PP_ELIFIDNI:
+       case PP_IFNIDNI:
+       case PP_ELIFNIDNI:
+           tline = expand_smacro(tline);
+           t = tt = tline;
+           while (tok_isnt_(tt, ","))
+               tt = tt->next;
+           if (!tt)
+           {
+               error(ERR_NONFATAL,
+                       "`%s' expects two comma-separated arguments",
+                       directives[i]);
+               free_tlist(tline);
+               return -1;
+           }
+           tt = tt->next;
+           casesense = (i == PP_IFIDN || i == PP_ELIFIDN ||
+                   i == PP_IFNIDN || i == PP_ELIFNIDN);
+           j = TRUE;           /* assume equality unless proved not */
+           while ((t->type != TOK_OTHER || strcmp(t->text, ",")) && tt)
+           {
+               if (tt->type == TOK_OTHER && !strcmp(tt->text, ","))
+               {
+                   error(ERR_NONFATAL, "`%s': more than one comma on line",
+                           directives[i]);
+                   free_tlist(tline);
+                   return -1;
+               }
+               if (t->type == TOK_WHITESPACE)
+               {
+                   t = t->next;
+                   continue;
+               }
+               else if (tt->type == TOK_WHITESPACE)
+               {
+                   tt = tt->next;
+                   continue;
+               }
+               else if (tt->type != t->type ||
+                       mstrcmp(tt->text, t->text, casesense))
+               {
+                   j = FALSE;  /* found mismatching tokens */
+                   break;
+               }
+               else
+               {
+                   t = t->next;
+                   tt = tt->next;
+                   continue;
+               }
+           }
+           if ((t->type != TOK_OTHER || strcmp(t->text, ",")) || tt)
+               j = FALSE;      /* trailing gunk on one end or other */
+           if (i == PP_IFNIDN || i == PP_ELIFNIDN ||
+                   i == PP_IFNIDNI || i == PP_ELIFNIDNI)
+               j = !j;
+           free_tlist(tline);
+           return j;
+
+        case PP_IFMACRO:
+        case PP_ELIFMACRO:
+        case PP_IFNMACRO:
+        case PP_ELIFNMACRO:
+       {
+           int found = 0;
+           MMacro searching, *mmac;
+
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tok_type_(tline, TOK_ID))
+           {
+               error(ERR_NONFATAL,
+                       "`%s' expects a macro name",
+                     directives[i]);
+               return -1;
+           }
+           searching.name = nasm_strdup(tline->text);
+           searching.casesense = (i == PP_MACRO);
+           searching.plus = FALSE;
+           searching.nolist = FALSE;
+           searching.in_progress = FALSE;
+           searching.rep_nest = NULL;
+           searching.nparam_min = 0;
+           searching.nparam_max = INT_MAX;
+           tline = expand_smacro(tline->next);
+           skip_white_(tline);
+           if (!tline)
+           {
+           } else if (!tok_type_(tline, TOK_NUMBER))
+           {
+               error(ERR_NONFATAL,
+                     "`%s' expects a parameter count or nothing",
+                     directives[i]);
+           }
+           else
+           {
+               searching.nparam_min = searching.nparam_max =
+                       readnum(tline->text, &j);
+               if (j)
+                   error(ERR_NONFATAL,
+                         "unable to parse parameter count `%s'",
+                         tline->text);
+           }
+           if (tline && tok_is_(tline->next, "-"))
+           {
+               tline = tline->next->next;
+               if (tok_is_(tline, "*"))
+                   searching.nparam_max = INT_MAX;
+               else if (!tok_type_(tline, TOK_NUMBER))
+                   error(ERR_NONFATAL,
+                         "`%s' expects a parameter count after `-'",
+                         directives[i]);
+               else
+               {
+                   searching.nparam_max = readnum(tline->text, &j);
+                   if (j)
+                       error(ERR_NONFATAL,
+                               "unable to parse parameter count `%s'",
+                               tline->text);
+                   if (searching.nparam_min > searching.nparam_max)
+                       error(ERR_NONFATAL,
+                               "minimum parameter count exceeds maximum");
+               }
+           }
+           if (tline && tok_is_(tline->next, "+"))
+           {
+               tline = tline->next;
+               searching.plus = TRUE;
+           }
+           mmac = mmacros[hash(searching.name)];
+           while (mmac)
+           {
+               if (!strcmp(mmac->name, searching.name) &&
+                       (mmac->nparam_min <= searching.nparam_max
+                               || searching.plus)
+                       && (searching.nparam_min <= mmac->nparam_max
+                               || mmac->plus))
+               {
+                   found = TRUE;
+                   break;
+               }
+               mmac = mmac->next;
+           }
+           nasm_free(searching.name);
+           free_tlist(origline);
+           if (i == PP_IFNMACRO || i == PP_ELIFNMACRO)
+               found = !found;
+           return found;
+       }
+
+       case PP_IFID:
+       case PP_ELIFID:
+       case PP_IFNID:
+       case PP_ELIFNID:
+       case PP_IFNUM:
+       case PP_ELIFNUM:
+       case PP_IFNNUM:
+       case PP_ELIFNNUM:
+       case PP_IFSTR:
+       case PP_ELIFSTR:
+       case PP_IFNSTR:
+       case PP_ELIFNSTR:
+           tline = expand_smacro(tline);
+           t = tline;
+           while (tok_type_(t, TOK_WHITESPACE))
+               t = t->next;
+           j = FALSE;          /* placate optimiser */
+           if (t)
+               switch (i)
+               {
+                   case PP_IFID:
+                   case PP_ELIFID:
+                   case PP_IFNID:
+                   case PP_ELIFNID:
+                       j = (t->type == TOK_ID);
+                       break;
+                   case PP_IFNUM:
+                   case PP_ELIFNUM:
+                   case PP_IFNNUM:
+                   case PP_ELIFNNUM:
+                       j = (t->type == TOK_NUMBER);
+                       break;
+                   case PP_IFSTR:
+                   case PP_ELIFSTR:
+                   case PP_IFNSTR:
+                   case PP_ELIFNSTR:
+                       j = (t->type == TOK_STRING);
+                       break;
+               }
+           if (i == PP_IFNID || i == PP_ELIFNID ||
+                   i == PP_IFNNUM || i == PP_ELIFNNUM ||
+                   i == PP_IFNSTR || i == PP_ELIFNSTR)
+               j = !j;
+           free_tlist(tline);
+           return j;
+
+       case PP_IF:
+       case PP_ELIF:
+           t = tline = expand_smacro(tline);
+           tptr = &t;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult = evaluate(ppscan, tptr, &tokval,
+                   NULL, pass | CRITICAL, error, NULL);
+           free_tlist(tline);
+           if (!evalresult)
+               return -1;
+           if (tokval.t_type)
+               error(ERR_WARNING,
+                       "trailing garbage after expression ignored");
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL,
+                       "non-constant value given to `%s'", directives[i]);
+               return -1;
+           }
+           return reloc_value(evalresult) != 0;
+
+       default:
+           error(ERR_FATAL,
+                   "preprocessor directive `%s' not yet implemented",
+                   directives[i]);
+           free_tlist(origline);
+           return -1;          /* yeah, right */
+    }
+}
+
+/*
+ * Expand macros in a string. Used in %error and %include directives.
+ * First tokenise the string, apply "expand_smacro" and then de-tokenise back.
+ * The returned variable should ALWAYS be freed after usage.
+ */
+void
+expand_macros_in_string(char **p)
+{
+    Token *line = tokenise(*p);
+    line = expand_smacro(line);
+    *p = detoken(line, FALSE);
+}
+
+/**
+ * find and process preprocessor directive in passed line
+ * Find out if a line contains a preprocessor directive, and deal
+ * with it if so.
+ * 
+ * If a directive _is_ found, it is the responsibility of this routine
+ * (and not the caller) to free_tlist() the line.
+ *
+ * @param tline a pointer to the current tokeninzed line linked list
+ * @return DIRECTIVE_FOUND or NO_DIRECTIVE_FOUND
+ * 
+ */
+static int
+do_directive(Token * tline)
+{
+    int i, j, k, m, nparam, nolist;
+    int offset;
+    char *p, *mname;
+    Include *inc;
+    Context *ctx;
+    Cond *cond;
+    SMacro *smac, **smhead;
+    MMacro *mmac;
+    Token *t, *tt, *param_start, *macro_start, *last, **tptr, *origline;
+    Line *l;
+    struct tokenval tokval;
+    expr *evalresult;
+    MMacro *tmp_defining;      /* Used when manipulating rep_nest */
+
+    origline = tline;
+
+    skip_white_(tline);
+    if (!tok_type_(tline, TOK_PREPROC_ID) ||
+           (tline->text[1] == '%' || tline->text[1] == '$'
+                   || tline->text[1] == '!'))
+       return NO_DIRECTIVE_FOUND;
+
+    i = -1;
+    j = elements(directives);
+    while (j - i > 1)
+    {
+       k = (j + i) / 2;
+       m = nasm_stricmp(tline->text, directives[k]);
+       if (m == 0) {
+               if (tasm_compatible_mode) {
+               i = k;
+               j = -2;
+               } else if (k != PP_ARG && k != PP_LOCAL && k != PP_STACKSIZE) {
+                   i = k;
+               j = -2;
+               }
+           break;
+       }
+       else if (m < 0) {
+           j = k;
+       }
+       else
+           i = k;
+    }
+
+    /*
+     * If we're in a non-emitting branch of a condition construct,
+     * or walking to the end of an already terminated %rep block,
+     * we should ignore all directives except for condition
+     * directives.
+     */
+    if (((istk->conds && !emitting(istk->conds->state)) ||
+        (istk->mstk && !istk->mstk->in_progress)) &&
+       !is_condition(i))
+    {
+       return NO_DIRECTIVE_FOUND;
+    }
+
+    /*
+     * If we're defining a macro or reading a %rep block, we should
+     * ignore all directives except for %macro/%imacro (which
+     * generate an error), %endm/%endmacro, and (only if we're in a
+     * %rep block) %endrep. If we're in a %rep block, another %rep
+     * causes an error, so should be let through.
+     */
+    if (defining && i != PP_MACRO && i != PP_IMACRO &&
+           i != PP_ENDMACRO && i != PP_ENDM &&
+           (defining->name || (i != PP_ENDREP && i != PP_REP)))
+    {
+       return NO_DIRECTIVE_FOUND;
+    }
+
+    if (j != -2)
+    {
+       error(ERR_NONFATAL, "unknown preprocessor directive `%s'",
+               tline->text);
+       return NO_DIRECTIVE_FOUND;              /* didn't get it */
+    }
+
+    switch (i)
+    {
+       case PP_STACKSIZE:
+           /* Directive to tell NASM what the default stack size is. The
+            * default is for a 16-bit stack, and this can be overriden with
+            * %stacksize large.
+            * the following form:
+            *
+            *      ARG arg1:WORD, arg2:DWORD, arg4:QWORD
+            */
+           tline = tline->next;
+           if (tline && tline->type == TOK_WHITESPACE)
+               tline = tline->next;
+           if (!tline || tline->type != TOK_ID)
+           {
+               error(ERR_NONFATAL, "`%%stacksize' missing size parameter");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           if (nasm_stricmp(tline->text, "flat") == 0)
+           {
+               /* All subsequent ARG directives are for a 32-bit stack */
+               StackSize = 4;
+               StackPointer = "ebp";
+               ArgOffset = 8;
+               LocalOffset = 4;
+           }
+           else if (nasm_stricmp(tline->text, "large") == 0)
+           {
+               /* All subsequent ARG directives are for a 16-bit stack,
+                * far function call.
+                */
+               StackSize = 2;
+               StackPointer = "bp";
+               ArgOffset = 4;
+               LocalOffset = 2;
+           }
+           else if (nasm_stricmp(tline->text, "small") == 0)
+           {
+               /* All subsequent ARG directives are for a 16-bit stack,
+                  * far function call. We don't support near functions.
+                */
+               StackSize = 2;
+               StackPointer = "bp";
+               ArgOffset = 6;
+               LocalOffset = 2;
+           }
+           else
+           {
+               error(ERR_NONFATAL, "`%%stacksize' invalid size type");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_ARG:
+           /* TASM like ARG directive to define arguments to functions, in
+            * the following form:
+            *
+            *      ARG arg1:WORD, arg2:DWORD, arg4:QWORD
+            */
+           offset = ArgOffset;
+           do
+           {
+               char *arg, directive[256];
+               int size = StackSize;
+
+               /* Find the argument name */
+               tline = tline->next;
+               if (tline && tline->type == TOK_WHITESPACE)
+                   tline = tline->next;
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL, "`%%arg' missing argument parameter");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               arg = tline->text;
+
+               /* Find the argument size type */
+               tline = tline->next;
+               if (!tline || tline->type != TOK_OTHER
+                       || tline->text[0] != ':')
+               {
+                   error(ERR_NONFATAL,
+                           "Syntax error processing `%%arg' directive");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               tline = tline->next;
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL,
+                           "`%%arg' missing size type parameter");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+
+               /* Allow macro expansion of type parameter */
+               tt = tokenise(tline->text);
+               tt = expand_smacro(tt);
+               if (nasm_stricmp(tt->text, "byte") == 0)
+               {
+                   size = MAX(StackSize, 1);
+               }
+               else if (nasm_stricmp(tt->text, "word") == 0)
+               {
+                   size = MAX(StackSize, 2);
+               }
+               else if (nasm_stricmp(tt->text, "dword") == 0)
+               {
+                   size = MAX(StackSize, 4);
+               }
+               else if (nasm_stricmp(tt->text, "qword") == 0)
+               {
+                   size = MAX(StackSize, 8);
+               }
+               else if (nasm_stricmp(tt->text, "tword") == 0)
+               {
+                   size = MAX(StackSize, 10);
+               }
+               else
+               {
+                   error(ERR_NONFATAL,
+                           "Invalid size type for `%%arg' missing directive");
+                   free_tlist(tt);
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               free_tlist(tt);
+
+               /* Now define the macro for the argument */
+               sprintf(directive, "%%define %s (%s+%d)", arg, StackPointer,
+                       offset);
+               do_directive(tokenise(directive));
+               offset += size;
+
+               /* Move to the next argument in the list */
+               tline = tline->next;
+               if (tline && tline->type == TOK_WHITESPACE)
+                   tline = tline->next;
+           }
+           while (tline && tline->type == TOK_OTHER
+                   && tline->text[0] == ',');
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_LOCAL:
+           /* TASM like LOCAL directive to define local variables for a
+            * function, in the following form:
+            *
+            *      LOCAL local1:WORD, local2:DWORD, local4:QWORD = LocalSize
+            *
+            * The '= LocalSize' at the end is ignored by NASM, but is
+            * required by TASM to define the local parameter size (and used
+            * by the TASM macro package).
+            */
+           offset = LocalOffset;
+           do
+           {
+               char *local, directive[256];
+               int size = StackSize;
+
+               /* Find the argument name */
+               tline = tline->next;
+               if (tline && tline->type == TOK_WHITESPACE)
+                   tline = tline->next;
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL,
+                           "`%%local' missing argument parameter");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               local = tline->text;
+
+               /* Find the argument size type */
+               tline = tline->next;
+               if (!tline || tline->type != TOK_OTHER
+                       || tline->text[0] != ':')
+               {
+                   error(ERR_NONFATAL,
+                           "Syntax error processing `%%local' directive");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               tline = tline->next;
+               if (!tline || tline->type != TOK_ID)
+               {
+                   error(ERR_NONFATAL,
+                           "`%%local' missing size type parameter");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+
+               /* Allow macro expansion of type parameter */
+               tt = tokenise(tline->text);
+               tt = expand_smacro(tt);
+               if (nasm_stricmp(tt->text, "byte") == 0)
+               {
+                   size = MAX(StackSize, 1);
+               }
+               else if (nasm_stricmp(tt->text, "word") == 0)
+               {
+                   size = MAX(StackSize, 2);
+               }
+               else if (nasm_stricmp(tt->text, "dword") == 0)
+               {
+                   size = MAX(StackSize, 4);
+               }
+               else if (nasm_stricmp(tt->text, "qword") == 0)
+               {
+                   size = MAX(StackSize, 8);
+               }
+               else if (nasm_stricmp(tt->text, "tword") == 0)
+               {
+                   size = MAX(StackSize, 10);
+               }
+               else
+               {
+                   error(ERR_NONFATAL,
+                           "Invalid size type for `%%local' missing directive");
+                   free_tlist(tt);
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               free_tlist(tt);
+
+               /* Now define the macro for the argument */
+               sprintf(directive, "%%define %s (%s-%d)", local, StackPointer,
+                       offset);
+               do_directive(tokenise(directive));
+               offset += size;
+
+               /* Now define the assign to setup the enter_c macro correctly */
+               sprintf(directive, "%%assign %%$localsize %%$localsize+%d",
+                       size);
+               do_directive(tokenise(directive));
+
+               /* Move to the next argument in the list */
+               tline = tline->next;
+               if (tline && tline->type == TOK_WHITESPACE)
+                   tline = tline->next;
+           }
+           while (tline && tline->type == TOK_OTHER
+                   && tline->text[0] == ',');
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_CLEAR:
+           if (tline->next)
+               error(ERR_WARNING,
+                       "trailing garbage after `%%clear' ignored");
+           for (j = 0; j < NHASH; j++)
+           {
+               while (mmacros[j])
+               {
+                   MMacro *m = mmacros[j];
+                   mmacros[j] = m->next;
+                   free_mmacro(m);
+               }
+               while (smacros[j])
+               {
+                   SMacro *s = smacros[j];
+                   smacros[j] = smacros[j]->next;
+                   nasm_free(s->name);
+                   free_tlist(s->expansion);
+                   nasm_free(s);
+               }
+           }
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_INCLUDE:
+           tline = tline->next;
+           skip_white_(tline);
+           if (!tline || (tline->type != TOK_STRING &&
+                           tline->type != TOK_INTERNAL_STRING))
+           {
+               error(ERR_NONFATAL, "`%%include' expects a file name");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND; /* but we did _something_ */
+           }
+           if (tline->next)
+               error(ERR_WARNING,
+                       "trailing garbage after `%%include' ignored");
+           if (tline->type != TOK_INTERNAL_STRING)
+           {
+               p = tline->text + 1;    /* point past the quote to the name */
+               p[strlen(p) - 1] = '\0';        /* remove the trailing quote */
+           }
+           else
+               p = tline->text;        /* internal_string is easier */
+           expand_macros_in_string(&p);
+           inc = nasm_malloc(sizeof(Include));
+           inc->next = istk;
+           inc->conds = NULL;
+           inc->fp = inc_fopen(p);
+           inc->fname = src_set_fname(p);
+           inc->lineno = src_set_linnum(0);
+           inc->lineinc = 1;
+           inc->expansion = NULL;
+           inc->mstk = NULL;
+           istk = inc;
+           list->uplevel(LIST_INCLUDE);
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_PUSH:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tok_type_(tline, TOK_ID))
+           {
+               error(ERR_NONFATAL, "`%%push' expects a context identifier");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND; /* but we did _something_ */
+           }
+           if (tline->next)
+               error(ERR_WARNING, "trailing garbage after `%%push' ignored");
+           ctx = nasm_malloc(sizeof(Context));
+           ctx->next = cstk;
+           ctx->localmac = NULL;
+           ctx->name = nasm_strdup(tline->text);
+           ctx->number = unique++;
+           cstk = ctx;
+           free_tlist(origline);
+           break;
+
+       case PP_REPL:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tok_type_(tline, TOK_ID))
+           {
+               error(ERR_NONFATAL, "`%%repl' expects a context identifier");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND; /* but we did _something_ */
+           }
+           if (tline->next)
+               error(ERR_WARNING, "trailing garbage after `%%repl' ignored");
+           if (!cstk)
+               error(ERR_NONFATAL, "`%%repl': context stack is empty");
+           else
+           {
+               nasm_free(cstk->name);
+               cstk->name = nasm_strdup(tline->text);
+           }
+           free_tlist(origline);
+           break;
+
+       case PP_POP:
+           if (tline->next)
+               error(ERR_WARNING, "trailing garbage after `%%pop' ignored");
+           if (!cstk)
+               error(ERR_NONFATAL,
+                       "`%%pop': context stack is already empty");
+           else
+               ctx_pop();
+           free_tlist(origline);
+           break;
+
+       case PP_ERROR:
+           tline->next = expand_smacro(tline->next);
+           tline = tline->next;
+           skip_white_(tline);
+           if (tok_type_(tline, TOK_STRING))
+           {
+               p = tline->text + 1;    /* point past the quote to the name */
+               p[strlen(p) - 1] = '\0';        /* remove the trailing quote */
+               expand_macros_in_string(&p);
+               error(ERR_NONFATAL, "%s", p);
+               nasm_free(p);
+           }
+           else
+           {
+               p = detoken(tline, FALSE);
+               error(ERR_WARNING, "%s", p);
+               nasm_free(p);
+           }
+           free_tlist(origline);
+           break;
+
+       case PP_IF:
+       case PP_IFCTX:
+       case PP_IFDEF:
+       case PP_IFID:
+       case PP_IFIDN:
+       case PP_IFIDNI:
+        case PP_IFMACRO:
+       case PP_IFNCTX:
+       case PP_IFNDEF:
+       case PP_IFNID:
+       case PP_IFNIDN:
+       case PP_IFNIDNI:
+        case PP_IFNMACRO:
+       case PP_IFNNUM:
+       case PP_IFNSTR:
+       case PP_IFNUM:
+       case PP_IFSTR:
+           if (istk->conds && !emitting(istk->conds->state))
+               j = COND_NEVER;
+           else
+           {
+               j = if_condition(tline->next, i);
+               tline->next = NULL;     /* it got freed */
+               free_tlist(origline);
+               j = j < 0 ? COND_NEVER : j ? COND_IF_TRUE : COND_IF_FALSE;
+           }
+           cond = nasm_malloc(sizeof(Cond));
+           cond->next = istk->conds;
+           cond->state = j;
+           istk->conds = cond;
+           return DIRECTIVE_FOUND;
+
+       case PP_ELIF:
+       case PP_ELIFCTX:
+       case PP_ELIFDEF:
+       case PP_ELIFID:
+       case PP_ELIFIDN:
+       case PP_ELIFIDNI:
+        case PP_ELIFMACRO:
+       case PP_ELIFNCTX:
+       case PP_ELIFNDEF:
+       case PP_ELIFNID:
+       case PP_ELIFNIDN:
+       case PP_ELIFNIDNI:
+        case PP_ELIFNMACRO:
+       case PP_ELIFNNUM:
+       case PP_ELIFNSTR:
+       case PP_ELIFNUM:
+       case PP_ELIFSTR:
+           if (!istk->conds)
+               error(ERR_FATAL, "`%s': no matching `%%if'", directives[i]);
+           if (emitting(istk->conds->state)
+                   || istk->conds->state == COND_NEVER)
+               istk->conds->state = COND_NEVER;
+           else
+           {
+               /*
+                * IMPORTANT: In the case of %if, we will already have
+                * called expand_mmac_params(); however, if we're
+                * processing an %elif we must have been in a
+                * non-emitting mode, which would have inhibited
+                * the normal invocation of expand_mmac_params().  Therefore,
+                * we have to do it explicitly here.
+                */
+                j = if_condition(expand_mmac_params(tline->next), i);
+                tline->next = NULL; /* it got freed */
+               free_tlist(origline);
+               istk->conds->state =
+                       j < 0 ? COND_NEVER : j ? COND_IF_TRUE : COND_IF_FALSE;
+           }
+           return DIRECTIVE_FOUND;
+
+       case PP_ELSE:
+           if (tline->next)
+               error(ERR_WARNING, "trailing garbage after `%%else' ignored");
+           if (!istk->conds)
+               error(ERR_FATAL, "`%%else': no matching `%%if'");
+           if (emitting(istk->conds->state)
+                   || istk->conds->state == COND_NEVER)
+               istk->conds->state = COND_ELSE_FALSE;
+           else
+               istk->conds->state = COND_ELSE_TRUE;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_ENDIF:
+           if (tline->next)
+               error(ERR_WARNING,
+                       "trailing garbage after `%%endif' ignored");
+           if (!istk->conds)
+               error(ERR_FATAL, "`%%endif': no matching `%%if'");
+           cond = istk->conds;
+           istk->conds = cond->next;
+           nasm_free(cond);
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_MACRO:
+       case PP_IMACRO:
+           if (defining)
+               error(ERR_FATAL,
+                       "`%%%smacro': already defining a macro",
+                       (i == PP_IMACRO ? "i" : ""));
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tok_type_(tline, TOK_ID))
+           {
+               error(ERR_NONFATAL,
+                       "`%%%smacro' expects a macro name",
+                       (i == PP_IMACRO ? "i" : ""));
+               return DIRECTIVE_FOUND;
+           }
+           defining = nasm_malloc(sizeof(MMacro));
+           defining->name = nasm_strdup(tline->text);
+           defining->casesense = (i == PP_MACRO);
+           defining->plus = FALSE;
+           defining->nolist = FALSE;
+           defining->in_progress = FALSE;
+           defining->rep_nest = NULL;
+           tline = expand_smacro(tline->next);
+           skip_white_(tline);
+           if (!tok_type_(tline, TOK_NUMBER))
+           {
+               error(ERR_NONFATAL,
+                       "`%%%smacro' expects a parameter count",
+                       (i == PP_IMACRO ? "i" : ""));
+               defining->nparam_min = defining->nparam_max = 0;
+           }
+           else
+           {
+               defining->nparam_min = defining->nparam_max =
+                       readnum(tline->text, &j);
+               if (j)
+                   error(ERR_NONFATAL,
+                           "unable to parse parameter count `%s'",
+                           tline->text);
+           }
+           if (tline && tok_is_(tline->next, "-"))
+           {
+               tline = tline->next->next;
+               if (tok_is_(tline, "*"))
+                   defining->nparam_max = INT_MAX;
+               else if (!tok_type_(tline, TOK_NUMBER))
+                   error(ERR_NONFATAL,
+                           "`%%%smacro' expects a parameter count after `-'",
+                           (i == PP_IMACRO ? "i" : ""));
+               else
+               {
+                   defining->nparam_max = readnum(tline->text, &j);
+                   if (j)
+                       error(ERR_NONFATAL,
+                               "unable to parse parameter count `%s'",
+                               tline->text);
+                   if (defining->nparam_min > defining->nparam_max)
+                       error(ERR_NONFATAL,
+                               "minimum parameter count exceeds maximum");
+               }
+           }
+           if (tline && tok_is_(tline->next, "+"))
+           {
+               tline = tline->next;
+               defining->plus = TRUE;
+           }
+           if (tline && tok_type_(tline->next, TOK_ID) &&
+                   !nasm_stricmp(tline->next->text, ".nolist"))
+           {
+               tline = tline->next;
+               defining->nolist = TRUE;
+           }
+           mmac = mmacros[hash(defining->name)];
+           while (mmac)
+           {
+               if (!strcmp(mmac->name, defining->name) &&
+                       (mmac->nparam_min <= defining->nparam_max
+                               || defining->plus)
+                       && (defining->nparam_min <= mmac->nparam_max
+                               || mmac->plus))
+               {
+                   error(ERR_WARNING,
+                           "redefining multi-line macro `%s'",
+                           defining->name);
+                   break;
+               }
+               mmac = mmac->next;
+           }
+           /*
+            * Handle default parameters.
+            */
+           if (tline && tline->next)
+           {
+               defining->dlist = tline->next;
+               tline->next = NULL;
+               count_mmac_params(defining->dlist, &defining->ndefs,
+                       &defining->defaults);
+           }
+           else
+           {
+               defining->dlist = NULL;
+               defining->defaults = NULL;
+           }
+           defining->expansion = NULL;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_ENDM:
+       case PP_ENDMACRO:
+           if (!defining)
+           {
+               error(ERR_NONFATAL, "`%s': not defining a macro",
+                       tline->text);
+               return DIRECTIVE_FOUND;
+           }
+           k = hash(defining->name);
+           defining->next = mmacros[k];
+           mmacros[k] = defining;
+           defining = NULL;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_ROTATE:
+           if (tline->next && tline->next->type == TOK_WHITESPACE)
+               tline = tline->next;
+           if (tline->next == NULL)
+           {
+               free_tlist(origline);
+               error(ERR_NONFATAL, "`%%rotate' missing rotate count");
+               return DIRECTIVE_FOUND;
+           }
+           t = expand_smacro(tline->next);
+           tline->next = NULL;
+           free_tlist(origline);
+           tline = t;
+           tptr = &t;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult =
+                   evaluate(ppscan, tptr, &tokval, NULL, pass, error, NULL);
+           free_tlist(tline);
+           if (!evalresult)
+               return DIRECTIVE_FOUND;
+           if (tokval.t_type)
+               error(ERR_WARNING,
+                       "trailing garbage after expression ignored");
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL, "non-constant value given to `%%rotate'");
+               return DIRECTIVE_FOUND;
+           }
+           mmac = istk->mstk;
+           while (mmac && !mmac->name) /* avoid mistaking %reps for macros */
+               mmac = mmac->next_active;
+           if (!mmac)
+           {
+               error(ERR_NONFATAL,
+                       "`%%rotate' invoked outside a macro call");
+           } 
+           else if (mmac->nparam == 0)
+           {
+               error(ERR_NONFATAL,
+                       "`%%rotate' invoked within macro without parameters");
+           }
+           else
+           {
+               mmac->rotate = mmac->rotate + reloc_value(evalresult);
+               
+               if (mmac->rotate < 0)
+                   mmac->rotate = 
+                       mmac->nparam - (-mmac->rotate) % mmac->nparam;
+               mmac->rotate %= mmac->nparam;
+           }
+           return DIRECTIVE_FOUND;
+
+       case PP_REP:
+           nolist = FALSE;
+           tline = tline->next;
+           if (tline->next && tline->next->type == TOK_WHITESPACE)
+               tline = tline->next;
+           if (tline->next && tline->next->type == TOK_ID &&
+                   !nasm_stricmp(tline->next->text, ".nolist"))
+           {
+               tline = tline->next;
+               nolist = TRUE;
+           }
+           t = expand_smacro(tline->next);
+           tline->next = NULL;
+           free_tlist(origline);
+           tline = t;
+           tptr = &t;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult =
+                   evaluate(ppscan, tptr, &tokval, NULL, pass, error, NULL);
+           free_tlist(tline);
+           if (!evalresult)
+               return DIRECTIVE_FOUND;
+           if (tokval.t_type)
+               error(ERR_WARNING,
+                       "trailing garbage after expression ignored");
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL, "non-constant value given to `%%rep'");
+               return DIRECTIVE_FOUND;
+           }
+           tmp_defining = defining;
+           defining = nasm_malloc(sizeof(MMacro));
+           defining->name = NULL;      /* flags this macro as a %rep block */
+           defining->casesense = 0;
+           defining->plus = FALSE;
+           defining->nolist = nolist;
+           defining->in_progress = reloc_value(evalresult) + 1;
+           defining->nparam_min = defining->nparam_max = 0;
+           defining->defaults = NULL;
+           defining->dlist = NULL;
+           defining->expansion = NULL;
+           defining->next_active = istk->mstk;
+           defining->rep_nest = tmp_defining;
+           return DIRECTIVE_FOUND;
+
+       case PP_ENDREP:
+           if (!defining || defining->name)
+           {
+               error(ERR_NONFATAL, "`%%endrep': no matching `%%rep'");
+               return DIRECTIVE_FOUND;
+           }
+
+           /*
+            * Now we have a "macro" defined - although it has no name
+            * and we won't be entering it in the hash tables - we must
+            * push a macro-end marker for it on to istk->expansion.
+            * After that, it will take care of propagating itself (a
+            * macro-end marker line for a macro which is really a %rep
+            * block will cause the macro to be re-expanded, complete
+            * with another macro-end marker to ensure the process
+            * continues) until the whole expansion is forcibly removed
+            * from istk->expansion by a %exitrep.
+            */
+           l = nasm_malloc(sizeof(Line));
+           l->next = istk->expansion;
+           l->finishes = defining;
+           l->first = NULL;
+           istk->expansion = l;
+
+           istk->mstk = defining;
+
+           list->uplevel(defining->nolist ? LIST_MACRO_NOLIST : LIST_MACRO);
+           tmp_defining = defining;
+           defining = defining->rep_nest;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_EXITREP:
+           /*
+            * We must search along istk->expansion until we hit a
+            * macro-end marker for a macro with no name. Then we set
+            * its `in_progress' flag to 0.
+            */
+           for (l = istk->expansion; l; l = l->next)
+               if (l->finishes && !l->finishes->name)
+                   break;
+
+           if (l)
+               l->finishes->in_progress = 0;
+           else
+               error(ERR_NONFATAL, "`%%exitrep' not within `%%rep' block");
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_XDEFINE:
+       case PP_IXDEFINE:
+       case PP_DEFINE:
+       case PP_IDEFINE:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL,
+                       "`%%%s%sdefine' expects a macro identifier",
+                       ((i == PP_IDEFINE || i == PP_IXDEFINE) ? "i" : ""),
+                       ((i == PP_XDEFINE || i == PP_IXDEFINE) ? "x" : ""));
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+           mname = tline->text;
+           last = tline;
+           param_start = tline = tline->next;
+           nparam = 0;
+
+           /* Expand the macro definition now for %xdefine and %ixdefine */
+           if ((i == PP_XDEFINE) || (i == PP_IXDEFINE))
+               tline = expand_smacro(tline);
+
+           if (tok_is_(tline, "("))
+           {
+               /*
+                * This macro has parameters.
+                */
+
+               tline = tline->next;
+               while (1)
+               {
+                   skip_white_(tline);
+                   if (!tline)
+                   {
+                       error(ERR_NONFATAL, "parameter identifier expected");
+                       free_tlist(origline);
+                       return DIRECTIVE_FOUND;
+                   }
+                   if (tline->type != TOK_ID)
+                   {
+                       error(ERR_NONFATAL,
+                               "`%s': parameter identifier expected",
+                               tline->text);
+                       free_tlist(origline);
+                       return DIRECTIVE_FOUND;
+                   }
+                   tline->type = TOK_SMAC_PARAM + nparam++;
+                   tline = tline->next;
+                   skip_white_(tline);
+                   if (tok_is_(tline, ","))
+                   {
+                       tline = tline->next;
+                       continue;
+                   }
+                   if (!tok_is_(tline, ")"))
+                   {
+                       error(ERR_NONFATAL,
+                               "`)' expected to terminate macro template");
+                       free_tlist(origline);
+                       return DIRECTIVE_FOUND;
+                   }
+                   break;
+               }
+               last = tline;
+               tline = tline->next;
+           }
+           if (tok_type_(tline, TOK_WHITESPACE))
+               last = tline, tline = tline->next;
+           macro_start = NULL;
+           last->next = NULL;
+           t = tline;
+           while (t)
+           {
+               if (t->type == TOK_ID)
+               {
+                   for (tt = param_start; tt; tt = tt->next)
+                       if (tt->type >= TOK_SMAC_PARAM &&
+                               !strcmp(tt->text, t->text))
+                           t->type = tt->type;
+               }
+               tt = t->next;
+               t->next = macro_start;
+               macro_start = t;
+               t = tt;
+           }
+           /*
+            * Good. We now have a macro name, a parameter count, and a
+            * token list (in reverse order) for an expansion. We ought
+            * to be OK just to create an SMacro, store it, and let
+            * free_tlist have the rest of the line (which we have
+            * carefully re-terminated after chopping off the expansion
+            * from the end).
+            */
+           if (smacro_defined(ctx, mname, nparam, &smac, i == PP_DEFINE))
+           {
+               if (!smac)
+               {
+                   error(ERR_WARNING,
+                           "single-line macro `%s' defined both with and"
+                           " without parameters", mname);
+                   free_tlist(origline);
+                   free_tlist(macro_start);
+                   return DIRECTIVE_FOUND;
+               }
+               else
+               {
+                   /*
+                    * We're redefining, so we have to take over an
+                    * existing SMacro structure. This means freeing
+                    * what was already in it.
+                    */
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+               }
+           }
+           else
+           {
+               smac = nasm_malloc(sizeof(SMacro));
+               smac->next = *smhead;
+               *smhead = smac;
+           }
+           smac->name = nasm_strdup(mname);
+           smac->casesense = ((i == PP_DEFINE) || (i == PP_XDEFINE));
+           smac->nparam = nparam;
+           smac->expansion = macro_start;
+           smac->in_progress = FALSE;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_UNDEF:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL, "`%%undef' expects a macro identifier");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           if (tline->next)
+           {
+               error(ERR_WARNING,
+                       "trailing garbage after macro name ignored");
+           }
+
+           /* Find the context that symbol belongs to */
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+
+           mname = tline->text;
+           last = tline;
+           last->next = NULL;
+
+           /*
+            * We now have a macro name... go hunt for it.
+            */
+           while (smacro_defined(ctx, mname, -1, &smac, 1))
+           {
+               /* Defined, so we need to find its predecessor and nuke it */
+               SMacro **s;
+               for (s = smhead; *s && *s != smac; s = &(*s)->next);
+               if (*s)
+               {
+                   *s = smac->next;
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+                   nasm_free(smac);
+               }
+           }
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_STRLEN:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL,
+                       "`%%strlen' expects a macro identifier as first parameter");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+           mname = tline->text;
+           last = tline;
+           tline = expand_smacro(tline->next);
+           last->next = NULL;
+
+           t = tline;
+           while (tok_type_(t, TOK_WHITESPACE))
+               t = t->next;
+           /* t should now point to the string */
+           if (t->type != TOK_STRING)
+           {
+               error(ERR_NONFATAL,
+                       "`%%strlen` requires string as second parameter");
+               free_tlist(tline);
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           macro_start = nasm_malloc(sizeof(*macro_start));
+           macro_start->next = NULL;
+           make_tok_num(macro_start, strlen(t->text) - 2);
+           macro_start->mac = NULL;
+
+           /*
+            * We now have a macro name, an implicit parameter count of
+            * zero, and a numeric token to use as an expansion. Create
+            * and store an SMacro.
+            */
+           if (smacro_defined(ctx, mname, 0, &smac, i == PP_STRLEN))
+           {
+               if (!smac)
+                   error(ERR_WARNING,
+                           "single-line macro `%s' defined both with and"
+                           " without parameters", mname);
+               else
+               {
+                   /*
+                    * We're redefining, so we have to take over an
+                    * existing SMacro structure. This means freeing
+                    * what was already in it.
+                    */
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+               }
+           }
+           else
+           {
+               smac = nasm_malloc(sizeof(SMacro));
+               smac->next = *smhead;
+               *smhead = smac;
+           }
+           smac->name = nasm_strdup(mname);
+           smac->casesense = (i == PP_STRLEN);
+           smac->nparam = 0;
+           smac->expansion = macro_start;
+           smac->in_progress = FALSE;
+           free_tlist(tline);
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_SUBSTR:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL,
+                       "`%%substr' expects a macro identifier as first parameter");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+           mname = tline->text;
+           last = tline;
+           tline = expand_smacro(tline->next);
+           last->next = NULL;
+
+           t = tline->next;
+           while (tok_type_(t, TOK_WHITESPACE))
+               t = t->next;
+
+           /* t should now point to the string */
+           if (t->type != TOK_STRING)
+           {
+               error(ERR_NONFATAL,
+                       "`%%substr` requires string as second parameter");
+               free_tlist(tline);
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           tt = t->next;
+           tptr = &tt;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult =
+                   evaluate(ppscan, tptr, &tokval, NULL, pass, error, NULL);
+           if (!evalresult)
+           {
+               free_tlist(tline);
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL, "non-constant value given to `%%substr`");
+               free_tlist(tline);
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           macro_start = nasm_malloc(sizeof(*macro_start));
+           macro_start->next = NULL;
+           macro_start->text = nasm_strdup("'''");
+           if (evalresult->value > 0
+                   && evalresult->value < strlen(t->text) - 1)
+           {
+               macro_start->text[1] = t->text[evalresult->value];
+           }
+           else
+           {
+               macro_start->text[2] = '\0';
+           }
+           macro_start->type = TOK_STRING;
+           macro_start->mac = NULL;
+
+           /*
+            * We now have a macro name, an implicit parameter count of
+            * zero, and a numeric token to use as an expansion. Create
+            * and store an SMacro.
+            */
+           if (smacro_defined(ctx, mname, 0, &smac, i == PP_SUBSTR))
+           {
+               if (!smac)
+                   error(ERR_WARNING,
+                           "single-line macro `%s' defined both with and"
+                           " without parameters", mname);
+               else
+               {
+                   /*
+                    * We're redefining, so we have to take over an
+                    * existing SMacro structure. This means freeing
+                    * what was already in it.
+                    */
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+               }
+           }
+           else
+           {
+               smac = nasm_malloc(sizeof(SMacro));
+               smac->next = *smhead;
+               *smhead = smac;
+           }
+           smac->name = nasm_strdup(mname);
+           smac->casesense = (i == PP_SUBSTR);
+           smac->nparam = 0;
+           smac->expansion = macro_start;
+           smac->in_progress = FALSE;
+           free_tlist(tline);
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+
+       case PP_ASSIGN:
+       case PP_IASSIGN:
+           tline = tline->next;
+           skip_white_(tline);
+           tline = expand_id(tline);
+           if (!tline || (tline->type != TOK_ID &&
+                           (tline->type != TOK_PREPROC_ID ||
+                                   tline->text[1] != '$')))
+           {
+               error(ERR_NONFATAL,
+                       "`%%%sassign' expects a macro identifier",
+                       (i == PP_IASSIGN ? "i" : ""));
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           ctx = get_ctx(tline->text, FALSE);
+           if (!ctx)
+               smhead = &smacros[hash(tline->text)];
+           else
+               smhead = &ctx->localmac;
+           mname = tline->text;
+           last = tline;
+           tline = expand_smacro(tline->next);
+           last->next = NULL;
+
+           t = tline;
+           tptr = &t;
+           tokval.t_type = TOKEN_INVALID;
+           evalresult =
+                   evaluate(ppscan, tptr, &tokval, NULL, pass, error, NULL);
+           free_tlist(tline);
+           if (!evalresult)
+           {
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           if (tokval.t_type)
+               error(ERR_WARNING,
+                       "trailing garbage after expression ignored");
+
+           if (!is_simple(evalresult))
+           {
+               error(ERR_NONFATAL,
+                       "non-constant value given to `%%%sassign'",
+                       (i == PP_IASSIGN ? "i" : ""));
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+
+           macro_start = nasm_malloc(sizeof(*macro_start));
+           macro_start->next = NULL;
+           make_tok_num(macro_start, reloc_value(evalresult));
+           macro_start->mac = NULL;
+
+           /*
+            * We now have a macro name, an implicit parameter count of
+            * zero, and a numeric token to use as an expansion. Create
+            * and store an SMacro.
+            */
+           if (smacro_defined(ctx, mname, 0, &smac, i == PP_ASSIGN))
+           {
+               if (!smac)
+                   error(ERR_WARNING,
+                           "single-line macro `%s' defined both with and"
+                           " without parameters", mname);
+               else
+               {
+                   /*
+                    * We're redefining, so we have to take over an
+                    * existing SMacro structure. This means freeing
+                    * what was already in it.
+                    */
+                   nasm_free(smac->name);
+                   free_tlist(smac->expansion);
+               }
+           }
+           else
+           {
+               smac = nasm_malloc(sizeof(SMacro));
+               smac->next = *smhead;
+               *smhead = smac;
+           }
+           smac->name = nasm_strdup(mname);
+           smac->casesense = (i == PP_ASSIGN);
+           smac->nparam = 0;
+           smac->expansion = macro_start;
+           smac->in_progress = FALSE;
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       case PP_LINE:
+           /*
+            * Syntax is `%line nnn[+mmm] [filename]'
+            */
+           tline = tline->next;
+           skip_white_(tline);
+           if (!tok_type_(tline, TOK_NUMBER))
+           {
+               error(ERR_NONFATAL, "`%%line' expects line number");
+               free_tlist(origline);
+               return DIRECTIVE_FOUND;
+           }
+           k = readnum(tline->text, &j);
+           m = 1;
+           tline = tline->next;
+           if (tok_is_(tline, "+"))
+           {
+               tline = tline->next;
+               if (!tok_type_(tline, TOK_NUMBER))
+               {
+                   error(ERR_NONFATAL, "`%%line' expects line increment");
+                   free_tlist(origline);
+                   return DIRECTIVE_FOUND;
+               }
+               m = readnum(tline->text, &j);
+               tline = tline->next;
+           }
+           skip_white_(tline);
+           src_set_linnum(k);
+           istk->lineinc = m;
+           if (tline)
+           {
+               nasm_free(src_set_fname(detoken(tline, FALSE)));
+           }
+           free_tlist(origline);
+           return DIRECTIVE_FOUND;
+
+       default:
+           error(ERR_FATAL,
+                   "preprocessor directive `%s' not yet implemented",
+                   directives[i]);
+           break;
+    }
+    return DIRECTIVE_FOUND;
+}
+
+/*
+ * Ensure that a macro parameter contains a condition code and
+ * nothing else. Return the condition code index if so, or -1
+ * otherwise.
+ */
+static int
+find_cc(Token * t)
+{
+    Token *tt;
+    int i, j, k, m;
+
+    skip_white_(t);
+    if (t->type != TOK_ID)
+       return -1;
+    tt = t->next;
+    skip_white_(tt);
+    if (tt && (tt->type != TOK_OTHER || strcmp(tt->text, ",")))
+       return -1;
+
+    i = -1;
+    j = elements(conditions);
+    while (j - i > 1)
+    {
+       k = (j + i) / 2;
+       m = nasm_stricmp(t->text, conditions[k]);
+       if (m == 0)
+       {
+           i = k;
+           j = -2;
+           break;
+       }
+       else if (m < 0)
+       {
+           j = k;
+       }
+       else
+           i = k;
+    }
+    if (j != -2)
+       return -1;
+    return i;
+}
+
+/*
+ * Expand MMacro-local things: parameter references (%0, %n, %+n,
+ * %-n) and MMacro-local identifiers (%%foo).
+ */
+static Token *
+expand_mmac_params(Token * tline)
+{
+    Token *t, *tt, **tail, *thead;
+
+    tail = &thead;
+    thead = NULL;
+
+    while (tline)
+    {
+       if (tline->type == TOK_PREPROC_ID &&
+               (((tline->text[1] == '+' || tline->text[1] == '-')
+                               && tline->text[2]) || tline->text[1] == '%'
+                       || (tline->text[1] >= '0' && tline->text[1] <= '9')))
+       {
+           char *text = NULL;
+           int type = 0, cc;   /* type = 0 to placate optimisers */
+           char tmpbuf[30];
+           int n, i;
+           MMacro *mac;
+
+           t = tline;
+           tline = tline->next;
+
+           mac = istk->mstk;
+           while (mac && !mac->name)   /* avoid mistaking %reps for macros */
+               mac = mac->next_active;
+           if (!mac)
+               error(ERR_NONFATAL, "`%s': not in a macro call", t->text);
+           else
+               switch (t->text[1])
+               {
+                       /*
+                        * We have to make a substitution of one of the
+                        * forms %1, %-1, %+1, %%foo, %0.
+                        */
+                   case '0':
+                       type = TOK_NUMBER;
+                       sprintf(tmpbuf, "%d", mac->nparam);
+                       text = nasm_strdup(tmpbuf);
+                       break;
+                   case '%':
+                       type = TOK_ID;
+                       sprintf(tmpbuf, "..@%lu.", mac->unique);
+                       text = nasm_strcat(tmpbuf, t->text + 2);
+                       break;
+                   case '-':
+                       n = atoi(t->text + 2) - 1;
+                       if (n >= mac->nparam)
+                           tt = NULL;
+                       else
+                       {
+                           if (mac->nparam > 1)
+                               n = (n + mac->rotate) % mac->nparam;
+                           tt = mac->params[n];
+                       }
+                       cc = find_cc(tt);
+                       if (cc == -1)
+                       {
+                           error(ERR_NONFATAL,
+                                   "macro parameter %d is not a condition code",
+                                   n + 1);
+                           text = NULL;
+                       }
+                       else
+                       {
+                           type = TOK_ID;
+                           if (inverse_ccs[cc] == -1)
+                           {
+                               error(ERR_NONFATAL,
+                                       "condition code `%s' is not invertible",
+                                       conditions[cc]);
+                               text = NULL;
+                           }
+                           else
+                               text =
+                                       nasm_strdup(conditions[inverse_ccs
+                                                [cc]]);
+                       }
+                       break;
+                   case '+':
+                       n = atoi(t->text + 2) - 1;
+                       if (n >= mac->nparam)
+                           tt = NULL;
+                       else
+                       {
+                           if (mac->nparam > 1)
+                               n = (n + mac->rotate) % mac->nparam;
+                           tt = mac->params[n];
+                       }
+                       cc = find_cc(tt);
+                       if (cc == -1)
+                       {
+                           error(ERR_NONFATAL,
+                                   "macro parameter %d is not a condition code",
+                                   n + 1);
+                           text = NULL;
+                       }
+                       else
+                       {
+                           type = TOK_ID;
+                           text = nasm_strdup(conditions[cc]);
+                       }
+                       break;
+                   default:
+                       n = atoi(t->text + 1) - 1;
+                       if (n >= mac->nparam)
+                           tt = NULL;
+                       else
+                       {
+                           if (mac->nparam > 1)
+                               n = (n + mac->rotate) % mac->nparam;
+                           tt = mac->params[n];
+                       }
+                       if (tt)
+                       {
+                           for (i = 0; i < mac->paramlen[n]; i++)
+                           {
+                               *tail =
+                                       new_Token(NULL, tt->type, tt->text,
+                                       0);
+                               tail = &(*tail)->next;
+                               tt = tt->next;
+                           }
+                       }
+                       text = NULL;    /* we've done it here */
+                       break;
+               }
+           if (!text)
+           {
+               delete_Token(t);
+           }
+           else
+           {
+               *tail = t;
+               tail = &t->next;
+               t->type = type;
+               nasm_free(t->text);
+               t->text = text;
+               t->mac = NULL;
+           }
+           continue;
+       }
+       else
+       {
+           t = *tail = tline;
+           tline = tline->next;
+           t->mac = NULL;
+           tail = &t->next;
+       }
+    }
+    *tail = NULL;
+    t = thead;
+    for (; t && (tt = t->next) != NULL; t = t->next)
+       switch (t->type)
+       {
+           case TOK_WHITESPACE:
+               if (tt->type == TOK_WHITESPACE)
+               {
+                   t->next = delete_Token(tt);
+               }
+               break;
+           case TOK_ID:
+               if (tt->type == TOK_ID || tt->type == TOK_NUMBER)
+               {
+                   char *tmp = nasm_strcat(t->text, tt->text);
+                   nasm_free(t->text);
+                   t->text = tmp;
+                   t->next = delete_Token(tt);
+               }
+               break;
+           case TOK_NUMBER:
+               if (tt->type == TOK_NUMBER)
+               {
+                   char *tmp = nasm_strcat(t->text, tt->text);
+                   nasm_free(t->text);
+                   t->text = tmp;
+                   t->next = delete_Token(tt);
+               }
+               break;
+       }
+
+    return thead;
+}
+
+/*
+ * Expand all single-line macro calls made in the given line.
+ * Return the expanded version of the line. The original is deemed
+ * to be destroyed in the process. (In reality we'll just move
+ * Tokens from input to output a lot of the time, rather than
+ * actually bothering to destroy and replicate.)
+ */
+static Token *
+expand_smacro(Token * tline)
+{
+    Token *t, *tt, *mstart, **tail, *thead;
+    SMacro *head = NULL, *m;
+    Token **params;
+    int *paramsize;
+    int nparam, sparam, brackets, rescan;
+    Token *org_tline = tline;
+    Context *ctx;
+    char *mname;
+
+    /*
+     * Trick: we should avoid changing the start token pointer since it can
+     * be contained in "next" field of other token. Because of this
+     * we allocate a copy of first token and work with it; at the end of
+     * routine we copy it back
+     */
+    if (org_tline)
+    {
+       tline =
+               new_Token(org_tline->next, org_tline->type, org_tline->text,
+               0);
+       tline->mac = org_tline->mac;
+       nasm_free(org_tline->text);
+       org_tline->text = NULL;
+    }
+
+  again:
+    tail = &thead;
+    thead = NULL;
+
+    while (tline)
+    {                          /* main token loop */
+       if ((mname = tline->text))
+       {
+           /* if this token is a local macro, look in local context */
+           if (tline->type == TOK_ID || tline->type == TOK_PREPROC_ID)
+               ctx = get_ctx(mname, TRUE);
+           else
+               ctx = NULL;
+           if (!ctx)
+               head = smacros[hash(mname)];
+           else
+               head = ctx->localmac;
+           /*
+            * We've hit an identifier. As in is_mmacro below, we first
+            * check whether the identifier is a single-line macro at
+            * all, then think about checking for parameters if
+            * necessary.
+            */
+           for (m = head; m; m = m->next)
+               if (!mstrcmp(m->name, mname, m->casesense))
+                   break;
+           if (m)
+           {
+               mstart = tline;
+               params = NULL;
+               paramsize = NULL;
+               if (m->nparam == 0)
+               {
+                   /*
+                    * Simple case: the macro is parameterless. Discard the
+                    * one token that the macro call took, and push the
+                    * expansion back on the to-do stack.
+                    */
+                   if (!m->expansion)
+                   {
+                       if (!strcmp("__FILE__", m->name))
+                       {
+                           long num = 0;
+                           src_get(&num, &(tline->text));
+                           nasm_quote(&(tline->text));
+                           tline->type = TOK_STRING;
+                           continue;
+                       }
+                       if (!strcmp("__LINE__", m->name))
+                       {
+                           nasm_free(tline->text);
+                           make_tok_num(tline, src_get_linnum());
+                           continue;
+                       }
+                       tline = delete_Token(tline);
+                       continue;
+                   }
+               }
+               else
+               {
+                   /*
+                    * Complicated case: at least one macro with this name
+                    * exists and takes parameters. We must find the
+                    * parameters in the call, count them, find the SMacro
+                    * that corresponds to that form of the macro call, and
+                    * substitute for the parameters when we expand. What a
+                    * pain.
+                    */
+                   tline = tline->next;
+                   skip_white_(tline);
+                   if (!tok_is_(tline, "("))
+                   {
+                       /*
+                        * This macro wasn't called with parameters: ignore
+                        * the call. (Behaviour borrowed from gnu cpp.)
+                        */
+                       tline = mstart;
+                       m = NULL;
+                   }
+                   else
+                   {
+                       int paren = 0;
+                       int white = 0;
+                       brackets = 0;
+                       nparam = 0;
+                       tline = tline->next;
+                       sparam = PARAM_DELTA;
+                       params = nasm_malloc(sparam * sizeof(Token *));
+                       params[0] = tline;
+                       paramsize = nasm_malloc(sparam * sizeof(int));
+                       paramsize[0] = 0;
+                       for (;; tline = tline->next)
+                       {       /* parameter loop */
+                           if (!tline)
+                           {
+                               error(ERR_NONFATAL,
+                                       "macro call expects terminating `)'");
+                               break;
+                           }
+                           if (tline->type == TOK_WHITESPACE
+                                   && brackets <= 0)
+                           {
+                               if (paramsize[nparam])
+                                   white++;
+                               else
+                                   params[nparam] = tline->next;
+                               continue;       /* parameter loop */
+                           }
+                           if (tline->type == TOK_OTHER
+                                   && tline->text[1] == 0)
+                           {
+                               char ch = tline->text[0];
+                               if (ch == ',' && !paren && brackets <= 0)
+                               {
+                                   if (++nparam >= sparam)
+                                   {
+                                       sparam += PARAM_DELTA;
+                                       params = nasm_realloc(params,
+                                               sparam * sizeof(Token *));
+                                       paramsize = nasm_realloc(paramsize,
+                                               sparam * sizeof(int));
+                                   }
+                                   params[nparam] = tline->next;
+                                   paramsize[nparam] = 0;
+                                   white = 0;
+                                   continue;   /* parameter loop */
+                               }
+                               if (ch == '{' &&
+                                       (brackets > 0 || (brackets == 0 &&
+                                                       !paramsize[nparam])))
+                               {
+                                   if (!(brackets++))
+                                   {
+                                       params[nparam] = tline->next;
+                                       continue;       /* parameter loop */
+                                   }
+                               }
+                               if (ch == '}' && brackets > 0)
+                                   if (--brackets == 0)
+                                   {
+                                       brackets = -1;
+                                       continue;       /* parameter loop */
+                                   }
+                               if (ch == '(' && !brackets)
+                                   paren++;
+                               if (ch == ')' && brackets <= 0)
+                                   if (--paren < 0)
+                                       break;
+                           }
+                           if (brackets < 0)
+                           {
+                               brackets = 0;
+                               error(ERR_NONFATAL, "braces do not "
+                                       "enclose all of macro parameter");
+                           }
+                           paramsize[nparam] += white + 1;
+                           white = 0;
+                       }       /* parameter loop */
+                       nparam++;
+                       while (m && (m->nparam != nparam ||
+                                       mstrcmp(m->name, mname,
+                                               m->casesense)))
+                           m = m->next;
+                       if (!m)
+                           error(ERR_WARNING | ERR_WARN_MNP,
+                                   "macro `%s' exists, "
+                                   "but not taking %d parameters",
+                                   mstart->text, nparam);
+                   }
+               }
+               if (m && m->in_progress)
+                   m = NULL;
+               if (!m)         /* in progess or didn't find '(' or wrong nparam */
+               {
+                   /* 
+                    * Design question: should we handle !tline, which
+                    * indicates missing ')' here, or expand those
+                    * macros anyway, which requires the (t) test a few
+                    * lines down?  
+                    */
+                   nasm_free(params);
+                   nasm_free(paramsize);
+                   tline = mstart;
+               }
+               else
+               {
+                   /*
+                    * Expand the macro: we are placed on the last token of the
+                    * call, so that we can easily split the call from the
+                    * following tokens. We also start by pushing an SMAC_END
+                    * token for the cycle removal.
+                    */
+                   t = tline;
+                   if (t)
+                   {
+                       tline = t->next;
+                       t->next = NULL;
+                   }
+                   tt = new_Token(tline, TOK_SMAC_END, NULL, 0);
+                   tt->mac = m;
+                   m->in_progress = TRUE;
+                   tline = tt;
+                   for (t = m->expansion; t; t = t->next)
+                   {
+                       if (t->type >= TOK_SMAC_PARAM)
+                       {
+                           Token *pcopy = tline, **ptail = &pcopy;
+                           Token *ttt, *pt;
+                           int i;
+
+                           ttt = params[t->type - TOK_SMAC_PARAM];
+                           for (i = paramsize[t->type - TOK_SMAC_PARAM];
+                                   --i >= 0;)
+                           {
+                               pt = *ptail =
+                                       new_Token(tline, ttt->type, ttt->text,
+                                       0);
+                               ptail = &pt->next;
+                               ttt = ttt->next;
+                           }
+                           tline = pcopy;
+                       }
+                       else
+                       {
+                           tt = new_Token(tline, t->type, t->text, 0);
+                           tline = tt;
+                       }
+                   }
+
+                   /*
+                    * Having done that, get rid of the macro call, and clean
+                    * up the parameters.
+                    */
+                   nasm_free(params);
+                   nasm_free(paramsize);
+                   free_tlist(mstart);
+                   continue;   /* main token loop */
+               }
+           }
+       }
+
+       if (tline->type == TOK_SMAC_END)
+       {
+           tline->mac->in_progress = FALSE;
+           tline = delete_Token(tline);
+       }
+       else
+       {
+           t = *tail = tline;
+           tline = tline->next;
+           t->mac = NULL;
+           t->next = NULL;
+           tail = &t->next;
+       }
+    }
+
+    /*
+     * Now scan the entire line and look for successive TOK_IDs that resulted
+     * after expansion (they can't be produced by tokenise()). The successive
+     * TOK_IDs should be concatenated.
+     * Also we look for %+ tokens and concatenate the tokens before and after
+     * them (without white spaces in between).
+     */
+    t = thead;
+    rescan = 0;
+    while (t)
+    {
+       while (t && t->type != TOK_ID && t->type != TOK_PREPROC_ID)
+           t = t->next;
+       if (!t || !t->next)
+           break;
+       if (t->next->type == TOK_ID ||
+               t->next->type == TOK_PREPROC_ID ||
+               t->next->type == TOK_NUMBER)
+       {
+           char *p = nasm_strcat(t->text, t->next->text);
+           nasm_free(t->text);
+           t->next = delete_Token(t->next);
+           t->text = p;
+           rescan = 1;
+       }
+       else if (t->next->type == TOK_WHITESPACE && t->next->next &&
+               t->next->next->type == TOK_PREPROC_ID &&
+               strcmp(t->next->next->text, "%+") == 0)
+       {
+           /* free the next whitespace, the %+ token and next whitespace */
+           int i;
+           for (i = 1; i <= 3; i++)
+           {
+               if (!t->next || (i != 2 && t->next->type != TOK_WHITESPACE))
+                   break;
+               t->next = delete_Token(t->next);
+           }                   /* endfor */
+       }
+       else
+           t = t->next;
+    }
+    /* If we concatenaded something, re-scan the line for macros */
+    if (rescan)
+    {
+       tline = thead;
+       goto again;
+    }
+
+    if (org_tline)
+    {
+       if (thead)
+       {
+           *org_tline = *thead;
+           /* since we just gave text to org_line, don't free it */
+           thead->text = NULL;
+           delete_Token(thead);
+       }
+       else
+       {
+           /* the expression expanded to empty line;
+              we can't return NULL for some reasons
+              we just set the line to a single WHITESPACE token. */
+           memset(org_tline, 0, sizeof(*org_tline));
+           org_tline->text = NULL;
+           org_tline->type = TOK_WHITESPACE;
+       }
+       thead = org_tline;
+    }
+
+    return thead;
+}
+
+/*
+ * Similar to expand_smacro but used exclusively with macro identifiers
+ * right before they are fetched in. The reason is that there can be
+ * identifiers consisting of several subparts. We consider that if there
+ * are more than one element forming the name, user wants a expansion,
+ * otherwise it will be left as-is. Example:
+ *
+ *     %define %$abc cde
+ *
+ * the identifier %$abc will be left as-is so that the handler for %define
+ * will suck it and define the corresponding value. Other case:
+ *
+ *     %define _%$abc cde
+ *
+ * In this case user wants name to be expanded *before* %define starts
+ * working, so we'll expand %$abc into something (if it has a value;
+ * otherwise it will be left as-is) then concatenate all successive
+ * PP_IDs into one.
+ */
+static Token *
+expand_id(Token * tline)
+{
+    Token *cur, *oldnext = NULL;
+
+    if (!tline || !tline->next)
+       return tline;
+
+    cur = tline;
+    while (cur->next &&
+           (cur->next->type == TOK_ID ||
+       cur->next->type == TOK_PREPROC_ID || cur->next->type == TOK_NUMBER))
+       cur = cur->next;
+
+    /* If identifier consists of just one token, don't expand */
+    if (cur == tline)
+       return tline;
+
+    if (cur)
+    {
+       oldnext = cur->next;    /* Detach the tail past identifier */
+       cur->next = NULL;       /* so that expand_smacro stops here */
+    }
+
+    tline = expand_smacro(tline);
+
+    if (cur)
+    {
+       /* expand_smacro possibly changhed tline; re-scan for EOL */
+       cur = tline;
+       while (cur && cur->next)
+           cur = cur->next;
+       if (cur)
+           cur->next = oldnext;
+    }
+
+    return tline;
+}
+
+/*
+ * Determine whether the given line constitutes a multi-line macro
+ * call, and return the MMacro structure called if so. Doesn't have
+ * to check for an initial label - that's taken care of in
+ * expand_mmacro - but must check numbers of parameters. Guaranteed
+ * to be called with tline->type == TOK_ID, so the putative macro
+ * name is easy to find.
+ */
+static MMacro *
+is_mmacro(Token * tline, Token *** params_array)
+{
+    MMacro *head, *m;
+    Token **params;
+    int nparam;
+
+    head = mmacros[hash(tline->text)];
+
+    /*
+     * Efficiency: first we see if any macro exists with the given
+     * name. If not, we can return NULL immediately. _Then_ we
+     * count the parameters, and then we look further along the
+     * list if necessary to find the proper MMacro.
+     */
+    for (m = head; m; m = m->next)
+       if (!mstrcmp(m->name, tline->text, m->casesense))
+           break;
+    if (!m)
+       return NULL;
+
+    /*
+     * OK, we have a potential macro. Count and demarcate the
+     * parameters.
+     */
+    count_mmac_params(tline->next, &nparam, &params);
+
+    /*
+     * So we know how many parameters we've got. Find the MMacro
+     * structure that handles this number.
+     */
+    while (m)
+    {
+       if (m->nparam_min <= nparam && (m->plus || nparam <= m->nparam_max))
+       {
+           /*
+            * This one is right. Just check if cycle removal
+            * prohibits us using it before we actually celebrate...
+            */
+           if (m->in_progress)
+           {
+#if 0
+               error(ERR_NONFATAL,
+                       "self-reference in multi-line macro `%s'", m->name);
+#endif
+               nasm_free(params);
+               return NULL;
+           }
+           /*
+            * It's right, and we can use it. Add its default
+            * parameters to the end of our list if necessary.
+            */
+           if (m->defaults && nparam < m->nparam_min + m->ndefs)
+           {
+               params =
+                       nasm_realloc(params,
+                       ((m->nparam_min + m->ndefs + 1) * sizeof(*params)));
+               while (nparam < m->nparam_min + m->ndefs)
+               {
+                   params[nparam] = m->defaults[nparam - m->nparam_min];
+                   nparam++;
+               }
+           }
+           /*
+            * If we've gone over the maximum parameter count (and
+            * we're in Plus mode), ignore parameters beyond
+            * nparam_max.
+            */
+           if (m->plus && nparam > m->nparam_max)
+               nparam = m->nparam_max;
+           /*
+            * Then terminate the parameter list, and leave.
+            */
+           if (!params)
+           {                   /* need this special case */
+               params = nasm_malloc(sizeof(*params));
+               nparam = 0;
+           }
+           params[nparam] = NULL;
+           *params_array = params;
+           return m;
+       }
+       /*
+        * This one wasn't right: look for the next one with the
+        * same name.
+        */
+       for (m = m->next; m; m = m->next)
+           if (!mstrcmp(m->name, tline->text, m->casesense))
+               break;
+    }
+
+    /*
+     * After all that, we didn't find one with the right number of
+     * parameters. Issue a warning, and fail to expand the macro.
+     */
+    error(ERR_WARNING | ERR_WARN_MNP,
+           "macro `%s' exists, but not taking %d parameters",
+           tline->text, nparam);
+    nasm_free(params);
+    return NULL;
+}
+
+/*
+ * Expand the multi-line macro call made by the given line, if
+ * there is one to be expanded. If there is, push the expansion on
+ * istk->expansion and return 1. Otherwise return 0.
+ */
+static int
+expand_mmacro(Token * tline)
+{
+    Token *startline = tline;
+    Token *label = NULL;
+    int dont_prepend = 0;
+    Token **params, *t, *tt;
+    MMacro *m;
+    Line *l, *ll;
+    int i, nparam, *paramlen;
+
+    t = tline;
+    skip_white_(t);
+/*    if (!tok_type_(t, TOK_ID))  Lino 02/25/02 */
+    if (!tok_type_(t, TOK_ID) && !tok_type_(t, TOK_PREPROC_ID))
+       return 0;
+    m = is_mmacro(t, &params);
+    if (!m)
+    {
+       Token *last;
+       /*
+        * We have an id which isn't a macro call. We'll assume
+        * it might be a label; we'll also check to see if a
+        * colon follows it. Then, if there's another id after
+        * that lot, we'll check it again for macro-hood.
+        */
+       label = last = t;
+       t = t->next;
+       if (tok_type_(t, TOK_WHITESPACE))
+           last = t, t = t->next;
+       if (tok_is_(t, ":"))
+       {
+           dont_prepend = 1;
+           last = t, t = t->next;
+           if (tok_type_(t, TOK_WHITESPACE))
+               last = t, t = t->next;
+       }
+       if (!tok_type_(t, TOK_ID) || (m = is_mmacro(t, &params)) == NULL)
+           return 0;
+       last->next = NULL;
+       tline = t;
+    }
+
+    /*
+     * Fix up the parameters: this involves stripping leading and
+     * trailing whitespace, then stripping braces if they are
+     * present.
+     */
+    for (nparam = 0; params[nparam]; nparam++)
+       ;
+    paramlen = nparam ? nasm_malloc(nparam * sizeof(*paramlen)) : NULL;
+
+    for (i = 0; params[i]; i++)
+    {
+       int brace = FALSE;
+       int comma = (!m->plus || i < nparam - 1);
+
+       t = params[i];
+       skip_white_(t);
+       if (tok_is_(t, "{"))
+           t = t->next, brace = TRUE, comma = FALSE;
+       params[i] = t;
+       paramlen[i] = 0;
+       while (t)
+       {
+           if (comma && t->type == TOK_OTHER && !strcmp(t->text, ","))
+               break;          /* ... because we have hit a comma */
+           if (comma && t->type == TOK_WHITESPACE && tok_is_(t->next, ","))
+               break;          /* ... or a space then a comma */
+           if (brace && t->type == TOK_OTHER && !strcmp(t->text, "}"))
+               break;          /* ... or a brace */
+           t = t->next;
+           paramlen[i]++;
+       }
+    }
+
+    /*
+     * OK, we have a MMacro structure together with a set of
+     * parameters. We must now go through the expansion and push
+     * copies of each Line on to istk->expansion. Substitution of
+     * parameter tokens and macro-local tokens doesn't get done
+     * until the single-line macro substitution process; this is
+     * because delaying them allows us to change the semantics
+     * later through %rotate.
+     *
+     * First, push an end marker on to istk->expansion, mark this
+     * macro as in progress, and set up its invocation-specific
+     * variables.
+     */
+    ll = nasm_malloc(sizeof(Line));
+    ll->next = istk->expansion;
+    ll->finishes = m;
+    ll->first = NULL;
+    istk->expansion = ll;
+
+    m->in_progress = TRUE;
+    m->params = params;
+    m->iline = tline;
+    m->nparam = nparam;
+    m->rotate = 0;
+    m->paramlen = paramlen;
+    m->unique = unique++;
+    m->lineno = 0;
+
+    m->next_active = istk->mstk;
+    istk->mstk = m;
+
+    for (l = m->expansion; l; l = l->next)
+    {
+       Token **tail;
+
+       ll = nasm_malloc(sizeof(Line));
+       ll->finishes = NULL;
+       ll->next = istk->expansion;
+       istk->expansion = ll;
+       tail = &ll->first;
+
+       for (t = l->first; t; t = t->next)
+       {
+           Token *x = t;
+           if (t->type == TOK_PREPROC_ID &&
+                   t->text[1] == '0' && t->text[2] == '0')
+           {
+               dont_prepend = -1;
+               x = label;
+               if (!x)
+                   continue;
+           }
+           tt = *tail = new_Token(NULL, x->type, x->text, 0);
+           tail = &tt->next;
+       }
+       *tail = NULL;
+    }
+
+    /*
+     * If we had a label, push it on as the first line of
+     * the macro expansion.
+     */
+    if (label)
+    {
+       if (dont_prepend < 0)
+           free_tlist(startline);
+       else
+       {
+           ll = nasm_malloc(sizeof(Line));
+           ll->finishes = NULL;
+           ll->next = istk->expansion;
+           istk->expansion = ll;
+           ll->first = startline;
+           if (!dont_prepend)
+           {
+               while (label->next)
+                   label = label->next;
+               label->next = tt = new_Token(NULL, TOK_OTHER, ":", 0);
+           }
+       }
+    }
+
+    list->uplevel(m->nolist ? LIST_MACRO_NOLIST : LIST_MACRO);
+
+    return 1;
+}
+
+/*
+ * Since preprocessor always operate only on the line that didn't
+ * arrived yet, we should always use ERR_OFFBY1. Also since user
+ * won't want to see same error twice (preprocessing is done once
+ * per pass) we will want to show errors only during pass one.
+ */
+static void
+error(int severity, const char *fmt, ...)
+{
+    va_list arg;
+    char buff[1024];
+
+    /* If we're in a dead branch of IF or something like it, ignore the error */
+    if (istk && istk->conds && !emitting(istk->conds->state))
+       return;
+
+    va_start(arg, fmt);
+    vsprintf(buff, fmt, arg);
+    va_end(arg);
+
+    if (istk && istk->mstk && istk->mstk->name)
+       _error(severity | ERR_PASS1, "(%s:%d) %s", istk->mstk->name,
+               istk->mstk->lineno, buff);
+    else 
+       _error(severity | ERR_PASS1, "%s", buff);
+}
+
+static void
+pp_reset(char *file, int apass, efunc errfunc, evalfunc eval,
+       ListGen * listgen)
+{
+    int h;
+
+    _error = errfunc;
+    cstk = NULL;
+    istk = nasm_malloc(sizeof(Include));
+    istk->next = NULL;
+    istk->conds = NULL;
+    istk->expansion = NULL;
+    istk->mstk = NULL;
+    istk->fp = fopen(file, "r");
+    istk->fname = NULL;
+    src_set_fname(nasm_strdup(file));
+    src_set_linnum(0);
+    istk->lineinc = 1;
+    if (!istk->fp)
+       error(ERR_FATAL | ERR_NOFILE, "unable to open input file `%s'", file);
+    defining = NULL;
+    for (h = 0; h < NHASH; h++)
+    {
+       mmacros[h] = NULL;
+       smacros[h] = NULL;
+    }
+    unique = 0;
+       if (tasm_compatible_mode) {
+           stdmacpos = stdmac;
+       } else {
+               stdmacpos = &stdmac[TASM_MACRO_COUNT];
+       }
+    any_extrastdmac = (extrastdmac != NULL);
+    list = listgen;
+    evaluate = eval;
+    pass = apass;
+}
+
+static char *
+pp_getline(void)
+{
+    char *line;
+    Token *tline;
+
+    while (1)
+    {
+       /*
+        * Fetch a tokenised line, either from the macro-expansion
+        * buffer or from the input file.
+        */
+       tline = NULL;
+       while (istk->expansion && istk->expansion->finishes)
+       {
+           Line *l = istk->expansion;
+           if (!l->finishes->name && l->finishes->in_progress > 1)
+           {
+               Line *ll;
+
+               /*
+                * This is a macro-end marker for a macro with no
+                * name, which means it's not really a macro at all
+                * but a %rep block, and the `in_progress' field is
+                * more than 1, meaning that we still need to
+                * repeat. (1 means the natural last repetition; 0
+                * means termination by %exitrep.) We have
+                * therefore expanded up to the %endrep, and must
+                * push the whole block on to the expansion buffer
+                * again. We don't bother to remove the macro-end
+                * marker: we'd only have to generate another one
+                * if we did.
+                */
+               l->finishes->in_progress--;
+               for (l = l->finishes->expansion; l; l = l->next)
+               {
+                   Token *t, *tt, **tail;
+
+                   ll = nasm_malloc(sizeof(Line));
+                   ll->next = istk->expansion;
+                   ll->finishes = NULL;
+                   ll->first = NULL;
+                   tail = &ll->first;
+
+                   for (t = l->first; t; t = t->next)
+                   {
+                       if (t->text || t->type == TOK_WHITESPACE)
+                       {
+                           tt = *tail = new_Token(NULL, t->type, t->text, 0);
+                           tail = &tt->next;
+                       }
+                   }
+
+                   istk->expansion = ll;
+               }
+           }
+           else
+           {
+               /*
+                * Check whether a `%rep' was started and not ended
+                * within this macro expansion. This can happen and
+                * should be detected. It's a fatal error because
+                * I'm too confused to work out how to recover
+                * sensibly from it.
+                */
+               if (defining)
+               {
+                   if (defining->name)
+                       error(ERR_PANIC, "defining with name in expansion");
+                   else if (istk->mstk->name)
+                       error(ERR_FATAL, "`%%rep' without `%%endrep' within"
+                               " expansion of macro `%s'", istk->mstk->name);
+               }
+
+               /*
+                * FIXME:  investigate the relationship at this point between
+                * istk->mstk and l->finishes
+                */
+               {
+                   MMacro *m = istk->mstk;
+                   istk->mstk = m->next_active;
+                   if (m->name)
+                   {
+                       /*
+                        * This was a real macro call, not a %rep, and
+                        * therefore the parameter information needs to
+                        * be freed.
+                        */
+                       nasm_free(m->params);
+                       free_tlist(m->iline);
+                       nasm_free(m->paramlen);
+                       l->finishes->in_progress = FALSE;
+                   }
+                   else
+                       free_mmacro(m);
+               }
+               istk->expansion = l->next;
+               nasm_free(l);
+               list->downlevel(LIST_MACRO);
+           }
+       }
+       while (1)
+       {                       /* until we get a line we can use */
+
+           if (istk->expansion)
+           {                   /* from a macro expansion */
+               char *p;
+               Line *l = istk->expansion;
+               if (istk->mstk)
+                   istk->mstk->lineno++;
+               tline = l->first;
+               istk->expansion = l->next;
+               nasm_free(l);
+               p = detoken(tline, FALSE);
+               list->line(LIST_MACRO, p);
+               nasm_free(p);
+               break;
+           }
+           line = read_line();
+           if (line)
+           {                   /* from the current input file */
+               line = prepreproc(line);
+               tline = tokenise(line);
+               nasm_free(line);
+               break;
+           }
+           /*
+            * The current file has ended; work down the istk
+            */
+           {
+               Include *i = istk;
+               fclose(i->fp);
+               if (i->conds)
+                   error(ERR_FATAL, "expected `%%endif' before end of file");
+               /* only set line and file name if there's a next node */
+               if (i->next) 
+               {
+                   src_set_linnum(i->lineno);
+                   nasm_free(src_set_fname(i->fname));
+               }
+               istk = i->next;
+               list->downlevel(LIST_INCLUDE);
+               nasm_free(i);
+               if (!istk)
+                   return NULL;
+           }
+       }
+
+       /*
+        * We must expand MMacro parameters and MMacro-local labels
+        * _before_ we plunge into directive processing, to cope
+        * with things like `%define something %1' such as STRUC
+        * uses. Unless we're _defining_ a MMacro, in which case
+        * those tokens should be left alone to go into the
+        * definition; and unless we're in a non-emitting
+        * condition, in which case we don't want to meddle with
+        * anything.
+        */
+       if (!defining && !(istk->conds && !emitting(istk->conds->state)))
+           tline = expand_mmac_params(tline);
+
+       /*
+        * Check the line to see if it's a preprocessor directive.
+        */
+       if (do_directive(tline) == DIRECTIVE_FOUND)
+       {
+           continue;
+       }
+       else if (defining)
+       {
+           /*
+            * We're defining a multi-line macro. We emit nothing
+            * at all, and just
+            * shove the tokenised line on to the macro definition.
+            */
+           Line *l = nasm_malloc(sizeof(Line));
+           l->next = defining->expansion;
+           l->first = tline;
+           l->finishes = FALSE;
+           defining->expansion = l;
+           continue;
+       }
+       else if (istk->conds && !emitting(istk->conds->state))
+       {
+           /*
+            * We're in a non-emitting branch of a condition block.
+            * Emit nothing at all, not even a blank line: when we
+            * emerge from the condition we'll give a line-number
+            * directive so we keep our place correctly.
+            */
+           free_tlist(tline);
+           continue;
+       }
+       else if (istk->mstk && !istk->mstk->in_progress)
+       {
+           /*
+            * We're in a %rep block which has been terminated, so
+            * we're walking through to the %endrep without
+            * emitting anything. Emit nothing at all, not even a
+            * blank line: when we emerge from the %rep block we'll
+            * give a line-number directive so we keep our place
+            * correctly.
+            */
+           free_tlist(tline);
+           continue;
+       }
+       else
+       {
+           tline = expand_smacro(tline);
+           if (!expand_mmacro(tline))
+           {
+               /*
+                * De-tokenise the line again, and emit it.
+                */
+               line = detoken(tline, TRUE);
+               free_tlist(tline);
+               break;
+           }
+           else
+           {
+               continue;       /* expand_mmacro calls free_tlist */
+           }
+       }
+    }
+
+    return line;
+}
+
+static void
+pp_cleanup(int pass)
+{
+    int h;
+
+    if (defining)
+    {
+       error(ERR_NONFATAL, "end of file while still defining macro `%s'",
+               defining->name);
+       free_mmacro(defining);
+    }
+    while (cstk)
+       ctx_pop();
+    for (h = 0; h < NHASH; h++)
+    {
+       while (mmacros[h])
+       {
+           MMacro *m = mmacros[h];
+           mmacros[h] = mmacros[h]->next;
+           free_mmacro(m);
+       }
+       while (smacros[h])
+       {
+           SMacro *s = smacros[h];
+           smacros[h] = smacros[h]->next;
+           nasm_free(s->name);
+           free_tlist(s->expansion);
+           nasm_free(s);
+       }
+    }
+    while (istk)
+    {
+       Include *i = istk;
+       istk = istk->next;
+       fclose(i->fp);
+       nasm_free(i->fname);
+       nasm_free(i);
+    }
+    while (cstk)
+       ctx_pop();
+    if (pass == 0)
+       {
+               free_llist(predef);
+               delete_Blocks();
+       }
+}
+
+void
+pp_include_path(char *path)
+{
+    IncPath *i;
+
+    i = nasm_malloc(sizeof(IncPath));
+    i->path = nasm_strdup(path);
+    i->next = ipath;
+    ipath = i;
+}
+
+void
+pp_pre_include(char *fname)
+{
+    Token *inc, *space, *name;
+    Line *l;
+
+    name = new_Token(NULL, TOK_INTERNAL_STRING, fname, 0);
+    space = new_Token(name, TOK_WHITESPACE, NULL, 0);
+    inc = new_Token(space, TOK_PREPROC_ID, "%include", 0);
+
+    l = nasm_malloc(sizeof(Line));
+    l->next = predef;
+    l->first = inc;
+    l->finishes = FALSE;
+    predef = l;
+}
+
+void
+pp_pre_define(char *definition)
+{
+    Token *def, *space;
+    Line *l;
+    char *equals;
+
+    equals = strchr(definition, '=');
+    space = new_Token(NULL, TOK_WHITESPACE, NULL, 0);
+    def = new_Token(space, TOK_PREPROC_ID, "%define", 0);
+    if (equals)
+       *equals = ' ';
+    space->next = tokenise(definition);
+    if (equals)
+       *equals = '=';
+
+    l = nasm_malloc(sizeof(Line));
+    l->next = predef;
+    l->first = def;
+    l->finishes = FALSE;
+    predef = l;
+}
+
+void
+pp_pre_undefine(char *definition)
+{
+    Token *def, *space;
+    Line *l;
+
+    space = new_Token(NULL, TOK_WHITESPACE, NULL, 0);
+    def = new_Token(space, TOK_PREPROC_ID, "%undef", 0);
+
+    l = nasm_malloc(sizeof(Line));
+    l->next = predef;
+    l->first = def;
+    l->finishes = FALSE;
+    predef = l;
+}
+
+void
+pp_extra_stdmac(const char **macros)
+{
+    extrastdmac = macros;
+}
+
+static void
+make_tok_num(Token * tok, long val)
+{
+    char numbuf[20];
+    sprintf(numbuf, "%ld", val);
+    tok->text = nasm_strdup(numbuf);
+    tok->type = TOK_NUMBER;
+}
+
+Preproc nasmpp = {
+    pp_reset,
+    pp_getline,
+    pp_cleanup
+};
diff --git a/src/preprocs/nasm/nasm-pp.h b/src/preprocs/nasm/nasm-pp.h
new file mode 100644 (file)
index 0000000..0b7df11
--- /dev/null
@@ -0,0 +1,20 @@
+/* preproc.h  header file for preproc.c
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ */
+
+#ifndef NASM_PREPROC_H
+#define NASM_PREPROC_H
+
+void pp_include_path (char *);
+void pp_pre_include (char *);
+void pp_pre_define (char *);
+void pp_pre_undefine (char *);
+void pp_extra_stdmac (const char **);
+
+extern Preproc nasmpp;
+
+#endif
diff --git a/src/preprocs/nasm/nasm.h b/src/preprocs/nasm/nasm.h
new file mode 100644 (file)
index 0000000..8f4f293
--- /dev/null
@@ -0,0 +1,850 @@
+/* nasm.h   main header file for the Netwide Assembler: inter-module interface
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ *
+ * initial version: 27/iii/95 by Simon Tatham
+ */
+
+#ifndef NASM_NASM_H
+#define NASM_NASM_H
+
+#include <stdio.h>
+#include "version.h"                  /* generated NASM version macros */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef FALSE
+#define FALSE 0                               /* comes in handy */
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define NO_SEG -1L                    /* null segment value */
+#define SEG_ABS 0x40000000L           /* mask for far-absolute segments */
+
+#ifndef FILENAME_MAX
+#define FILENAME_MAX 256
+#endif
+
+#ifndef PREFIX_MAX
+#define PREFIX_MAX 10
+#endif
+
+#ifndef POSTFIX_MAX
+#define POSTFIX_MAX 10
+#endif
+
+
+
+/*
+ * Name pollution problems: <time.h> on Digital UNIX pulls in some
+ * strange hardware header file which sees fit to define R_SP. We
+ * undefine it here so as not to break the enum below.
+ */
+#ifdef R_SP
+#undef R_SP
+#endif
+
+/*
+ * We must declare the existence of this structure type up here,
+ * since we have to reference it before we define it...
+ */
+struct ofmt;
+
+/*
+ * -------------------------
+ * Error reporting functions
+ * -------------------------
+ */
+
+/*
+ * An error reporting function should look like this.
+ */
+typedef void (*efunc) (int severity, const char *fmt, ...);
+
+/*
+ * These are the error severity codes which get passed as the first
+ * argument to an efunc.
+ */
+
+#define ERR_DEBUG      0x00000008      /* put out debugging message */
+#define ERR_WARNING    0x00000000      /* warn only: no further action */
+#define ERR_NONFATAL   0x00000001      /* terminate assembly after phase */
+#define ERR_FATAL      0x00000002      /* instantly fatal: exit with error */
+#define ERR_PANIC      0x00000003      /* internal error: panic instantly
+                                       * and dump core for reference */
+#define ERR_MASK       0x0000000F      /* mask off the above codes */
+#define ERR_NOFILE     0x00000010      /* don't give source file name/line */
+#define ERR_USAGE      0x00000020      /* print a usage message */
+#define ERR_PASS1      0x00000040      /* only print this error on pass one */
+
+/*
+ * These codes define specific types of suppressible warning.
+ */
+
+#define ERR_WARN_MASK  0x0000FF00      /* the mask for this feature */
+#define ERR_WARN_SHR  8                       /* how far to shift right */
+
+#define ERR_WARN_MNP   0x00000100      /* macro-num-parameters warning */
+#define ERR_WARN_MSR   0x00000200      /* macro self-reference */
+#define ERR_WARN_OL    0x00000300      /* orphan label (no colon, and
+                                       * alone on line) */
+#define ERR_WARN_NOV   0x00000400      /* numeric overflow */
+#define ERR_WARN_GNUELF        0x00000500      /* using GNU ELF extensions */
+#define ERR_WARN_MAX   5               /* the highest numbered one */
+
+/*
+ * -----------------------
+ * Other function typedefs
+ * -----------------------
+ */
+
+/*
+ * A label-lookup function should look like this.
+ */
+typedef int (*lfunc) (char *label, long *segment, long *offset);
+
+/*
+ * And a label-definition function like this. The boolean parameter
+ * `is_norm' states whether the label is a `normal' label (which
+ * should affect the local-label system), or something odder like
+ * an EQU or a segment-base symbol, which shouldn't.
+ */
+typedef void (*ldfunc) (char *label, long segment, long offset, char *special,
+                       int is_norm, int isextrn, struct ofmt *ofmt,
+                       efunc error);
+
+/*
+ * List-file generators should look like this:
+ */
+typedef struct {
+    /*
+     * Called to initialise the listing file generator. Before this
+     * is called, the other routines will silently do nothing when
+     * called. The `char *' parameter is the file name to write the
+     * listing to.
+     */
+    void (*init) (char *, efunc);
+
+    /*
+     * Called to clear stuff up and close the listing file.
+     */
+    void (*cleanup) (void);
+
+    /*
+     * Called to output binary data. Parameters are: the offset;
+     * the data; the data type. Data types are similar to the
+     * output-format interface, only OUT_ADDRESS will _always_ be
+     * displayed as if it's relocatable, so ensure that any non-
+     * relocatable address has been converted to OUT_RAWDATA by
+     * then. Note that OUT_RAWDATA+0 is a valid data type, and is a
+     * dummy call used to give the listing generator an offset to
+     * work with when doing things like uplevel(LIST_TIMES) or
+     * uplevel(LIST_INCBIN).
+     */
+    void (*output) (long, const void *, unsigned long);
+
+    /*
+     * Called to send a text line to the listing generator. The
+     * `int' parameter is LIST_READ or LIST_MACRO depending on
+     * whether the line came directly from an input file or is the
+     * result of a multi-line macro expansion.
+     */
+    void (*line) (int, char *);
+
+    /*
+     * Called to change one of the various levelled mechanisms in
+     * the listing generator. LIST_INCLUDE and LIST_MACRO can be
+     * used to increase the nesting level of include files and
+     * macro expansions; LIST_TIMES and LIST_INCBIN switch on the
+     * two binary-output-suppression mechanisms for large-scale
+     * pseudo-instructions.
+     *
+     * LIST_MACRO_NOLIST is synonymous with LIST_MACRO except that
+     * it indicates the beginning of the expansion of a `nolist'
+     * macro, so anything under that level won't be expanded unless
+     * it includes another file.
+     */
+    void (*uplevel) (int);
+
+    /*
+     * Reverse the effects of uplevel.
+     */
+    void (*downlevel) (int);
+} ListGen;
+
+/*
+ * The expression evaluator must be passed a scanner function; a
+ * standard scanner is provided as part of nasmlib.c. The
+ * preprocessor will use a different one. Scanners, and the
+ * token-value structures they return, look like this.
+ *
+ * The return value from the scanner is always a copy of the
+ * `t_type' field in the structure.
+ */
+struct tokenval {
+    int t_type;
+    long t_integer, t_inttwo;
+    char *t_charptr;
+};
+typedef int (*scanner) (void *private_data, struct tokenval *tv);
+
+/*
+ * Token types returned by the scanner, in addition to ordinary
+ * ASCII character values, and zero for end-of-string.
+ */
+enum {                                /* token types, other than chars */
+    TOKEN_INVALID = -1,                       /* a placeholder value */
+    TOKEN_EOS = 0,                    /* end of string */
+    TOKEN_EQ = '=', TOKEN_GT = '>', TOKEN_LT = '<',   /* aliases */
+    TOKEN_ID = 256, TOKEN_NUM, TOKEN_REG, TOKEN_INSN,  /* major token types */
+    TOKEN_ERRNUM,                     /* numeric constant with error in */
+    TOKEN_HERE, TOKEN_BASE,           /* $ and $$ */
+    TOKEN_SPECIAL,                    /* BYTE, WORD, DWORD, FAR, NEAR, etc */
+    TOKEN_PREFIX,                     /* A32, O16, LOCK, REPNZ, TIMES, etc */
+    TOKEN_SHL, TOKEN_SHR,             /* << and >> */
+    TOKEN_SDIV, TOKEN_SMOD,           /* // and %% */
+    TOKEN_GE, TOKEN_LE, TOKEN_NE,      /* >=, <= and <> (!= is same as <>) */
+    TOKEN_DBL_AND, TOKEN_DBL_OR, TOKEN_DBL_XOR,   /* &&, || and ^^ */
+    TOKEN_SEG, TOKEN_WRT,             /* SEG and WRT */
+    TOKEN_FLOAT                               /* floating-point constant */
+};
+
+typedef struct {
+    long segment;
+    long offset;
+    int  known;
+} loc_t;
+
+/*
+ * Expression-evaluator datatype. Expressions, within the
+ * evaluator, are stored as an array of these beasts, terminated by
+ * a record with type==0. Mostly, it's a vector type: each type
+ * denotes some kind of a component, and the value denotes the
+ * multiple of that component present in the expression. The
+ * exception is the WRT type, whose `value' field denotes the
+ * segment to which the expression is relative. These segments will
+ * be segment-base types, i.e. either odd segment values or SEG_ABS
+ * types. So it is still valid to assume that anything with a
+ * `value' field of zero is insignificant.
+ */
+typedef struct {
+    long type;                        /* a register, or EXPR_xxx */
+    long value;                               /* must be >= 32 bits */
+} expr;
+
+/*
+ * The evaluator can also return hints about which of two registers
+ * used in an expression should be the base register. See also the
+ * `operand' structure.
+ */
+struct eval_hints {
+    int base;
+    int type;
+};
+
+/*
+ * The actual expression evaluator function looks like this. When
+ * called, it expects the first token of its expression to already
+ * be in `*tv'; if it is not, set tv->t_type to TOKEN_INVALID and
+ * it will start by calling the scanner.
+ *
+ * If a forward reference happens during evaluation, the evaluator
+ * must set `*fwref' to TRUE if `fwref' is non-NULL.
+ *
+ * `critical' is non-zero if the expression may not contain forward
+ * references. The evaluator will report its own error if this
+ * occurs; if `critical' is 1, the error will be "symbol not
+ * defined before use", whereas if `critical' is 2, the error will
+ * be "symbol undefined".
+ *
+ * If `critical' has bit 8 set (in addition to its main value: 0x101
+ * and 0x102 correspond to 1 and 2) then an extended expression
+ * syntax is recognised, in which relational operators such as =, <
+ * and >= are accepted, as well as low-precedence logical operators
+ * &&, ^^ and ||.
+ *
+ * If `hints' is non-NULL, it gets filled in with some hints as to
+ * the base register in complex effective addresses.
+ */
+#define CRITICAL 0x100
+typedef expr *(*evalfunc) (scanner sc, void *scprivate, struct tokenval *tv,
+                          int *fwref, int critical, efunc error,
+                          struct eval_hints *hints);
+
+/*
+ * Special values for expr->type. ASSUMPTION MADE HERE: the number
+ * of distinct register names (i.e. possible "type" fields for an
+ * expr structure) does not exceed 124 (EXPR_REG_START through
+ * EXPR_REG_END).
+ */
+#define EXPR_REG_START 1
+#define EXPR_REG_END 124
+#define EXPR_UNKNOWN 125L             /* for forward references */
+#define EXPR_SIMPLE 126L
+#define EXPR_WRT 127L
+#define EXPR_SEGBASE 128L
+
+/*
+ * Preprocessors ought to look like this:
+ */
+typedef struct {
+    /*
+     * Called at the start of a pass; given a file name, the number
+     * of the pass, an error reporting function, an evaluator
+     * function, and a listing generator to talk to.
+     */
+    void (*reset) (char *, int, efunc, evalfunc, ListGen *);
+
+    /*
+     * Called to fetch a line of preprocessed source. The line
+     * returned has been malloc'ed, and so should be freed after
+     * use.
+     */
+    char *(*getline) (void);
+
+    /*
+     * Called at the end of a pass.
+     */
+    void (*cleanup) (int);
+} Preproc;
+
+/*
+ * ----------------------------------------------------------------
+ * Some lexical properties of the NASM source language, included
+ * here because they are shared between the parser and preprocessor
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * isidstart matches any character that may start an identifier, and isidchar
+ * matches any character that may appear at places other than the start of an
+ * identifier. E.g. a period may only appear at the start of an identifier
+ * (for local labels), whereas a number may appear anywhere *but* at the
+ * start. 
+ */
+
+#define isidstart(c) ( isalpha(c) || (c)=='_' || (c)=='.' || (c)=='?' \
+                                  || (c)=='@' )
+#define isidchar(c)  ( isidstart(c) || isdigit(c) || (c)=='$' || (c)=='#' \
+                                                  || (c)=='~' )
+
+/* Ditto for numeric constants. */
+
+#define isnumstart(c)  ( isdigit(c) || (c)=='$' )
+#define isnumchar(c)   ( isalnum(c) )
+
+/* This returns the numeric value of a given 'digit'. */
+
+#define numvalue(c)  ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0')
+
+/*
+ * Data-type flags that get passed to listing-file routines.
+ */
+enum {
+    LIST_READ, LIST_MACRO, LIST_MACRO_NOLIST, LIST_INCLUDE,
+    LIST_INCBIN, LIST_TIMES
+};
+
+/*
+ * -----------------------------------------------------------
+ * Format of the `insn' structure returned from `parser.c' and
+ * passed into `assemble.c'
+ * -----------------------------------------------------------
+ */
+
+/*
+ * Here we define the operand types. These are implemented as bit
+ * masks, since some are subsets of others; e.g. AX in a MOV
+ * instruction is a special operand type, whereas AX in other
+ * contexts is just another 16-bit register. (Also, consider CL in
+ * shift instructions, DX in OUT, etc.)
+ */
+
+/* size, and other attributes, of the operand */
+#define BITS8     0x00000001L
+#define BITS16    0x00000002L
+#define BITS32    0x00000004L
+#define BITS64    0x00000008L         /* FPU only */
+#define BITS80    0x00000010L         /* FPU only */
+#define FAR       0x00000020L         /* grotty: this means 16:16 or */
+                                      /* 16:32, like in CALL/JMP */
+#define NEAR      0x00000040L
+#define SHORT     0x00000080L         /* and this means what it says :) */
+
+#define SIZE_MASK 0x000000FFL         /* all the size attributes */
+#define NON_SIZE  (~SIZE_MASK)
+
+#define TO        0x00000100L          /* reverse effect in FADD, FSUB &c */
+#define COLON     0x00000200L         /* operand is followed by a colon */
+#define STRICT    0x00000400L         /* do not optimize this operand */
+
+/* type of operand: memory reference, register, etc. */
+#define MEMORY    0x00204000L
+#define REGISTER  0x00001000L         /* register number in 'basereg' */
+#define IMMEDIATE 0x00002000L
+
+#define REGMEM    0x00200000L         /* for r/m, ie EA, operands */
+#define REGNORM   0x00201000L         /* 'normal' reg, qualifies as EA */
+#define REG8      0x00201001L
+#define REG16     0x00201002L
+#define REG32     0x00201004L
+#define MMXREG    0x00201008L         /* MMX registers */
+#define XMMREG    0x00201010L          /* XMM Katmai reg */
+#define FPUREG    0x01000000L         /* floating point stack registers */
+#define FPU0      0x01000800L         /* FPU stack register zero */
+
+/* special register operands: these may be treated differently */
+#define REG_SMASK 0x00070000L         /* a mask for the following */
+#define REG_ACCUM 0x00211000L         /* accumulator: AL, AX or EAX */
+#define REG_AL    0x00211001L         /* REG_ACCUM | BITSxx */
+#define REG_AX    0x00211002L         /* ditto */
+#define REG_EAX   0x00211004L         /* and again */
+#define REG_COUNT 0x00221000L         /* counter: CL, CX or ECX */
+#define REG_CL    0x00221001L         /* REG_COUNT | BITSxx */
+#define REG_CX    0x00221002L         /* ditto */
+#define REG_ECX   0x00221004L         /* another one */
+#define REG_DX    0x00241002L
+#define REG_SREG  0x00081002L         /* any segment register */
+#define REG_CS    0x01081002L         /* CS */
+#define REG_DESS  0x02081002L         /* DS, ES, SS (non-CS 86 registers) */
+#define REG_FSGS  0x04081002L         /* FS, GS (386 extended registers) */
+#define REG_SEG67 0x08081002L          /* Non-implemented segment registers */
+#define REG_CDT   0x00101004L         /* CRn, DRn and TRn */
+#define REG_CREG  0x08101004L         /* CRn */
+#define REG_DREG  0x10101004L         /* DRn */
+#define REG_TREG  0x20101004L         /* TRn */
+
+/* special type of EA */
+#define MEM_OFFS  0x00604000L         /* simple [address] offset */
+
+/* special type of immediate operand */
+#define ONENESS   0x00800000L          /* so UNITY == IMMEDIATE | ONENESS */
+#define UNITY     0x00802000L         /* for shift/rotate instructions */
+#define BYTENESS  0x40000000L          /* so SBYTE == IMMEDIATE | BYTENESS */
+#define SBYTE    0x40002000L          /* for op r16/32,immediate instrs. */
+               
+/* Register names automatically generated from regs.dat */
+#include "regs.h"
+
+enum {                                /* condition code names */
+    C_A, C_AE, C_B, C_BE, C_C, C_E, C_G, C_GE, C_L, C_LE, C_NA, C_NAE,
+    C_NB, C_NBE, C_NC, C_NE, C_NG, C_NGE, C_NL, C_NLE, C_NO, C_NP,
+    C_NS, C_NZ, C_O, C_P, C_PE, C_PO, C_S, C_Z
+};
+
+/*
+ * Note that because segment registers may be used as instruction
+ * prefixes, we must ensure the enumerations for prefixes and
+ * register names do not overlap.
+ */
+enum {                                /* instruction prefixes */
+    PREFIX_ENUM_START = REG_ENUM_LIMIT,
+    P_A16 = PREFIX_ENUM_START, P_A32, P_LOCK, P_O16, P_O32, P_REP, P_REPE,
+    P_REPNE, P_REPNZ, P_REPZ, P_TIMES
+};
+
+enum {                                /* extended operand types */
+    EOT_NOTHING, EOT_DB_STRING, EOT_DB_NUMBER
+};
+
+enum {                                /* special EA flags */
+    EAF_BYTEOFFS = 1,                 /* force offset part to byte size */
+    EAF_WORDOFFS = 2,                 /* force offset part to [d]word size */
+    EAF_TIMESTWO = 4                  /* really do EAX*2 not EAX+EAX */
+};
+
+enum {                                /* values for `hinttype' */
+    EAH_NOHINT = 0,                   /* no hint at all - our discretion */
+    EAH_MAKEBASE = 1,                 /* try to make given reg the base */
+    EAH_NOTBASE = 2                   /* try _not_ to make reg the base */
+};
+
+typedef struct {                      /* operand to an instruction */
+    long type;                        /* type of operand */
+    int addr_size;                    /* 0 means default; 16; 32 */
+    int basereg, indexreg, scale;      /* registers and scale involved */
+    int hintbase, hinttype;           /* hint as to real base register */
+    long segment;                     /* immediate segment, if needed */
+    long offset;                      /* any immediate number */
+    long wrt;                         /* segment base it's relative to */
+    int eaflags;                      /* special EA flags */
+    int opflags;                      /* see OPFLAG_* defines below */
+} operand;
+
+#define OPFLAG_FORWARD         1      /* operand is a forward reference */
+#define OPFLAG_EXTERN          2      /* operand is an external reference */
+
+typedef struct extop {                /* extended operand */
+    struct extop *next;                       /* linked list */
+    long type;                        /* defined above */
+    char *stringval;                  /* if it's a string, then here it is */
+    int stringlen;                    /* ... and here's how long it is */
+    long segment;                     /* if it's a number/address, then... */
+    long offset;                      /* ... it's given here ... */
+    long wrt;                         /* ... and here */
+} extop;
+
+#define MAXPREFIX 4
+
+typedef struct {                      /* an instruction itself */
+    char *label;                      /* the label defined, or NULL */
+    int prefixes[MAXPREFIX];          /* instruction prefixes, if any */
+    int nprefix;                      /* number of entries in above */
+    int opcode;                               /* the opcode - not just the string */
+    int condition;                    /* the condition code, if Jcc/SETcc */
+    int operands;                     /* how many operands? 0-3 
+                                        * (more if db et al) */
+    operand oprs[3];                  /* the operands, defined as above */
+    extop *eops;                      /* extended operands */
+    int eops_float;                    /* true if DD and floating */
+    long times;                               /* repeat count (TIMES prefix) */
+    int forw_ref;                     /* is there a forward reference? */
+} insn;
+
+enum geninfo { GI_SWITCH };
+/*
+ * ------------------------------------------------------------
+ * The data structure defining an output format driver, and the
+ * interfaces to the functions therein.
+ * ------------------------------------------------------------
+ */
+
+struct ofmt {
+    /*
+     * This is a short (one-liner) description of the type of
+     * output generated by the driver.
+     */
+    const char *fullname;
+
+    /*
+     * This is a single keyword used to select the driver.
+     */
+    const char *shortname;
+
+    /*
+     * this is reserved for out module specific help.
+     * It is set to NULL in all the out modules but is not implemented
+     * in the main program
+     */
+    const char *helpstring;
+
+    /*
+     * this is a pointer to the first element of the debug information
+     */
+    struct dfmt **debug_formats;
+
+    /*
+     * and a pointer to the element that is being used
+     * note: this is set to the default at compile time and changed if the
+     * -F option is selected.  If developing a set of new debug formats for
+     * an output format, be sure to set this to whatever default you want
+     *
+     */
+    struct dfmt *current_dfmt;
+
+    /*
+     * This, if non-NULL, is a NULL-terminated list of `char *'s
+     * pointing to extra standard macros supplied by the object
+     * format (e.g. a sensible initial default value of __SECT__,
+     * and user-level equivalents for any format-specific
+     * directives).
+     */
+    const char **stdmac;
+
+    /*
+     * This procedure is called at the start of an output session.
+     * It tells the output format what file it will be writing to,
+     * what routine to report errors through, and how to interface
+     * to the label manager and expression evaluator if necessary.
+     * It also gives it a chance to do other initialisation.
+     */
+    void (*init) (FILE *fp, efunc error, ldfunc ldef, evalfunc eval);
+
+    /*
+     * This procedure is called to pass generic information to the
+     * object file.  The first parameter gives the information type
+     * (currently only command line switches)
+     * and the second parameter gives the value.  This function returns
+     * 1 if recognized, 0 if unrecognized
+     */
+    int (*setinfo)(enum geninfo type, char **string);
+
+    /*
+     * This procedure is called by assemble() to write actual
+     * generated code or data to the object file. Typically it
+     * doesn't have to actually _write_ it, just store it for
+     * later.
+     *
+     * The `type' argument specifies the type of output data, and
+     * usually the size as well: its contents are described below.
+     */
+    void (*output) (long segto, const void *data, unsigned long type,
+                   long segment, long wrt);
+
+    /*
+     * This procedure is called once for every symbol defined in
+     * the module being assembled. It gives the name and value of
+     * the symbol, in NASM's terms, and indicates whether it has
+     * been declared to be global. Note that the parameter "name",
+     * when passed, will point to a piece of static storage
+     * allocated inside the label manager - it's safe to keep using
+     * that pointer, because the label manager doesn't clean up
+     * until after the output driver has.
+     *
+     * Values of `is_global' are: 0 means the symbol is local; 1
+     * means the symbol is global; 2 means the symbol is common (in
+     * which case `offset' holds the _size_ of the variable).
+     * Anything else is available for the output driver to use
+     * internally.
+     *
+     * This routine explicitly _is_ allowed to call the label
+     * manager to define further symbols, if it wants to, even
+     * though it's been called _from_ the label manager. That much
+     * re-entrancy is guaranteed in the label manager. However, the
+     * label manager will in turn call this routine, so it should
+     * be prepared to be re-entrant itself.
+     *
+     * The `special' parameter contains special information passed
+     * through from the command that defined the label: it may have
+     * been an EXTERN, a COMMON or a GLOBAL. The distinction should
+     * be obvious to the output format from the other parameters.
+     */
+    void (*symdef) (char *name, long segment, long offset, int is_global,
+                   char *special);
+
+    /*
+     * This procedure is called when the source code requests a
+     * segment change. It should return the corresponding segment
+     * _number_ for the name, or NO_SEG if the name is not a valid
+     * segment name.
+     *
+     * It may also be called with NULL, in which case it is to
+     * return the _default_ section number for starting assembly in.
+     *
+     * It is allowed to modify the string it is given a pointer to.
+     *
+     * It is also allowed to specify a default instruction size for
+     * the segment, by setting `*bits' to 16 or 32. Or, if it
+     * doesn't wish to define a default, it can leave `bits' alone.
+     */
+    long (*section) (char *name, int pass, int *bits);
+
+    /*
+     * This procedure is called to modify the segment base values
+     * returned from the SEG operator. It is given a segment base
+     * value (i.e. a segment value with the low bit set), and is
+     * required to produce in return a segment value which may be
+     * different. It can map segment bases to absolute numbers by
+     * means of returning SEG_ABS types.
+     *
+     * It should return NO_SEG if the segment base cannot be
+     * determined; the evaluator (which calls this routine) is
+     * responsible for throwing an error condition if that occurs
+     * in pass two or in a critical expression.
+     */
+    long (*segbase) (long segment);
+
+    /*
+     * This procedure is called to allow the output driver to
+     * process its own specific directives. When called, it has the
+     * directive word in `directive' and the parameter string in
+     * `value'. It is called in both assembly passes, and `pass'
+     * will be either 1 or 2.
+     *
+     * This procedure should return zero if it does not _recognise_
+     * the directive, so that the main program can report an error.
+     * If it recognises the directive but then has its own errors,
+     * it should report them itself and then return non-zero. It
+     * should also return non-zero if it correctly processes the
+     * directive.
+     */
+    int (*directive) (char *directive, char *value, int pass);
+
+    /*
+     * This procedure is called before anything else - even before
+     * the "init" routine - and is passed the name of the input
+     * file from which this output file is being generated. It
+     * should return its preferred name for the output file in
+     * `outname', if outname[0] is not '\0', and do nothing to
+     * `outname' otherwise. Since it is called before the driver is
+     * properly initialised, it has to be passed its error handler
+     * separately.
+     *
+     * This procedure may also take its own copy of the input file
+     * name for use in writing the output file: it is _guaranteed_
+     * that it will be called before the "init" routine.
+     *
+     * The parameter `outname' points to an area of storage
+     * guaranteed to be at least FILENAME_MAX in size.
+     */
+    void (*filename) (char *inname, char *outname, efunc error);
+
+    /*
+     * This procedure is called after assembly finishes, to allow
+     * the output driver to clean itself up and free its memory.
+     * Typically, it will also be the point at which the object
+     * file actually gets _written_.
+     *
+     * One thing the cleanup routine should always do is to close
+     * the output file pointer.
+     */
+    void (*cleanup) (int debuginfo);
+};
+
+/*
+ * values for the `type' parameter to an output function. Each one
+ * must have the actual number of _bytes_ added to it.
+ *
+ * Exceptions are OUT_RELxADR, which denote an x-byte relocation
+ * which will be a relative jump. For this we need to know the
+ * distance in bytes from the start of the relocated record until
+ * the end of the containing instruction. _This_ is what is stored
+ * in the size part of the parameter, in this case.
+ *
+ * Also OUT_RESERVE denotes reservation of N bytes of BSS space,
+ * and the contents of the "data" parameter is irrelevant.
+ *
+ * The "data" parameter for the output function points to a "long",
+ * containing the address in question, unless the type is
+ * OUT_RAWDATA, in which case it points to an "unsigned char"
+ * array.
+ */
+#define OUT_RAWDATA 0x00000000UL
+#define OUT_ADDRESS 0x10000000UL
+#define OUT_REL2ADR 0x20000000UL
+#define OUT_REL4ADR 0x30000000UL
+#define OUT_RESERVE 0x40000000UL
+#define OUT_TYPMASK 0xF0000000UL
+#define OUT_SIZMASK 0x0FFFFFFFUL
+
+/*
+ * ------------------------------------------------------------
+ * The data structure defining a debug format driver, and the
+ * interfaces to the functions therein.
+ * ------------------------------------------------------------
+ */
+
+struct dfmt {
+    
+    /*
+     * This is a short (one-liner) description of the type of
+     * output generated by the driver.
+     */
+    const char *fullname;
+
+    /*
+     * This is a single keyword used to select the driver.
+     */
+    const char *shortname;
+
+
+    /*
+     * init - called initially to set up local pointer to object format, 
+     * void pointer to implementation defined data, file pointer (which
+     * probably won't be used, but who knows?), and error function.
+     */
+    void (*init) (struct ofmt * of, void * id, FILE * fp, efunc error);
+
+    /*
+     * linenum - called any time there is output with a change of
+     * line number or file.
+     */
+    void (*linenum) (const char * filename, long linenumber, long segto);
+
+    /*
+     * debug_deflabel - called whenever a label is defined. Parameters
+     * are the same as to 'symdef()' in the output format. This function
+     * would be called before the output format version.
+     */
+
+    void (*debug_deflabel) (char * name, long segment, long offset,
+                            int is_global, char * special);
+    /*
+     * debug_directive - called whenever a DEBUG directive other than 'LINE'
+     * is encountered. 'directive' contains the first parameter to the
+     * DEBUG directive, and params contains the rest. For example,
+     * 'DEBUG VAR _somevar:int' would translate to a call to this
+     * function with 'directive' equal to "VAR" and 'params' equal to 
+     * "_somevar:int".
+     */
+    void (*debug_directive) (const char * directive, const char * params);
+
+    /*
+     * typevalue - called whenever the assembler wishes to register a type
+     * for the last defined label.  This routine MUST detect if a type was
+     * already registered and not re-register it.
+     */
+    void (*debug_typevalue) (long type);
+
+    /*
+     * debug_output - called whenever output is required
+     * 'type' is the type of info required, and this is format-specific
+     */
+    void (*debug_output) (int type, void *param);
+
+    /*
+     * cleanup - called after processing of file is complete
+     */
+    void (*cleanup) (void);
+
+};
+/*
+ * The type definition macros
+ * for debugging
+ *
+ * low 3 bits: reserved
+ * next 5 bits: type
+ * next 24 bits: number of elements for arrays (0 for labels)
+ */
+
+#define TY_UNKNOWN 0x00
+#define TY_LABEL   0x08
+#define TY_BYTE    0x10
+#define TY_WORD    0x18
+#define TY_DWORD   0x20
+#define TY_FLOAT   0x28
+#define TY_QWORD   0x30
+#define TY_TBYTE   0x38
+#define TY_COMMON  0xE0
+#define TY_SEG     0xE8
+#define TY_EXTERN  0xF0
+#define TY_EQU     0xF8
+
+#define TYM_TYPE(x) ((x) & 0xF8)
+#define TYM_ELEMENTS(x) (((x) & 0xFFFFFF00) >> 8)
+
+#define TYS_ELEMENTS(x)  ((x) << 8)
+/*
+ * -----
+ * Other
+ * -----
+ */
+
+/*
+ * This is a useful #define which I keep meaning to use more often:
+ * the number of elements of a statically defined array.
+ */
+
+#define elements(x)     ( sizeof(x) / sizeof(*(x)) )
+
+extern int tasm_compatible_mode;
+
+/*
+ * This declaration passes the "pass" number to all other modules
+ * "pass0" assumes the values: 0, 0, ..., 0, 1, 2
+ * where 0 = optimizing pass
+ *       1 = pass 1
+ *       2 = pass 2
+ */
+
+extern int pass0;      /* this is globally known */
+extern int optimizing;
+
+#endif
diff --git a/src/preprocs/nasm/nasmlib.c b/src/preprocs/nasm/nasmlib.c
new file mode 100644 (file)
index 0000000..bb0ab29
--- /dev/null
@@ -0,0 +1,1116 @@
+/* nasmlib.c   library routines for the Netwide Assembler
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "nasm.h"
+#include "nasmlib.h"
+#include "insns.h"             /* For MAX_KEYWORD */
+
+static efunc nasm_malloc_error;
+
+#ifdef LOGALLOC
+static FILE *logfp;
+#endif
+
+void nasm_set_malloc_error (efunc error) 
+{
+    nasm_malloc_error = error;
+#ifdef LOGALLOC
+    logfp = fopen ("malloc.log", "w");
+    setvbuf (logfp, NULL, _IOLBF, BUFSIZ);
+    fprintf (logfp, "null pointer is %p\n", NULL);
+#endif
+}
+
+#ifdef LOGALLOC
+void *nasm_malloc_log (char *file, int line, size_t size)
+#else
+void *nasm_malloc (size_t size)
+#endif
+{
+    void *p = malloc(size);
+    if (!p)
+       nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
+#ifdef LOGALLOC
+    else
+       fprintf(logfp, "%s %d malloc(%ld) returns %p\n",
+               file, line, (long)size, p);
+#endif
+    return p;
+}
+
+#ifdef LOGALLOC
+void *nasm_realloc_log (char *file, int line, void *q, size_t size)
+#else
+void *nasm_realloc (void *q, size_t size)
+#endif
+{
+    void *p = q ? realloc(q, size) : malloc(size);
+    if (!p)
+       nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
+#ifdef LOGALLOC
+    else if (q)
+       fprintf(logfp, "%s %d realloc(%p,%ld) returns %p\n",
+               file, line, q, (long)size, p);
+    else
+       fprintf(logfp, "%s %d malloc(%ld) returns %p\n",
+               file, line, (long)size, p);
+#endif
+    return p;
+}
+
+#ifdef LOGALLOC
+void nasm_free_log (char *file, int line, void *q)
+#else
+void nasm_free (void *q)
+#endif
+{
+    if (q) {
+       free (q);
+#ifdef LOGALLOC
+       fprintf(logfp, "%s %d free(%p)\n",
+               file, line, q);
+#endif
+    }
+}
+
+#ifdef LOGALLOC
+char *nasm_strdup_log (char *file, int line, const char *s)
+#else
+char *nasm_strdup (const char *s)
+#endif
+{
+    char *p;
+    int size = strlen(s)+1;
+
+    p = malloc(size);
+    if (!p)
+       nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
+#ifdef LOGALLOC
+    else
+       fprintf(logfp, "%s %d strdup(%ld) returns %p\n",
+               file, line, (long)size, p);
+#endif
+    strcpy (p, s);
+    return p;
+}
+
+#ifdef LOGALLOC
+char *nasm_strndup_log (char *file, int line, char *s, size_t len)
+#else
+char *nasm_strndup (char *s, size_t len)
+#endif
+{
+    char *p;
+    int size = len+1;
+
+    p = malloc(size);
+    if (!p)
+       nasm_malloc_error (ERR_FATAL | ERR_NOFILE, "out of memory");
+#ifdef LOGALLOC
+    else
+       fprintf(logfp, "%s %d strndup(%ld) returns %p\n",
+               file, line, (long)size, p);
+#endif
+    strncpy (p, s, len);
+    p[len] = '\0';
+    return p;
+}
+
+#if !defined(stricmp) && !defined(strcasecmp)
+int nasm_stricmp (const char *s1, const char *s2) 
+{
+    while (*s1 && tolower(*s1) == tolower(*s2))
+       s1++, s2++;
+    if (!*s1 && !*s2)
+       return 0;
+    else if (tolower(*s1) < tolower(*s2))
+       return -1;
+    else
+       return 1;
+}
+#endif
+
+#if !defined(strnicmp) && !defined(strncasecmp)
+int nasm_strnicmp (const char *s1, const char *s2, int n) 
+{
+    while (n > 0 && *s1 && tolower(*s1) == tolower(*s2))
+       s1++, s2++, n--;
+    if ((!*s1 && !*s2) || n==0)
+       return 0;
+    else if (tolower(*s1) < tolower(*s2))
+       return -1;
+    else
+       return 1;
+}
+#endif
+
+#define lib_isnumchar(c)   ( isalnum(c) || (c) == '$')
+#define numvalue(c)  ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0')
+
+long readnum (char *str, int *error) 
+{
+    char *r = str, *q;
+    long radix;
+    unsigned long result, checklimit;
+    int digit, last;
+    int warn = FALSE;
+    int sign = 1;
+
+    *error = FALSE;
+
+    while (isspace(*r)) r++;          /* find start of number */
+
+    /*
+     * If the number came from make_tok_num (as a result of an %assign), it
+     * might have a '-' built into it (rather than in a preceeding token).
+     */
+    if (*r == '-')
+    {
+       r++;
+       sign = -1;
+    }
+
+    q = r;
+
+    while (lib_isnumchar(*q)) q++;     /* find end of number */
+
+    /*
+     * If it begins 0x, 0X or $, or ends in H, it's in hex. if it
+     * ends in Q, it's octal. if it ends in B, it's binary.
+     * Otherwise, it's ordinary decimal.
+     */
+    if (*r=='0' && (r[1]=='x' || r[1]=='X'))
+       radix = 16, r += 2;
+    else if (*r=='$')
+       radix = 16, r++;
+    else if (q[-1]=='H' || q[-1]=='h')
+       radix = 16 , q--;
+    else if (q[-1]=='Q' || q[-1]=='q')
+       radix = 8 , q--;
+    else if (q[-1]=='B' || q[-1]=='b')
+       radix = 2 , q--;
+    else
+       radix = 10;
+
+    /*
+     * If this number has been found for us by something other than
+     * the ordinary scanners, then it might be malformed by having
+     * nothing between the prefix and the suffix. Check this case
+     * now.
+     */
+    if (r >= q) {
+       *error = TRUE;
+       return 0;
+    }
+
+    /*
+     * `checklimit' must be 2**32 / radix. We can't do that in
+     * 32-bit arithmetic, which we're (probably) using, so we
+     * cheat: since we know that all radices we use are even, we
+     * can divide 2**31 by radix/2 instead.
+     */
+    checklimit = 0x80000000UL / (radix>>1);
+
+    /*
+     * Calculate the highest allowable value for the last digit
+     * of a 32 bit constant... in radix 10, it is 6, otherwise it is 0
+     */
+    last = (radix == 10 ? 6 : 0);
+
+    result = 0;
+    while (*r && r < q) {
+       if (*r<'0' || (*r>'9' && *r<'A') || (digit = numvalue(*r)) >= radix) 
+       {
+           *error = TRUE;
+           return 0;
+       }
+       if (result > checklimit ||
+           (result == checklimit && digit >= last))
+       {
+           warn = TRUE;
+       }
+
+       result = radix * result + digit;
+       r++;
+    }
+
+    if (warn)
+       nasm_malloc_error (ERR_WARNING | ERR_PASS1 | ERR_WARN_NOV,
+                          "numeric constant %s does not fit in 32 bits",
+                          str);
+
+    return result*sign;
+}
+
+long readstrnum (char *str, int length, int *warn) 
+{
+    long charconst = 0;
+    int i;
+
+    *warn = FALSE;
+
+    str += length;
+    for (i=0; i<length; i++) {
+       if (charconst & 0xff000000UL) {
+           *warn = TRUE;
+       }
+       charconst = (charconst<<8) + (unsigned char) *--str;
+    }
+    return charconst;
+}
+
+static long next_seg;
+
+void seg_init(void) 
+{
+    next_seg = 0;
+}
+
+long seg_alloc(void) 
+{
+    return (next_seg += 2) - 2;
+}
+
+void fwriteshort (int data, FILE *fp) 
+{
+    fputc ((int) (data & 255), fp);
+    fputc ((int) ((data >> 8) & 255), fp);
+}
+
+void fwritelong (long data, FILE *fp) 
+{
+    fputc ((int) (data & 255), fp);
+    fputc ((int) ((data >> 8) & 255), fp);
+    fputc ((int) ((data >> 16) & 255), fp);
+    fputc ((int) ((data >> 24) & 255), fp);
+}
+
+void standard_extension (char *inname, char *outname, char *extension,
+                        efunc error) 
+{
+    char *p, *q;
+
+    if (*outname)                     /* file name already exists, */
+       return;                        /* so do nothing */
+    q = inname;
+    p = outname;
+    while (*q) *p++ = *q++;           /* copy, and find end of string */
+    *p = '\0';                        /* terminate it */
+    while (p > outname && *--p != '.');/* find final period (or whatever) */
+    if (*p != '.') while (*p) p++;     /* go back to end if none found */
+    if (!strcmp(p, extension)) {       /* is the extension already there? */
+       if (*extension)
+           error(ERR_WARNING | ERR_NOFILE,
+                 "file name already ends in `%s': "
+                 "output will be in `nasm.out'",
+                 extension);
+       else
+           error(ERR_WARNING | ERR_NOFILE,
+                 "file name already has no extension: "
+                 "output will be in `nasm.out'");
+       strcpy(outname, "nasm.out");
+    } else
+       strcpy(p, extension);
+}
+
+#define LEAFSIZ (sizeof(RAA)-sizeof(RAA_UNION)+sizeof(RAA_LEAF))
+#define BRANCHSIZ (sizeof(RAA)-sizeof(RAA_UNION)+sizeof(RAA_BRANCH))
+
+#define LAYERSIZ(r) ( (r)->layers==0 ? RAA_BLKSIZE : RAA_LAYERSIZE )
+
+static struct RAA *real_raa_init (int layers) 
+{
+    struct RAA *r;
+    int i;
+
+    if (layers == 0) {
+       r = nasm_malloc (LEAFSIZ);
+       r->layers = 0;
+       memset (r->u.l.data, 0, sizeof(r->u.l.data));
+       r->stepsize = 1L;
+    } else {
+       r = nasm_malloc (BRANCHSIZ);
+       r->layers = layers;
+       for ( i = 0 ; i < RAA_LAYERSIZE ; i++ )
+         r->u.b.data[i] = NULL;
+       r->stepsize = RAA_BLKSIZE;
+       while (--layers)
+           r->stepsize *= RAA_LAYERSIZE;
+    }
+    return r;
+}
+
+struct RAA *raa_init (void) 
+{
+    return real_raa_init (0);
+}
+
+void raa_free (struct RAA *r) 
+{
+    if (r->layers == 0)
+       nasm_free (r);
+    else {
+       struct RAA **p;
+       for (p = r->u.b.data; p - r->u.b.data < RAA_LAYERSIZE; p++)
+           if (*p)
+               raa_free (*p);
+    }
+}
+
+long raa_read (struct RAA *r, long posn) 
+{
+    if (posn >= r->stepsize * LAYERSIZ(r))
+       return 0;               /* Return 0 for undefined entries */
+    while (r->layers > 0) {
+       ldiv_t l;
+       l = ldiv (posn, r->stepsize);
+       r = r->u.b.data[l.quot];
+       posn = l.rem;
+       if (!r)
+           return 0;           /* Return 0 for undefined entries */
+    }
+    return r->u.l.data[posn];
+}
+
+struct RAA *raa_write (struct RAA *r, long posn, long value) 
+{
+    struct RAA *result;
+
+    if (posn < 0)
+       nasm_malloc_error (ERR_PANIC, "negative position in raa_write");
+
+    while (r->stepsize * LAYERSIZ(r) <= posn) {
+       /*
+        * Must add a layer.
+        */
+       struct RAA *s;
+       int i;
+
+       s = nasm_malloc (BRANCHSIZ);
+       for ( i = 0 ; i < RAA_LAYERSIZE ; i++ )
+           s->u.b.data[i] = NULL;
+       s->layers = r->layers + 1;
+       s->stepsize = LAYERSIZ(r) * r->stepsize;
+       s->u.b.data[0] = r;
+       r = s;
+    }
+
+    result = r;
+
+    while (r->layers > 0) {
+       ldiv_t l;
+       struct RAA **s;
+       l = ldiv (posn, r->stepsize);
+       s = &r->u.b.data[l.quot];
+       if (!*s)
+           *s = real_raa_init (r->layers - 1);
+       r = *s;
+       posn = l.rem;
+    }
+
+    r->u.l.data[posn] = value;
+
+    return result;
+}
+
+#define SAA_MAXLEN 8192
+
+struct SAA *saa_init (long elem_len) 
+{
+    struct SAA *s;
+
+    if (elem_len > SAA_MAXLEN)
+       nasm_malloc_error (ERR_PANIC | ERR_NOFILE, "SAA with huge elements");
+
+    s = nasm_malloc (sizeof(struct SAA));
+    s->posn = s->start = 0L;
+    s->elem_len = elem_len;
+    s->length = SAA_MAXLEN - (SAA_MAXLEN % elem_len);
+    s->data = nasm_malloc (s->length);
+    s->next = NULL;
+    s->end = s;
+
+    return s;
+}
+
+void saa_free (struct SAA *s) 
+{
+    struct SAA *t;
+
+    while (s) {
+       t = s->next;
+       nasm_free (s->data);
+       nasm_free (s);
+       s = t;
+    }
+}
+
+void *saa_wstruct (struct SAA *s) 
+{
+    void *p;
+
+    if (s->end->length - s->end->posn < s->elem_len) {
+       s->end->next = nasm_malloc (sizeof(struct SAA));
+       s->end->next->start = s->end->start + s->end->posn;
+       s->end = s->end->next;
+       s->end->length = s->length;
+       s->end->next = NULL;
+       s->end->posn = 0L;
+       s->end->data = nasm_malloc (s->length);
+    }
+
+    p = s->end->data + s->end->posn;
+    s->end->posn += s->elem_len;
+    return p;
+}
+
+void saa_wbytes (struct SAA *s, const void *data, long len) 
+{
+    const char *d = data;
+
+    while (len > 0) {
+       long l = s->end->length - s->end->posn;
+       if (l > len)
+           l = len;
+       if (l > 0) {
+           if (d) {
+               memcpy (s->end->data + s->end->posn, d, l);
+               d += l;
+           } else
+               memset (s->end->data + s->end->posn, 0, l);
+           s->end->posn += l;
+           len -= l;
+       }
+       if (len > 0) {
+           s->end->next = nasm_malloc (sizeof(struct SAA));
+           s->end->next->start = s->end->start + s->end->posn;
+           s->end = s->end->next;
+           s->end->length = s->length;
+           s->end->next = NULL;
+           s->end->posn = 0L;
+           s->end->data = nasm_malloc (s->length);
+       }
+    }
+}
+
+void saa_rewind (struct SAA *s) 
+{
+    s->rptr = s;
+    s->rpos = 0L;
+}
+
+void *saa_rstruct (struct SAA *s) 
+{
+    void *p;
+
+    if (!s->rptr)
+       return NULL;
+
+    if (s->rptr->posn - s->rpos < s->elem_len) {
+       s->rptr = s->rptr->next;
+       if (!s->rptr)
+           return NULL;               /* end of array */
+       s->rpos = 0L;
+    }
+
+    p = s->rptr->data + s->rpos;
+    s->rpos += s->elem_len;
+    return p;
+}
+
+void *saa_rbytes (struct SAA *s, long *len) 
+{
+    void *p;
+
+    if (!s->rptr)
+       return NULL;
+
+    p = s->rptr->data + s->rpos;
+    *len = s->rptr->posn - s->rpos;
+    s->rptr = s->rptr->next;
+    s->rpos = 0L;
+    return p;
+}
+
+void saa_rnbytes (struct SAA *s, void *data, long len) 
+{
+    char *d = data;
+
+    while (len > 0) {
+       long l;
+
+       if (!s->rptr)
+           return;
+
+       l = s->rptr->posn - s->rpos;
+       if (l > len)
+           l = len;
+       if (l > 0) {
+           memcpy (d, s->rptr->data + s->rpos, l);
+           d += l;
+           s->rpos += l;
+           len -= l;
+       }
+       if (len > 0) {
+           s->rptr = s->rptr->next;
+           s->rpos = 0L;
+       }
+    }
+}
+
+void saa_fread (struct SAA *s, long posn, void *data, long len) 
+{
+    struct SAA *p;
+    long pos;
+    char *cdata = data;
+
+    if (!s->rptr || posn < s->rptr->start)
+       saa_rewind (s);
+    p = s->rptr;
+    while (posn >= p->start + p->posn) {
+       p = p->next;
+       if (!p)
+           return;                    /* what else can we do?! */
+    }
+
+    pos = posn - p->start;
+    while (len) {
+       long l = p->posn - pos;
+       if (l > len)
+           l = len;
+       memcpy (cdata, p->data+pos, l);
+       len -= l;
+       cdata += l;
+       p = p->next;
+       if (!p)
+           return;
+       pos = 0L;
+    }
+    s->rptr = p;
+}
+
+void saa_fwrite (struct SAA *s, long posn, void *data, long len) 
+{
+    struct SAA *p;
+    long pos;
+    char *cdata = data;
+
+    if (!s->rptr || posn < s->rptr->start)
+       saa_rewind (s);
+    p = s->rptr;
+    while (posn >= p->start + p->posn) {
+       p = p->next;
+       if (!p)
+           return;                    /* what else can we do?! */
+    }
+
+    pos = posn - p->start;
+    while (len) {
+       long l = p->posn - pos;
+       if (l > len)
+           l = len;
+       memcpy (p->data+pos, cdata, l);
+       len -= l;
+       cdata += l;
+       p = p->next;
+       if (!p)
+           return;
+       pos = 0L;
+    }
+    s->rptr = p;
+}
+
+void saa_fpwrite (struct SAA *s, FILE *fp) 
+{
+    char *data;
+    long len;
+
+    saa_rewind (s);
+    while ( (data = saa_rbytes (s, &len)) )
+       fwrite (data, 1, len, fp);
+}
+
+/*
+ * Register, instruction, condition-code and prefix keywords used
+ * by the scanner.
+ */
+#include "names.c"
+static const char *special_names[] = {
+    "byte", "dword", "far", "long", "near", "nosplit", "qword",
+    "short", "strict", "to", "tword", "word"
+};
+static const char *prefix_names[] = {
+    "a16", "a32", "lock", "o16", "o32", "rep", "repe", "repne",
+    "repnz", "repz", "times"
+};
+
+
+/*
+ * Standard scanner routine used by parser.c and some output
+ * formats. It keeps a succession of temporary-storage strings in
+ * stdscan_tempstorage, which can be cleared using stdscan_reset.
+ */
+static char **stdscan_tempstorage = NULL;
+static int stdscan_tempsize = 0, stdscan_templen = 0;
+#define STDSCAN_TEMP_DELTA 256
+
+static void stdscan_pop(void) 
+{
+    nasm_free (stdscan_tempstorage[--stdscan_templen]);
+}
+
+void stdscan_reset(void) 
+{
+    while (stdscan_templen > 0)
+       stdscan_pop();
+}
+
+/*
+ * Unimportant cleanup is done to avoid confusing people who are trying
+ * to debug real memory leaks
+ */
+void nasmlib_cleanup (void) 
+{
+    stdscan_reset();
+    nasm_free (stdscan_tempstorage);
+}
+
+static char *stdscan_copy(char *p, int len) 
+{
+    char *text;
+
+    text = nasm_malloc(len+1);
+    strncpy (text, p, len);
+    text[len] = '\0';
+
+    if (stdscan_templen >= stdscan_tempsize) {
+       stdscan_tempsize += STDSCAN_TEMP_DELTA;
+       stdscan_tempstorage = nasm_realloc(stdscan_tempstorage,
+                                          stdscan_tempsize*sizeof(char *));
+    }
+    stdscan_tempstorage[stdscan_templen++] = text;
+
+    return text;
+}
+
+char *stdscan_bufptr = NULL;
+int stdscan (void *private_data, struct tokenval *tv) 
+{
+    char ourcopy[MAX_KEYWORD+1], *r, *s;
+
+    (void) private_data;  /* Don't warn that this parameter is unused */
+
+    while (isspace(*stdscan_bufptr)) stdscan_bufptr++;
+    if (!*stdscan_bufptr)
+       return tv->t_type = 0;
+
+    /* we have a token; either an id, a number or a char */
+    if (isidstart(*stdscan_bufptr) ||
+       (*stdscan_bufptr == '$' && isidstart(stdscan_bufptr[1]))) {
+       /* now we've got an identifier */
+       int i;
+       int is_sym = FALSE;
+
+       if (*stdscan_bufptr == '$') {
+           is_sym = TRUE;
+           stdscan_bufptr++;
+       }
+
+       r = stdscan_bufptr++;
+       while (isidchar(*stdscan_bufptr)) stdscan_bufptr++;
+       tv->t_charptr = stdscan_copy(r, stdscan_bufptr - r);
+
+       if (is_sym || stdscan_bufptr-r > MAX_KEYWORD)
+           return tv->t_type = TOKEN_ID;/* bypass all other checks */
+    
+       for (s=tv->t_charptr, r=ourcopy; *s; s++)
+           *r++ = tolower (*s);
+       *r = '\0';
+       /* right, so we have an identifier sitting in temp storage. now,
+        * is it actually a register or instruction name, or what? */
+       if ((tv->t_integer=bsi(ourcopy, reg_names,
+                              elements(reg_names)))>=0) {
+           tv->t_integer += EXPR_REG_START;
+           return tv->t_type = TOKEN_REG;
+       } else if ((tv->t_integer=bsi(ourcopy, insn_names,
+                                     elements(insn_names)))>=0) {
+           return tv->t_type = TOKEN_INSN;
+       }
+       for (i=0; i<elements(icn); i++)
+           if (!strncmp(ourcopy, icn[i], strlen(icn[i]))) {
+               char *p = ourcopy + strlen(icn[i]);
+               tv->t_integer = ico[i];
+               if ((tv->t_inttwo=bsi(p, conditions,
+                                        elements(conditions)))>=0)
+                   return tv->t_type = TOKEN_INSN;
+           }
+       if ((tv->t_integer=bsi(ourcopy, prefix_names,
+                                 elements(prefix_names)))>=0) {
+           tv->t_integer += PREFIX_ENUM_START;
+           return tv->t_type = TOKEN_PREFIX;
+       }
+       if ((tv->t_integer=bsi(ourcopy, special_names,
+                                 elements(special_names)))>=0)
+           return tv->t_type = TOKEN_SPECIAL;
+       if (!nasm_stricmp(ourcopy, "seg"))
+           return tv->t_type = TOKEN_SEG;
+       if (!nasm_stricmp(ourcopy, "wrt"))
+           return tv->t_type = TOKEN_WRT;
+       return tv->t_type = TOKEN_ID;
+    } else if (*stdscan_bufptr == '$' && !isnumchar(stdscan_bufptr[1])) {
+       /*
+        * It's a $ sign with no following hex number; this must
+        * mean it's a Here token ($), evaluating to the current
+        * assembly location, or a Base token ($$), evaluating to
+        * the base of the current segment.
+        */
+       stdscan_bufptr++;
+       if (*stdscan_bufptr == '$') {
+           stdscan_bufptr++;
+           return tv->t_type = TOKEN_BASE;
+       }
+       return tv->t_type = TOKEN_HERE;
+    } else if (isnumstart(*stdscan_bufptr)) {  /* now we've got a number */
+       int rn_error;
+
+       r = stdscan_bufptr++;
+       while (isnumchar(*stdscan_bufptr))
+           stdscan_bufptr++;
+
+       if (*stdscan_bufptr == '.') {
+           /*
+            * a floating point constant
+            */
+           stdscan_bufptr++;
+           while (isnumchar(*stdscan_bufptr) ||
+                  ((stdscan_bufptr[-1] == 'e' || stdscan_bufptr[-1] == 'E')
+                   && (*stdscan_bufptr == '-' || *stdscan_bufptr == '+')) ) 
+           {
+               stdscan_bufptr++;
+           }
+           tv->t_charptr = stdscan_copy(r, stdscan_bufptr - r);
+           return tv->t_type = TOKEN_FLOAT;
+       }
+       r = stdscan_copy(r, stdscan_bufptr - r);
+       tv->t_integer = readnum(r, &rn_error);
+       stdscan_pop();
+       if (rn_error)
+           return tv->t_type = TOKEN_ERRNUM;/* some malformation occurred */
+       tv->t_charptr = NULL;
+       return tv->t_type = TOKEN_NUM;
+    } else if (*stdscan_bufptr == '\'' ||
+              *stdscan_bufptr == '"') {/* a char constant */
+       char quote = *stdscan_bufptr++, *r;
+       int rn_warn;
+       r = tv->t_charptr = stdscan_bufptr;
+       while (*stdscan_bufptr && *stdscan_bufptr != quote) stdscan_bufptr++;
+       tv->t_inttwo = stdscan_bufptr - r;      /* store full version */
+       if (!*stdscan_bufptr)
+           return tv->t_type = TOKEN_ERRNUM;       /* unmatched quotes */
+       stdscan_bufptr++;                       /* skip over final quote */
+       tv->t_integer = readstrnum(r, tv->t_inttwo, &rn_warn);
+       /* FIXME: rn_warn is not checked! */
+       return tv->t_type = TOKEN_NUM;
+    } else if (*stdscan_bufptr == ';') {  /* a comment has happened - stay */
+       return tv->t_type = 0;
+    } else if (stdscan_bufptr[0] == '>' && stdscan_bufptr[1] == '>') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_SHR;
+    } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '<') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_SHL;
+    } else if (stdscan_bufptr[0] == '/' && stdscan_bufptr[1] == '/') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_SDIV;
+    } else if (stdscan_bufptr[0] == '%' && stdscan_bufptr[1] == '%') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_SMOD;
+    } else if (stdscan_bufptr[0] == '=' && stdscan_bufptr[1] == '=') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_EQ;
+    } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '>') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_NE;
+    } else if (stdscan_bufptr[0] == '!' && stdscan_bufptr[1] == '=') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_NE;
+    } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '=') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_LE;
+    } else if (stdscan_bufptr[0] == '>' && stdscan_bufptr[1] == '=') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_GE;
+    } else if (stdscan_bufptr[0] == '&' && stdscan_bufptr[1] == '&') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_DBL_AND;
+    } else if (stdscan_bufptr[0] == '^' && stdscan_bufptr[1] == '^') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_DBL_XOR;
+    } else if (stdscan_bufptr[0] == '|' && stdscan_bufptr[1] == '|') {
+       stdscan_bufptr += 2;
+       return tv->t_type = TOKEN_DBL_OR;
+    } else                            /* just an ordinary char */
+       return tv->t_type = (unsigned char) (*stdscan_bufptr++);
+}
+
+/*
+ * Return TRUE if the argument is a simple scalar. (Or a far-
+ * absolute, which counts.)
+ */
+int is_simple (expr *vect) 
+{
+    while (vect->type && !vect->value)
+       vect++;
+    if (!vect->type)
+       return 1;
+    if (vect->type != EXPR_SIMPLE)
+       return 0;
+    do {
+       vect++;
+    } while (vect->type && !vect->value);
+    if (vect->type && vect->type < EXPR_SEGBASE+SEG_ABS) return 0;
+    return 1;
+}
+
+/*
+ * Return TRUE if the argument is a simple scalar, _NOT_ a far-
+ * absolute.
+ */
+int is_really_simple (expr *vect) 
+{
+    while (vect->type && !vect->value)
+       vect++;
+    if (!vect->type)
+       return 1;
+    if (vect->type != EXPR_SIMPLE)
+       return 0;
+    do {
+       vect++;
+    } while (vect->type && !vect->value);
+    if (vect->type) return 0;
+    return 1;
+}
+
+/*
+ * Return TRUE if the argument is relocatable (i.e. a simple
+ * scalar, plus at most one segment-base, plus possibly a WRT).
+ */
+int is_reloc (expr *vect) 
+{
+    while (vect->type && !vect->value) /* skip initial value-0 terms */
+       vect++;
+    if (!vect->type)                  /* trivially return TRUE if nothing */
+       return 1;                      /* is present apart from value-0s */
+    if (vect->type < EXPR_SIMPLE)      /* FALSE if a register is present */
+       return 0;
+    if (vect->type == EXPR_SIMPLE) {   /* skip over a pure number term... */
+       do {
+           vect++;
+       } while (vect->type && !vect->value);
+       if (!vect->type)               /* ...returning TRUE if that's all */
+           return 1;
+    }
+    if (vect->type == EXPR_WRT) {      /* skip over a WRT term... */
+       do {
+           vect++;
+       } while (vect->type && !vect->value);
+       if (!vect->type)               /* ...returning TRUE if that's all */
+           return 1;
+    }
+    if (vect->value != 0 && vect->value != 1)
+       return 0;                      /* segment base multiplier non-unity */
+    do {                              /* skip over _one_ seg-base term... */
+       vect++;
+    } while (vect->type && !vect->value);
+    if (!vect->type)                  /* ...returning TRUE if that's all */
+       return 1;
+    return 0;                         /* And return FALSE if there's more */
+}
+
+/*
+ * Return TRUE if the argument contains an `unknown' part.
+ */
+int is_unknown(expr *vect) 
+{
+    while (vect->type && vect->type < EXPR_UNKNOWN)
+       vect++;
+    return (vect->type == EXPR_UNKNOWN);
+}
+
+/*
+ * Return TRUE if the argument contains nothing but an `unknown'
+ * part.
+ */
+int is_just_unknown(expr *vect) 
+{
+    while (vect->type && !vect->value)
+       vect++;
+    return (vect->type == EXPR_UNKNOWN);
+}
+
+/*
+ * Return the scalar part of a relocatable vector. (Including
+ * simple scalar vectors - those qualify as relocatable.)
+ */
+long reloc_value (expr *vect) 
+{
+    while (vect->type && !vect->value)
+       vect++;
+    if (!vect->type) return 0;
+    if (vect->type == EXPR_SIMPLE)
+       return vect->value;
+    else
+       return 0;
+}
+
+/*
+ * Return the segment number of a relocatable vector, or NO_SEG for
+ * simple scalars.
+ */
+long reloc_seg (expr *vect) 
+{
+    while (vect->type && (vect->type == EXPR_WRT || !vect->value))
+       vect++;
+    if (vect->type == EXPR_SIMPLE) {
+       do {
+           vect++;
+       } while (vect->type && (vect->type == EXPR_WRT || !vect->value));
+    }
+    if (!vect->type)
+       return NO_SEG;
+    else
+       return vect->type - EXPR_SEGBASE;
+}
+
+/*
+ * Return the WRT segment number of a relocatable vector, or NO_SEG
+ * if no WRT part is present.
+ */
+long reloc_wrt (expr *vect) 
+{
+    while (vect->type && vect->type < EXPR_WRT)
+       vect++;
+    if (vect->type == EXPR_WRT) {
+       return vect->value;
+    } else
+       return NO_SEG;
+}
+
+/*
+ * Binary search.
+ */
+int bsi (char *string, const char **array, int size) 
+{
+    int i = -1, j = size;             /* always, i < index < j */
+    while (j-i >= 2) {
+       int k = (i+j)/2;
+       int l = strcmp(string, array[k]);
+       if (l<0)                       /* it's in the first half */
+           j = k;
+       else if (l>0)                  /* it's in the second half */
+           i = k;
+       else                           /* we've got it :) */
+           return k;
+    }
+    return -1;                        /* we haven't got it :( */
+}
+
+static char *file_name = NULL;
+static long line_number = 0;
+
+char *src_set_fname(char *newname) 
+{
+    char *oldname = file_name;
+    file_name = newname;
+    return oldname;
+}
+
+long src_set_linnum(long newline) 
+{
+    long oldline = line_number;
+    line_number = newline;
+    return oldline;
+}
+
+long src_get_linnum(void) 
+{
+    return line_number;
+}
+
+int src_get(long *xline, char **xname) 
+{
+    if (!file_name || !*xname || strcmp(*xname, file_name)) 
+    {
+       nasm_free(*xname);
+       *xname = file_name ? nasm_strdup(file_name) : NULL;
+       *xline = line_number;
+       return -2;
+    }
+    if (*xline != line_number) 
+    {
+       long tmp = line_number - *xline;
+       *xline = line_number;
+       return tmp;
+    }
+    return 0;
+}
+
+void nasm_quote(char **str) 
+{
+    int ln=strlen(*str);
+    char q=(*str)[0];
+    char *p;
+    if (ln>1 && (*str)[ln-1]==q && (q=='"' || q=='\''))
+       return;
+    q = '"';
+    if (strchr(*str,q))
+       q = '\'';
+    p = nasm_malloc(ln+3);
+    strcpy(p+1, *str);
+    nasm_free(*str);
+    p[ln+1] = p[0] = q;
+    p[ln+2] = 0;
+    *str = p;
+}
+    
+char *nasm_strcat(char *one, char *two) 
+{
+    char *rslt;
+    int l1=strlen(one);
+    rslt = nasm_malloc(l1+strlen(two)+1);
+    strcpy(rslt, one);
+    strcpy(rslt+l1, two);
+    return rslt;
+}
+
+void null_debug_init(struct ofmt *of, void *id, FILE *fp, efunc error ) {}
+void null_debug_linenum(const char *filename, long linenumber, long segto) {}
+void null_debug_deflabel(char *name, long segment, long offset, int is_global, char *special) {}
+void null_debug_routine(const char *directive, const char *params) {}
+void null_debug_typevalue(long type) {}
+void null_debug_output(int type, void *param) {}
+void null_debug_cleanup(void){}
+
+struct dfmt null_debug_form = {
+    "Null debug format",
+    "null",
+    null_debug_init,
+    null_debug_linenum,
+    null_debug_deflabel,
+    null_debug_routine,
+    null_debug_typevalue,
+    null_debug_output,
+    null_debug_cleanup
+};
+
+struct dfmt *null_debug_arr[2] = { &null_debug_form, NULL };
diff --git a/src/preprocs/nasm/nasmlib.h b/src/preprocs/nasm/nasmlib.h
new file mode 100644 (file)
index 0000000..5496453
--- /dev/null
@@ -0,0 +1,258 @@
+/* nasmlib.h   header file for nasmlib.c
+ *
+ * The Netwide Assembler is copyright (C) 1996 Simon Tatham and
+ * Julian Hall. All rights reserved. The software is
+ * redistributable under the licence given in the file "Licence"
+ * distributed in the NASM archive.
+ */
+
+#ifndef NASM_NASMLIB_H
+#define NASM_NASMLIB_H
+
+/*
+ * If this is defined, the wrappers around malloc et al will
+ * transform into logging variants, which will cause NASM to create
+ * a file called `malloc.log' when run, and spew details of all its
+ * memory management into that. That can then be analysed to detect
+ * memory leaks and potentially other problems too.
+ */
+/* #define LOGALLOC */
+
+/*
+ * Wrappers around malloc, realloc and free. nasm_malloc will
+ * fatal-error and die rather than return NULL; nasm_realloc will
+ * do likewise, and will also guarantee to work right on being
+ * passed a NULL pointer; nasm_free will do nothing if it is passed
+ * a NULL pointer.
+ */
+#ifdef NASM_NASM_H                    /* need efunc defined for this */
+void nasm_set_malloc_error (efunc);
+#ifndef LOGALLOC
+void *nasm_malloc (size_t);
+void *nasm_realloc (void *, size_t);
+void nasm_free (void *);
+char *nasm_strdup (const char *);
+char *nasm_strndup (char *, size_t);
+#else
+void *nasm_malloc_log (char *, int, size_t);
+void *nasm_realloc_log (char *, int, void *, size_t);
+void nasm_free_log (char *, int, void *);
+char *nasm_strdup_log (char *, int, const char *);
+char *nasm_strndup_log (char *, int, char *, size_t);
+#define nasm_malloc(x) nasm_malloc_log(__FILE__,__LINE__,x)
+#define nasm_realloc(x,y) nasm_realloc_log(__FILE__,__LINE__,x,y)
+#define nasm_free(x) nasm_free_log(__FILE__,__LINE__,x)
+#define nasm_strdup(x) nasm_strdup_log(__FILE__,__LINE__,x)
+#define nasm_strndup(x,y) nasm_strndup_log(__FILE__,__LINE__,x,y)
+#endif
+#endif
+
+/*
+ * ANSI doesn't guarantee the presence of `stricmp' or
+ * `strcasecmp'.
+ */
+#if defined(stricmp) || defined(strcasecmp)
+#if defined(stricmp)
+#define nasm_stricmp stricmp
+#else
+#define nasm_stricmp strcasecmp
+#endif
+#else
+int nasm_stricmp (const char *, const char *);
+#endif
+
+#if defined(strnicmp) || defined(strncasecmp)
+#if defined(strnicmp)
+#define nasm_strnicmp strnicmp
+#else
+#define nasm_strnicmp strncasecmp
+#endif
+#else
+int nasm_strnicmp (const char *, const char *, int);
+#endif
+
+/*
+ * Convert a string into a number, using NASM number rules. Sets
+ * `*error' to TRUE if an error occurs, and FALSE otherwise.
+ */
+long readnum(char *str, int *error);
+
+/*
+ * Convert a character constant into a number. Sets
+ * `*warn' to TRUE if an overflow occurs, and FALSE otherwise.
+ * str points to and length covers the middle of the string,
+ * without the quotes.
+ */
+long readstrnum(char *str, int length, int *warn);
+
+/*
+ * seg_init: Initialise the segment-number allocator.
+ * seg_alloc: allocate a hitherto unused segment number.
+ */
+void seg_init(void);
+long seg_alloc(void);
+
+/*
+ * many output formats will be able to make use of this: a standard
+ * function to add an extension to the name of the input file
+ */
+#ifdef NASM_NASM_H
+void standard_extension (char *inname, char *outname, char *extension,
+                        efunc error);
+#endif
+
+/*
+ * some handy macros that will probably be of use in more than one
+ * output format: convert integers into little-endian byte packed
+ * format in memory
+ */
+
+#define WRITELONG(p,v) \
+  do { \
+    *(p)++ = (v) & 0xFF; \
+    *(p)++ = ((v) >> 8) & 0xFF; \
+    *(p)++ = ((v) >> 16) & 0xFF; \
+    *(p)++ = ((v) >> 24) & 0xFF; \
+  } while (0)
+
+#define WRITESHORT(p,v) \
+  do { \
+    *(p)++ = (v) & 0xFF; \
+    *(p)++ = ((v) >> 8) & 0xFF; \
+  } while (0)
+
+/*
+ * and routines to do the same thing to a file
+ */
+void fwriteshort (int data, FILE *fp);
+void fwritelong (long data, FILE *fp);
+
+/*
+ * Routines to manage a dynamic random access array of longs which
+ * may grow in size to be more than the largest single malloc'able
+ * chunk.
+ */
+
+#define RAA_BLKSIZE 4096              /* this many longs allocated at once */
+#define RAA_LAYERSIZE 1024            /* this many _pointers_ allocated */
+
+typedef struct RAA RAA;
+typedef union RAA_UNION RAA_UNION;
+typedef struct RAA_LEAF RAA_LEAF;
+typedef struct RAA_BRANCH RAA_BRANCH;
+
+struct RAA {
+    /*
+     * Number of layers below this one to get to the real data. 0
+     * means this structure is a leaf, holding RAA_BLKSIZE real
+     * data items; 1 and above mean it's a branch, holding
+     * RAA_LAYERSIZE pointers to the next level branch or leaf
+     * structures.
+     */
+    int layers;
+    /*
+     * Number of real data items spanned by one position in the
+     * `data' array at this level. This number is 1, trivially, for
+     * a leaf (level 0): for a level 1 branch it should be
+     * RAA_BLKSIZE, and for a level 2 branch it's
+     * RAA_LAYERSIZE*RAA_BLKSIZE.
+     */
+    long stepsize;
+    union RAA_UNION {
+       struct RAA_LEAF {
+           long data[RAA_BLKSIZE];
+       } l;
+       struct RAA_BRANCH {
+           struct RAA *data[RAA_LAYERSIZE];
+       } b;
+    } u;
+};
+
+
+struct RAA *raa_init (void);
+void raa_free (struct RAA *);
+long raa_read (struct RAA *, long);
+struct RAA *raa_write (struct RAA *r, long posn, long value);
+
+/*
+ * Routines to manage a dynamic sequential-access array, under the
+ * same restriction on maximum mallocable block. This array may be
+ * written to in two ways: a contiguous chunk can be reserved of a
+ * given size, and a pointer returned, or single-byte data may be
+ * written. The array can also be read back in the same two ways:
+ * as a series of big byte-data blocks or as a list of structures
+ * of a given size.
+ */
+
+struct SAA {
+    /*
+     * members `end' and `elem_len' are only valid in first link in
+     * list; `rptr' and `rpos' are used for reading
+     */
+    struct SAA *next, *end, *rptr;
+    long elem_len, length, posn, start, rpos;
+    char *data;
+};
+
+struct SAA *saa_init (long elem_len);  /* 1 == byte */
+void saa_free (struct SAA *);
+void *saa_wstruct (struct SAA *);      /* return a structure of elem_len */
+void saa_wbytes (struct SAA *, const void *, long);  /* write arbitrary bytes */
+void saa_rewind (struct SAA *);               /* for reading from beginning */
+void *saa_rstruct (struct SAA *);      /* return NULL on EOA */
+void *saa_rbytes (struct SAA *, long *);   /* return 0 on EOA */
+void saa_rnbytes (struct SAA *, void *, long); /* read a given no. of bytes */
+void saa_fread (struct SAA *s, long posn, void *p, long len);   /* fixup */
+void saa_fwrite (struct SAA *s, long posn, void *p, long len);   /* fixup */
+void saa_fpwrite (struct SAA *, FILE *);
+
+#ifdef NASM_NASM_H
+/*
+ * Standard scanner.
+ */
+extern char *stdscan_bufptr;
+void stdscan_reset(void);
+int stdscan (void *private_data, struct tokenval *tv);
+#endif
+
+#ifdef NASM_NASM_H
+/*
+ * Library routines to manipulate expression data types.
+ */
+int is_reloc(expr *);
+int is_simple(expr *);
+int is_really_simple (expr *);
+int is_unknown(expr *);
+int is_just_unknown(expr *);
+long reloc_value(expr *);
+long reloc_seg(expr *);
+long reloc_wrt(expr *);
+#endif
+
+/*
+ * Binary search routine. Returns index into `array' of an entry
+ * matching `string', or <0 if no match. `array' is taken to
+ * contain `size' elements.
+ */
+int bsi (char *string, const char **array, int size);
+
+
+char *src_set_fname(char *newname);
+long src_set_linnum(long newline);
+long src_get_linnum(void);
+/*
+ * src_get may be used if you simply want to know the source file and line.
+ * It is also used if you maintain private status about the source location
+ * It return 0 if the information was the same as the last time you
+ * checked, -1 if the name changed and (new-old) if just the line changed.
+ */
+int src_get(long *xline, char **xname);
+
+void nasm_quote(char **str);
+char *nasm_strcat(char *one, char *two);
+void nasmlib_cleanup(void);
+
+void null_debug_routine(const char *directive, const char *params);
+extern struct dfmt null_debug_form;
+extern struct dfmt *null_debug_arr[2];
+#endif
diff --git a/src/preprocs/nasm/standard.mac b/src/preprocs/nasm/standard.mac
new file mode 100644 (file)
index 0000000..bbbf90d
--- /dev/null
@@ -0,0 +1,110 @@
+; Standard macro set for NASM -*- nasm -*-
+
+; Macros to make NASM ignore some TASM directives before the first include
+; directive.
+
+    %idefine IDEAL
+    %idefine JUMPS
+    %idefine P386
+    %idefine P486
+    %idefine P586
+    %idefine END
+
+; This is a magic token which indicates the end of the TASM macros
+*END*TASM*MACROS*
+
+; Note that although some user-level forms of directives are defined
+; here, not all of them are: the user-level form of a format-specific
+; directive should be defined in the module for that directive.
+
+; These two need to be defined, though the actual definitions will
+; be constantly updated during preprocessing.
+%define __FILE__
+%define __LINE__
+
+%define __SECT__               ; it ought to be defined, even if as nothing
+
+%imacro section 1+.nolist
+%define __SECT__ [section %1]
+         __SECT__
+%endmacro
+%imacro segment 1+.nolist
+%define __SECT__ [segment %1]
+         __SECT__
+%endmacro
+
+%imacro absolute 1+.nolist
+%define __SECT__ [absolute %1]
+         __SECT__
+%endmacro
+
+%imacro struc 1.nolist
+%push struc
+%define %$strucname %1
+[absolute 0]
+%$strucname:                   ; allow definition of `.member' to work sanely
+%endmacro 
+%imacro endstruc 0.nolist
+%{$strucname}_size:
+%pop
+__SECT__
+%endmacro
+
+%imacro istruc 1.nolist
+%push istruc
+%define %$strucname %1
+%$strucstart:
+%endmacro
+%imacro at 1-2+.nolist
+         times %1-($-%$strucstart) db 0
+         %2
+%endmacro
+%imacro iend 0.nolist
+         times %{$strucname}_size-($-%$strucstart) db 0
+%pop
+%endmacro
+
+%imacro align 1-2+.nolist nop
+         times ($$-$) & ((%1)-1) %2
+%endmacro
+%imacro alignb 1-2+.nolist resb 1
+         times ($$-$) & ((%1)-1) %2
+%endmacro
+
+%imacro extern 1-*.nolist
+%rep %0
+[extern %1]
+%rotate 1
+%endrep
+%endmacro
+
+%imacro bits 1+.nolist
+[bits %1]
+%endmacro
+
+%imacro use16 0.nolist
+[bits 16]
+%endmacro
+%imacro use32 0.nolist
+[bits 32]
+%endmacro
+
+%imacro global 1-*.nolist
+%rep %0
+[global %1]
+%rotate 1
+%endrep
+%endmacro
+
+%imacro common 1-*.nolist
+%rep %0
+[common %1]
+%rotate 1
+%endrep
+%endmacro
+
+%imacro cpu 1+.nolist
+[cpu %1]
+%endmacro
+
+