+/* gasp.c - Gnu assembler preprocessor main program.
+ Copyright (C) 1994 Free Software Foundation, Inc.
+
+ Written by Steve and Judy Chamberlain of Cygnus Support,
+ sac@cygnus.com
+
+ This file is part of GASP, the GNU Assembler Preprocessor.
+
+ GASP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GASP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GASP; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+
+This program translates the input macros and stuff into a form
+suitable for gas to consume.
+
+
+ gasp [-c] [-o <outfile>] <infile>*
+
+ -c copy source to output
+
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern char *malloc ();
+
+#define MAX_INCLUDES 30 /* Maximum include depth */
+#define MAX_REASONABLE 1000 /* Maximum number of expansions */
+
+int unreasonable; /* -u on command line */
+int stats; /* -s on command line */
+int print_line_number; /* -p flag on command line */
+int copysource; /* -c flag on command line */
+int warnings; /* Number of WARNINGs generated so far. */
+int errors; /* Number of ERRORs generated so far. */
+int fatals; /* Number of fatal ERRORs generated so far (either 0 or 1). */
+
+
+
+int radix = 10; /* Default radix */
+
+int had_end; /* Seen .END */
+
+/* The output stream */
+FILE *outfile;
+
+
+/* Forward declarations. */
+static int condass_lookup_name();
+static int condass_on();
+static int get();
+static int get_and_process();
+static int get_token();
+static int getstring();
+static int include_next_index();
+static int macro_op();
+static int linecount();
+static int process_pseudo_op();
+static void include_pop();
+static void include_print_where_line();
+/* string blocks
+
+ I had a couple of choices when deciding upon this data structure.
+ gas uses null terminated strings for all its internal work. This
+ often means that parts of the program that want to examine
+ substrings have to manipulate the data in the string to do the
+ right thing (a common operation is to single out a bit of text by
+ saving away the character after it, nulling it out, operating on
+ the substring and then replacing the character which was under the
+ null). This is a pain and I remember a load of problems that I had with
+ code in gas which almost got this right. Also, it's harder to grow and
+ allocate null terminated strings efficiently.
+
+ Obstacks provide all the functionality needed, but are too
+ complicated, hence the sb.
+
+ An sb is allocated by the caller, and is initialzed to point to an
+ sb_element. sb_elements are kept on a free lists, and used when
+ needed, replaced onto the free list when unused.
+ */
+
+#define max_power_two 30 /* don't allow strings more than
+ 2^max_power_two long */
+/* structure of an sb */
+typedef struct sb
+ {
+ char *ptr; /* points to the current block. */
+ int len; /* how much is used. */
+ int pot; /* the maximum length is 1<<pot */
+ struct le *item;
+ }
+sb;
+
+/* Structure of the free list object of an sb */
+typedef struct le
+ {
+ struct le *next;
+ int size;
+ char data[1];
+ }
+sb_element;
+
+/* The free list */
+typedef struct
+ {
+ sb_element *size[max_power_two];
+ } sb_list_vector;
+
+sb_list_vector free_list;
+
+int string_count[max_power_two];
+
+/* the attributes of each character are stored as a bit pattern
+ chartype, which gives us quick tests. */
+
+
+#define FIRSTBIT 1
+#define NEXTBIT 2
+#define SEPBIT 4
+#define WHITEBIT 8
+#define ISFIRSTCHAR(x) (chartype[(unsigned)(x)] & FIRSTBIT)
+#define ISNEXTCHAR(x) (chartype[(unsigned)(x)] & NEXTBIT)
+#define ISSEP(x) (chartype[(unsigned)(x)] & SEPBIT)
+#define ISWHITE(x) (chartype[(unsigned)(x)] & WHITEBIT)
+
+static char chartype[256];
+
+
+/* Conditional assembly uses the `ifstack'. Each aif pushes another
+ entry onto the stack, and sets the on flag if it should. The aelse
+ sets hadelse, and toggles on. An aend pops a level. We limit to
+ 100 levels of nesting, not because we're facists pigs with read
+ only minds, but because more than 100 levels of nesting is probably
+ a bug in the user's macro structure. */
+
+#define IFNESTING 100
+struct
+ {
+ int on; /* is the level being output */
+ int hadelse; /* has an aelse been seen */
+ }
+ifstack[IFNESTING];
+int ifi;
+
+/* The final and intermediate results of expression evaluation are kept in
+ exp_t's. Note that a symbol is not an sb, but a pointer into the input
+ line. It must be coped somewhere safe before the next line is read in. */
+
+typedef struct
+ {
+ char *name;
+ int len;
+ }
+symbol;
+
+typedef struct
+ {
+ int value; /* constant part */
+ symbol add_symbol; /* name part */
+ symbol sub_symbol; /* name part */
+ }
+exp_t;
+
+
+/* Hashing is done in a pretty standard way. A hash_table has a
+ pointer to a vector of pointers to hash_entrys, and the size of the
+ vector. A hash_entry contains a union of all the info we like to
+ store in hash table. If there is a hash collision, hash_entries
+ with the same hash are kept in a chain. */
+
+/* What the data in a hash_entry means */
+typedef enum
+ {
+ hash_integer, /* name->integer mapping */
+ hash_string, /* name->string mapping */
+ hash_macro, /* name is a macro */
+ hash_formal /* name is a formal argument */
+ } hash_type;
+
+typedef struct hs
+ {
+ sb key; /* symbol name */
+ hash_type type; /* symbol meaning */
+ union
+ {
+ sb s;
+ int i;
+ struct macro_struct *m;
+ struct formal_struct *f;
+ } value;
+ struct hs *next; /* next hash_entry with same hash key */
+ } hash_entry;
+
+typedef struct
+ {
+ hash_entry **table;
+ int size;
+ } hash_table;
+
+
+/* Structures used to store macros.
+
+ Each macro knows its name and included text. It gets built with a
+ list of formal arguments, and also keeps a hash table which points
+ into the list to speed up formal search. Each formal knows its
+ name and its default value. Each time the macro is expanded, the
+ formals get the actual values attatched to them. */
+
+/* describe the formal arguments to a macro */
+
+typedef struct formal_struct
+ {
+ struct formal_struct *next; /* next formal in list */
+ sb name; /* name of the formal */
+ sb def; /* the default value */
+ sb actual; /* the actual argument (changed on each expansion) */
+ int index; /* the index of the formal 0..formal_count-1 */
+ }
+formal_entry;
+
+/* describe the macro. */
+
+typedef struct macro_struct
+ {
+ sb sub; /* substitution text. */
+ int formal_count; /* number of formal args. */
+ formal_entry *formals; /* pointer to list of formal_structs */
+ hash_table formal_hash; /* hash table of formals. */
+ }
+macro_entry;
+
+/* how we nest files and expand macros etc.
+
+ we keep a stack of of include_stack structs. each include file
+ pushes a new level onto the stack. we keep an sb with a pushback
+ too. unget chars are pushed onto the pushback sb, getchars first
+ checks the pushback sb before reading from the input stream.
+
+ small things are expanded by adding the text of the item onto the
+ pushback sb. larger items are grown by pushing a new level and
+ allocating the entire pushback buf for the item. each time
+ something like a macro is expanded, the stack index is changed. we
+ can then perform an exitm by popping all entries off the stack with
+ the same stack index. if we're being reasonable, we can detect
+ recusive expansion by checking the index is reasonably small.
+ */
+
+typedef enum
+ {
+ include_file, include_repeat, include_while, include_macro
+ } include_type;
+
+struct include_stack
+ {
+ sb pushback; /* current pushback stream */
+ int pushback_index; /* next char to read from stream */
+ FILE *handle; /* open file */
+ sb name; /* name of file */
+ int linecount; /* number of lines read so far */
+ include_type type;
+ int index; /* index of this layer */
+ }
+include_stack[MAX_INCLUDES];
+
+struct include_stack *sp;
+#define isp (sp - include_stack)
+
+#define dsize 5
+
+
+void include_print_where_line ();
+
+
+#define FATAL(x) \
+ do { include_print_where_line (stderr); fprintf x ; fatals++; quit(); } while(0)
+#define ERROR(x) \
+ do { include_print_where_line (stderr); fprintf x; errors++; } while(0)
+#define WARNING(x) \
+ do { include_print_where_line (stderr); fprintf x; warnings++;} while(0)
+
+
+
+/* exit the program and return the right ERROR code. */
+void
+quit ()
+{
+ int exitcode;
+ if (fatals + errors)
+ exitcode = 1;
+ else
+ exitcode = 0;
+
+ if (stats)
+ {
+ int i;
+ for (i = 0; i < max_power_two; i++)
+ {
+ fprintf (stderr, "strings size %8d : %d\n", 1<<i, string_count[i]);
+ }
+ }
+ exit (exitcode);
+}
+
+static
+char *
+xmalloc (x)
+ int x;
+{
+ char *p = malloc (x);
+ if (!p)
+ FATAL ((stderr, "out of memory\n"));
+ memset (p, 0, x);
+ return p;
+}
+
+/* this program is about manipulating strings.
+ they are managed in things called `sb's which is an abbreviation
+ for string buffers. an sb has to be created, things can be glued
+ on to it, and at the end of it's life it should be freed. the
+ contents should never be pointed at whilst it is still growing,
+ since it could be moved at any time
+
+ eg:
+ sb_new (&foo);
+ sb_grow... (&foo,...);
+ use foo->ptr[*];
+ sb_kill (&foo);
+
+*/
+
+/* initializes an sb. */
+
+void
+sb_build (ptr, size)
+ sb *ptr;
+ int size;
+{
+ /* see if we can find one to allocate */
+ sb_element *e;
+
+ if (size > max_power_two)
+ {
+ FATAL ((stderr, "string longer than %d bytes requested.\n",
+ 1 << max_power_two));
+ }
+ e = free_list.size[size];
+ if (!e)
+ {
+ /* nothing there, allocate one and stick into the free list */
+ e = (sb_element *) xmalloc (sizeof (sb_element) + (1 << size));
+ e->next = free_list.size[size];
+ e->size = 1 << size;
+ free_list.size[size] = e;
+ string_count[size]++;
+ }
+
+ /* remove from free list */
+
+ free_list.size[size] = e->next;
+
+ /* copy into callers world */
+ ptr->ptr = e->data;
+ ptr->pot = size;
+ ptr->len = 0;
+ ptr->item = e;
+}
+
+
+static void
+sb_new (ptr)
+ sb *ptr;
+{
+ sb_build (ptr, dsize);
+}
+
+/* deallocate the sb at ptr */
+
+static
+void
+sb_kill (ptr)
+ sb *ptr;
+{
+ /* return item to free list */
+ ptr->item->next = free_list.size[ptr->pot];
+ free_list.size[ptr->pot] = ptr->item;
+}
+
+/* add the sb at s to the end of the sb at ptr */
+
+static void sb_check ();
+
+static
+void
+sb_add_sb (ptr, s)
+ sb *ptr;
+ sb *s;
+{
+ sb_check (ptr, s->len);
+ memcpy (ptr->ptr + ptr->len, s->ptr, s->len);
+ ptr->len += s->len;
+}
+
+/* make sure that the sb at ptr has room for another len characters,
+ and grow it if it doesn't. */
+
+static void
+sb_check (ptr, len)
+ sb *ptr;
+ int len;
+{
+ if (ptr->len + len >= 1 << ptr->pot)
+ {
+ sb tmp;
+ int pot = ptr->pot;
+ while (ptr->len + len >= 1 << pot)
+ pot++;
+ sb_build (&tmp, pot);
+ sb_add_sb (&tmp, ptr);
+ sb_kill (ptr);
+ *ptr = tmp;
+ }
+}
+
+/* make the sb at ptr point back to the beginning. */
+
+static void
+sb_reset (ptr)
+ sb *ptr;
+{
+ ptr->len = 0;
+}
+
+/* add character c to the end of the sb at ptr. */
+
+void
+sb_add_char (ptr, c)
+ sb *ptr;
+ char c;
+{
+ sb_check (ptr, 1);
+ ptr->ptr[ptr->len++] = c;
+}
+
+/* add null terminated string s to the end of sb at ptr. */
+
+static void
+sb_add_string (ptr, s)
+ sb *ptr;
+ char *s;
+{
+ int len = strlen (s);
+ sb_check (ptr, len);
+ memcpy (ptr->ptr + ptr->len, s, len);
+ ptr->len += len;
+}
+
+/* add string at s of length len to sb at ptr */
+
+static void
+sb_add_buffer (ptr, s, len)
+ sb *ptr;
+ char *s;
+ int len;
+{
+ sb_check (ptr, len);
+ memcpy (ptr->ptr + ptr->len, s, len);
+ ptr->len += len;
+}
+
+
+/* print the sb at ptr to the output file */
+
+static
+void
+sb_print (ptr)
+ sb *ptr;
+{
+ int i;
+ int nc = 0;
+
+ for (i = 0; i < ptr->len; i++)
+ {
+ if (nc)
+ {
+ fprintf (outfile, ",");
+ }
+ fprintf (outfile, "%d", ptr->ptr[i]);
+ nc = 1;
+ }
+}
+
+/* put a null at the end of the sb at in and return the start of the
+ string, so that it can be used as an arg to printf %s. */
+
+static
+char *
+sb_name (in)
+ sb *in;
+{
+ /* stick a null on the end of the string */
+ sb_add_char (in, 0);
+ return in->ptr;
+}
+
+/* start at the index idx into the string in sb at ptr and skip
+ whitespace. return the index of the first non whitespace character */
+
+static int
+sb_skip_white (idx, ptr)
+ int idx;
+ sb *ptr;
+{
+ while (idx < ptr->len && ISWHITE (ptr->ptr[idx]))
+ idx++;
+ return idx;
+}
+
+/* start at the index idx into the sb at ptr. skips whitespace,
+ a comma and any following whitespace. returnes the index of the
+ next character. */
+
+static int
+sb_skip_comma (idx, ptr)
+ int idx;
+ sb *ptr;
+{
+ while (idx < ptr->len && ISWHITE (ptr->ptr[idx]))
+ idx++;
+
+ if (idx < ptr->len
+ && ptr->ptr[idx] == ',')
+ idx++;
+
+ while (idx < ptr->len && ISWHITE (ptr->ptr[idx]))
+ idx++;
+
+ return idx;
+}
+
+
+/* hash table maintenance. */
+
+/* build a new hash table with size buckets, and fill in the info at ptr. */
+
+static void
+hash_new_table (size, ptr)
+ int size;
+ hash_table *ptr;
+{
+ ptr->size = size;
+ ptr->table = (hash_entry **) xmalloc (size * (sizeof (hash_entry *)));
+}
+
+/* calculate and return the hash value of the sb at key. */
+
+static int
+hash (key)
+ sb *key;
+{
+ int k = 0x1234;
+ int i;
+ char *p = key->ptr;
+ for (i = 0; i < key->len; i++)
+ {
+ k ^= (k << 2) ^ *p;
+ p++;
+ }
+ return k & 0xf0fff;
+}
+
+/* lookup key in hash_table tab, if present, then return it, otherwise
+ build a new one and fill it with hash_integer. */
+
+static
+hash_entry *
+hash_create (tab, key)
+ hash_table *tab;
+ sb *key;
+{
+ int k = hash (key) % tab->size;
+ hash_entry *p;
+ hash_entry **table = tab->table;
+
+ p = table[k];
+
+ while (1)
+ {
+ if (!p)
+ {
+ hash_entry *n = (hash_entry *) xmalloc (sizeof (hash_entry));
+ n->next = table[k];
+ sb_new (&n->key);
+ sb_add_sb (&n->key, key);
+ table[k] = n;
+ n->type = hash_integer;
+ return n;
+ }
+ if (strncmp (table[k]->key.ptr, key->ptr, key->len) == 0)
+ {
+ return p;
+ }
+ p = p->next;
+ }
+}
+
+/* add sb name with key into hash_table tab. if replacing old value
+ and again, then ERROR. */
+
+static
+void
+hash_add_to_string_table (tab, key, name, again)
+ hash_table *tab;
+ sb *key;
+ sb *name;
+ int again;
+{
+ hash_entry *ptr = hash_create (tab, key);
+ if (ptr->type == hash_integer)
+ {
+ sb_new (&ptr->value.s);
+ }
+ if (ptr->value.s.len)
+ {
+ if (!again)
+ ERROR ((stderr, "redefintion not allowed"));
+ }
+ sb_reset (&ptr->value.s);
+ sb_add_sb (&ptr->value.s, name);
+}
+
+/* add integer name to hash_table tab with sb key. */
+
+static
+void
+hash_add_to_int_table (tab, key, name)
+ hash_table *tab;
+ sb *key;
+ int name;
+{
+ hash_entry *ptr = hash_create (tab, key);
+ ptr->value.i = name;
+}
+
+/* lookup sb key in hash_table tab. if found return hash_entry result,
+ else 0. */
+
+static
+hash_entry *
+hash_lookup (tab, key)
+ hash_table *tab;
+ sb *key;
+{
+ int k = hash (key) % tab->size;
+ hash_entry **table = tab->table;
+ hash_entry *p = table[k];
+ while (p)
+ {
+ if (p->key.len == key->len
+ && strncmp (p->key.ptr, key->ptr, key->len) == 0)
+ return p;
+ p = p->next;
+ }
+ return 0;
+}
+
+
+/* expressions
+
+ are handled in a really simple recursive decent way. each bit of
+ the machine takes an index into an sb and a pointer to an exp_t,
+ modifies the *exp_t and returns the index of the first character
+ past the part of the expression parsed.
+
+ expression precedence:
+ ( )
+ unary + - ~
+* /
++ -
+&
+| ~
+
+*/
+
+
+/* make sure that the exp_t at term is constant, if not the give the op ERROR. */
+
+static
+void
+checkconst (op, term)
+ char op;
+ exp_t *term;
+{
+ if (term->add_symbol.len
+ || term->sub_symbol.len)
+ {
+ ERROR ((stderr, "the %c operator cannot take non-absolute arguments.\n", op));
+ }
+}
+
+/* turn the number in string at idx into a number of base,
+ fill in ptr and return the index of the first character not in the
+ number. */
+
+static
+int
+sb_strtol (idx, string, base, ptr)
+ int idx;
+ sb *string;
+ int base;
+ int *ptr;
+{
+ int value = 0;
+ idx = sb_skip_white (idx, string);
+
+ while (idx < string->len)
+ {
+ int ch = string->ptr[idx];
+ int dig = 0;
+ if (isdigit (ch))
+ dig = ch - '0';
+ else if (ch >= 'a' && ch <= 'f')
+ dig = ch - 'a' + 10;
+ else if (ch >= 'a' && ch <= 'f')
+ dig = ch - 'a' + 10;
+ else
+ break;
+
+ if (dig >= base)
+ break;
+
+ value = value * base + dig;
+ idx++;
+ }
+ *ptr = value;
+ return idx;
+}
+
+static int level_5 ();
+
+int
+level_0 (idx, string, lhs)
+ int idx;
+ sb *string;
+ exp_t *lhs;
+{
+ lhs->add_symbol.len = 0;
+ lhs->add_symbol.name = 0;
+
+ lhs->sub_symbol.len = 0;
+ lhs->sub_symbol.name = 0;
+
+ idx = sb_skip_white (idx, string);
+
+ lhs->value = 0;
+
+ if (isdigit (string->ptr[idx]))
+ {
+ idx = sb_strtol (idx, string, 10, &lhs->value);
+ }
+ else if (ISFIRSTCHAR (string->ptr[idx]))
+ {
+ int len = 0;
+ lhs->add_symbol.name = string->ptr + idx;
+ while (idx < string->len && ISNEXTCHAR (string->ptr[idx]))
+ {
+ idx++;
+ len++;
+ }
+ lhs->add_symbol.len = len;
+ }
+ else if (string->ptr[idx] == '"')
+ {
+ sb acc;
+ sb_new (&acc);
+ ERROR ((stderr, "string where expression expected.\n"));
+ idx = getstring (idx, string, &acc);
+ sb_kill (&acc);
+ }
+ else
+ {
+ ERROR ((stderr, "can't find primary in expression.\n"));
+ idx++;
+ }
+ return sb_skip_white (idx, string);
+}
+
+
+
+static int
+level_1 (idx, string, lhs)
+ int idx;
+ sb *string;
+ exp_t *lhs;
+{
+ idx = sb_skip_white (idx, string);
+
+ switch (string->ptr[idx])
+ {
+ case '+':
+ idx = level_1 (idx + 1, string, lhs);
+ break;
+ case '~':
+ idx = level_1 (idx + 1, string, lhs);
+ checkconst ('~', lhs);
+ lhs->value = ~lhs->value;
+ break;
+ case '-':
+ {
+ symbol t;
+ idx = level_1 (idx + 1, string, lhs);
+ lhs->value = -lhs->value;
+ t = lhs->add_symbol;
+ lhs->add_symbol = lhs->sub_symbol;
+ lhs->sub_symbol = t;
+ break;
+ }
+ case '(':
+ idx++;
+ idx = level_5 (sb_skip_white (idx, string), string, lhs);
+ if (string->ptr[idx] != ')')
+ ERROR ((stderr, "misplaced closing parens.\n"));
+ else
+ idx++;
+ break;
+ default:
+ idx = level_0 (idx, string, lhs);
+ break;
+ }
+ return sb_skip_white (idx, string);
+}
+
+static int
+level_2 (idx, string, lhs)
+ int idx;
+ sb *string;
+ exp_t *lhs;
+{
+ exp_t rhs;
+
+ idx = level_1 (idx, string, lhs);
+
+ while (idx < string->len && (string->ptr[idx] == '*'
+ || string->ptr[idx] == '/'))
+ {
+ char op = string->ptr[idx++];
+ idx = level_1 (idx, string, &rhs);
+ switch (op)
+ {
+ case '*':
+ checkconst ('*', lhs);
+ checkconst ('*', &rhs);
+ lhs->value *= rhs.value;
+ break;
+ case '/':
+ checkconst ('/', lhs);
+ checkconst ('/', &rhs);
+ if (rhs.value == 0)
+ ERROR ((stderr, "attempt to divide by zero.\n"));
+ else
+ lhs->value /= rhs.value;
+ break;
+ }
+ }
+ return sb_skip_white (idx, string);
+}
+
+
+static int
+level_3 (idx, string, lhs)
+ int idx;
+ sb *string;
+ exp_t *lhs;
+{
+ exp_t rhs;
+
+ idx = level_2 (idx, string, lhs);
+
+ while (idx < string->len
+ && (string->ptr[idx] == '+'
+ || string->ptr[idx] == '-'))
+ {
+ char op = string->ptr[idx++];
+ idx = level_2 (idx, string, &rhs);
+ switch (op)
+ {
+ case '+':
+ lhs->value += rhs.value;
+ if (lhs->add_symbol.name && rhs.add_symbol.name)
+ {
+ ERROR ((stderr, "can't add two relocatable expressions\n"));
+ }
+ /* change nn+symbol to symbol + nn */
+ if (rhs.add_symbol.name)
+ {
+ lhs->add_symbol = rhs.add_symbol;
+ }
+ break;
+ case '-':
+ lhs->value -= rhs.value;
+ lhs->sub_symbol = rhs.add_symbol;
+ break;
+ }
+ }
+ return sb_skip_white (idx, string);
+}
+
+static int
+level_4 (idx, string, lhs)
+ int idx;
+ sb *string;
+ exp_t *lhs;
+{
+ exp_t rhs;
+
+ idx = level_3 (idx, string, lhs);
+
+ while (idx < string->len &&
+ string->ptr[idx] == '&')
+ {
+ char op = string->ptr[idx++];
+ idx = level_3 (idx, string, &rhs);
+ switch (op)
+ {
+ case '&':
+ checkconst ('&', lhs);
+ checkconst ('&', &rhs);
+ lhs->value &= rhs.value;
+ break;
+ }
+ }
+ return sb_skip_white (idx, string);
+}
+
+static int
+level_5 (idx, string, lhs)
+ int idx;
+ sb *string;
+ exp_t *lhs;
+{
+ exp_t rhs;
+
+ idx = level_4 (idx, string, lhs);
+
+ while (idx < string->len
+ && (string->ptr[idx] == '|' || string->ptr[idx] == '~'))
+ {
+ char op = string->ptr[idx++];
+ idx = level_4 (idx, string, &rhs);
+ switch (op)
+ {
+ case '|':
+ checkconst ('|', lhs);
+ checkconst ('|', &rhs);
+ lhs->value |= rhs.value;
+ break;
+ case '~':
+ checkconst ('~', lhs);
+ checkconst ('~', &rhs);
+ lhs->value ^= rhs.value;
+ break;
+ }
+ }
+ return sb_skip_white (idx, string);
+}
+
+
+/* parse the expression at offset idx into string, fill up res with
+ the result. return the index of the first char past the expression.
+ */
+
+static int
+exp_parse (idx, string, res)
+ int idx;
+ sb *string;
+ exp_t *res;
+{
+ return level_5 (sb_skip_white (idx, string), string, res);
+}
+
+
+/* turn the expression at exp into text and glue it onto the end of
+ string. */
+
+static void
+exp_string (exp, string)
+ exp_t *exp;
+ sb *string;
+{
+ int np = 0;
+ int ad = 0;
+ sb_reset (string);
+
+ if (exp->add_symbol.len)
+ {
+ sb_add_buffer (string, exp->add_symbol.name, exp->add_symbol.len);
+ np = 1;
+ ad = 1;
+ }
+ if (exp->value)
+ {
+ char buf[20];
+ if (np)
+ sb_add_char (string, '+');
+ sprintf (buf, "%d", exp->value);
+ sb_add_string (string, buf);
+ np = 1;
+ ad = 1;
+ }
+ if (exp->sub_symbol.len)
+ {
+ sb_add_char (string, '-');
+ sb_add_buffer (string, exp->add_symbol.name, exp->add_symbol.len);
+ np = 0;
+ ad = 1;
+ }
+
+ if (!ad)
+ sb_add_char (string, '0');
+}
+
+
+/* parse the expression at offset idx into sb in, return the value in val.
+ if the expression is not constant, give ERROR emsg. returns the index
+ of the first character past the end of the expression. */
+
+static int
+exp_get_abs (emsg, idx, in, val)
+ char *emsg;
+ int idx;
+ sb *in;
+ int *val;
+{
+ exp_t res;
+ idx = exp_parse (idx, in, &res);
+ if (res.add_symbol.len || res.sub_symbol.len)
+ ERROR ((stderr, emsg));
+ *val = res.value;
+ return idx;
+}
+
+
+sb label; /* current label parsed from line */
+hash_table assign_hash_table; /* hash table for all assigned variables */
+hash_table keyword_hash_table; /* hash table for keyword */
+hash_table vars; /* hash table for eq variables */
+
+#define in_comment ';'
+
+#if 0
+void
+strip_comments (out)
+ sb *out;
+{
+ char *s = out->ptr;
+ int i = 0;
+ for (i = 0; i < out->len; i++)
+ {
+ if (s[i] == in_comment)
+ {
+ out->len = i;
+ return;
+ }
+ }
+}
+#endif
+
+/* push back character ch so that it can be read again. */
+
+void
+unget (ch)
+ int ch;
+{
+ if (ch == '\n')
+ {
+ sp->linecount--;
+ }
+ if (sp->pushback_index)
+ sp->pushback_index--;
+ else
+ sb_add_char (&sp->pushback, ch);
+}
+
+/* push the sb ptr onto the include stack, with the given name, type and index. */
+
+static
+void
+include_buf (name, ptr, type, index)
+ sb *name;
+ sb *ptr;
+ include_type type;
+ int index;
+{
+ sp++;
+ if (sp - include_stack >= MAX_INCLUDES)
+ FATAL ((stderr, "unreasonable nesting.\n"));
+ sb_new (&sp->name);
+ sb_add_sb (&sp->name, name);
+ sp->handle = 0;
+ sp->linecount = 1;
+ sp->pushback_index = 0;
+ sp->type = type;
+ sp->index = index;
+ sb_new (&sp->pushback);
+ sb_add_sb (&sp->pushback, ptr);
+}
+
+
+/* used in ERROR messages, print info on where the include stack is onto file. */
+static
+void
+include_print_where_line (file)
+ FILE *file;
+{
+ struct include_stack *p = include_stack + 1;
+
+ while (p <= sp)
+ {
+ fprintf (file, "%s:%d ", sb_name (&p->name), p->linecount - ((p == sp) ? 1 : 0));
+ p++;
+ }
+}
+
+/* used in listings, print the line number onto file. */
+static void
+include_print_line (file)
+ FILE *file;
+{
+ int n;
+ struct include_stack *p = include_stack + 1;
+
+ n = fprintf (file, "%4d", p->linecount);
+ p++;
+ while (p <= sp)
+ {
+ n += fprintf (file, ".%d", p->linecount);
+ p++;
+ }
+ while (n < 8 * 3)
+ {
+ fprintf (file, " ");
+ n++;
+ }
+}
+
+
+/* read a line from the top of the include stack into sb in. */
+
+static int
+get_line (in)
+ sb *in;
+{
+ int online = 0;
+ int more = 1;
+
+ if (copysource)
+ {
+ putc ('!', outfile);
+ if (print_line_number)
+ include_print_line (outfile);
+ }
+
+ while (1)
+ {
+ int ch = get ();
+
+ while (ch == '\r')
+ ch = get ();
+
+ if (ch == EOF)
+ {
+ if (online)
+ {
+ WARNING ((stderr, "end of file not at start of line.\n"));
+ if (copysource)
+ putc ('\n', outfile);
+ }
+ more = 0;
+ break;
+ }
+
+ if (copysource)
+ {
+ putc (ch, outfile);
+ }
+
+ if (ch == '\n')
+ {
+ ch = get ();
+ online = 0;
+ if (ch == '+')
+ {
+ /* continued line */
+ if (copysource)
+ {
+ putc ('!', outfile);
+ putc ('+', outfile);
+ }
+ ch = get ();
+ }
+ else
+ {
+ if (ch != EOF)
+ unget (ch);
+ break;
+ }
+ }
+ else
+ {
+ sb_add_char (in, ch);
+ }
+ online++;
+ }
+
+ return more;
+}
+
+/* find a label from sb in and put it in out. */
+
+static int
+grab_label (in, out)
+ sb *in;
+ sb *out;
+{
+ int i = 0;
+ sb_reset (out);
+ if (ISFIRSTCHAR (in->ptr[i]))
+ {
+ sb_add_char (out, in->ptr[i]);
+ i++;
+ while (ISNEXTCHAR (in->ptr[i]) && i < in->len)
+ {
+ sb_add_char (out, in->ptr[i]);
+ i++;
+ }
+ }
+ return i;
+}
+
+/* find all strange base stuff and turn into decimal. also
+ find all the other numbers and convert them from the default radix */
+
+static void
+change_base (idx, in, out)
+ int idx;
+ sb *in;
+ sb *out;
+{
+ char buffer[20];
+
+ while (idx < in->len)
+ {
+ if (idx < in->len - 1 && in->ptr[idx + 1] == '\'')
+ {
+ int base;
+ int value;
+ switch (in->ptr[idx])
+ {
+ case 'b':
+ case 'B':
+ base = 2;
+ break;
+ case 'q':
+ case 'Q':
+ base = 8;
+ break;
+ case 'h':
+ case 'H':
+ base = 16;
+ break;
+ case 'd':
+ case 'D':
+ base = 10;
+ break;
+ default:
+ ERROR ((stderr, "Illegal base character %c.\n", in->ptr[idx]));
+ base = 10;
+ break;
+ }
+
+ idx = sb_strtol (idx + 2, in, base, &value);
+ sprintf (buffer, "%d", value);
+ sb_add_string (out, buffer);
+ }
+ else if (ISFIRSTCHAR (in->ptr[idx]))
+ {
+ /* copy entire names through quickly */
+ sb_add_char (out, in->ptr[idx]);
+ idx++;
+ while (idx < in->len && ISNEXTCHAR (in->ptr[idx]))
+ {
+ sb_add_char (out, in->ptr[idx]);
+ idx++;
+ }
+ }
+ else if (isdigit (in->ptr[idx]))
+ {
+ int value;
+ /* all numbers must start with a digit, let's chew it and
+ spit out decimal */
+ idx = sb_strtol (idx, in, radix, &value);
+ sprintf (buffer, "%d", value);
+ sb_add_string (out, buffer);
+
+ /* skip all undigsested letters */
+ while (idx < in->len && ISNEXTCHAR (in->ptr[idx]))
+ {
+ sb_add_char (out, in->ptr[idx]);
+ idx++;
+ }
+ }
+ else
+ {
+ /* nothing special, just pass it through */
+ sb_add_char (out, in->ptr[idx]);
+ idx++;
+ }
+ }
+
+}
+
+/* .end */
+static void
+do_end ()
+{
+ had_end = 1;
+}
+
+/* .assign */
+
+static void
+do_assign (again, idx, in)
+ int again;
+ int idx;
+ sb *in;
+{
+ /* stick label in symbol table with following value */
+ exp_t e;
+ sb acc;
+
+ sb_new (&acc);
+ idx = exp_parse (idx, in, &e);
+ exp_string (&e, &acc);
+ hash_add_to_string_table (&assign_hash_table, &label, &acc, again);
+ sb_kill (&acc);
+}
+
+
+/* .radix [b|q|d|h] */
+
+static
+void
+do_radix (ptr)
+ sb *ptr;
+{
+ int idx = sb_skip_white (0, ptr);
+ switch (ptr->ptr[idx])
+ {
+ case 'B':
+ case 'b':
+ radix = 2;
+ break;
+ case 'q':
+ case 'Q':
+ radix = 8;
+ break;
+ case 'd':
+ case 'D':
+ radix = 10;
+ break;
+ case 'h':
+ case 'H':
+ radix = 16;
+ break;
+ default:
+ ERROR ((stderr, "radix is %c must be one of b, q, d or h", radix));
+ }
+}
+
+
+/* Parse off a .b, .w or .l */
+
+static int
+get_opsize (idx, in, size)
+int idx;
+sb *in;
+int *size;
+{
+ *size = 4;
+ if (in->ptr[idx] == '.')
+ {
+ idx++;
+ switch (in->ptr[idx])
+ {
+ case 'b':
+ case 'B':
+ *size = 1;
+ break;
+ case 'w':
+ case 'W':
+ *size = 2;
+ break;
+ case 'l':
+ case 'L':
+ *size = 4;
+ break;
+ default:
+ ERROR ((stderr, "size must be one of b, w or l, is %c.\n", in->ptr[idx]));
+ break;
+ }
+ idx++;
+ }
+ return idx;
+}
+
+/* .data [.b|.w|.l] <data>* */
+
+static void
+do_data (idx, in)
+ int idx;
+ sb *in;
+{
+ int opsize = 4;
+ char *opname;
+ sb acc;
+ sb_new (&acc);
+
+ idx = get_opsize (idx, in, &opsize);
+
+ switch (opsize)
+ {
+ case 4:
+ opname = ".long";
+ break;
+ case 2:
+ opname = ".short";
+ break;
+ case 1:
+ opname = ".byte";
+ break;
+ }
+
+ fprintf (outfile, "%s\t", opname);
+ while (idx < in->len)
+ {
+ exp_t e;
+ idx = exp_parse (idx, in, &e);
+ exp_string (&e, &acc);
+ sb_add_char (&acc, 0);
+ fprintf (outfile, acc.ptr);
+ if (idx < in->len && in->ptr[idx] == ',')
+ {
+ fprintf (outfile, ",");
+ idx++;
+ }
+ }
+ sb_kill (&acc);
+ fprintf (outfile, "\n");
+}
+
+/* .datab [.b|.w|.l] <repeat>,<fill> */
+
+static void
+do_datab (idx, in)
+ int idx;
+ sb *in;
+{
+ int opsize;
+ int repeat;
+ int fill;
+
+ idx = get_opsize (idx, in, &opsize);
+
+ idx = exp_get_abs ("datab repeat must be constant.\n", idx, in, &repeat);
+ idx = sb_skip_comma (idx, in);
+ idx = exp_get_abs ("datab data must be absolute.\n", idx, in, &fill);
+
+ fprintf (outfile, ".fill\t%d,%d,%d\n", repeat, opsize, fill);
+}
+
+/* .align <size> */
+
+void
+do_align (idx, in)
+ int idx;
+ sb *in;
+{
+ int al;
+ idx = exp_get_abs ("align needs absolute expression.\n", idx, in, &al);
+
+ if (al != 1
+ && al != 2
+ && al != 4)
+ WARNING ((stderr, "alignment must be one of 1, 2 or 4.\n"));
+
+ fprintf (outfile, ".align %d\n", al);
+}
+
+/* .res[.b|.w|.l] <size> */
+
+void
+do_res (idx, in, type)
+ int idx;
+ sb *in;
+ char type;
+{
+ int size = 4;
+ int count = 0;
+
+ idx = get_opsize (idx, in, &size);
+ while (idx < in->len)
+ {
+ idx = sb_skip_white (idx, in);
+ if (in->ptr[idx] == ',')
+ idx++;
+ idx = exp_get_abs ("res needs absolute expression for fill count.\n", idx, in, &count);
+
+ if (type == 'c' || type == 'z')
+ count++;
+
+ fprintf (outfile, ".space %d\n", count * size);
+ }
+}
+
+
+/* .export */
+
+void
+do_export (in)
+ sb *in;
+{
+ fprintf (outfile, ".global %s\n", sb_name (in));
+}
+
+/* .print [list] [nolist] */
+
+void
+do_print (idx, in)
+ int idx;
+ sb *in;
+{
+ idx = sb_skip_white (idx, in);
+ while (idx < in->len)
+ {
+ if (strncmp (in->ptr + idx, "LIST", 4) == 0)
+ {
+ fprintf (outfile, ".list\n");
+ idx += 4;
+ }
+ else if (strncmp (in->ptr + idx, "NOLIST", 6) == 0)
+ {
+ fprintf (outfile, ".nolist\n");
+ idx += 6;
+ }
+ idx++;
+ }
+}
+
+/* .head */
+void
+do_heading (idx, in)
+ int idx;
+ sb *in;
+{
+ sb head;
+ sb_new (&head);
+ idx = getstring (idx, in, &head);
+ fprintf (outfile, ".title \"%s\"\n", sb_name (&head));
+ sb_kill (&head);
+}
+
+/* .page */
+
+void
+do_page ()
+{
+ fprintf (outfile, ".eject\n");
+}
+
+/* .form [lin=<value>] [col=<value>] */
+void
+do_form (idx, in)
+ int idx;
+ sb *in;
+{
+ int lines = 60;
+ int columns = 132;
+ idx = sb_skip_white (idx, in);
+
+ while (idx < in->len)
+ {
+
+ if (strncmp (in->ptr + idx, "LIN=", 4) == 0)
+ {
+ idx += 4;
+ idx = exp_get_abs ("form LIN= needs absolute expresssion.\n", idx, in, &lines);
+ }
+
+ if (strncmp (in->ptr + idx, "COL=", 4) == 0)
+ {
+ idx += 4;
+ idx = exp_get_abs ("form COL= needs absolute expresssion.\n", idx, in, &columns);
+ }
+
+ idx++;
+ }
+ fprintf (outfile, ".psize %d,%d\n", lines, columns);
+
+}
+
+int
+get_any_string (idx, in, out)
+ int idx;
+ sb *in;
+ sb *out;
+{
+ idx = sb_skip_white (idx, in);
+ if (idx < in->len && (in->ptr[idx] == '"'
+ || in->ptr[idx] == '<'))
+ return getstring (idx, in, out);
+
+ sb_reset (out);
+
+ while (idx < in->len && !ISSEP (in->ptr[idx]))
+ {
+ sb_add_char (out, in->ptr[idx++]);
+ }
+ return idx;
+}
+
+
+/* skip along sb in starting at idx, suck off whitespace a ( and more
+ whitespace. return the idx of the next char */
+
+int
+skip_openp (idx, in)
+ int idx;
+ sb *in;
+{
+ idx = sb_skip_white (idx, in);
+ if (in->ptr[idx] != '(')
+ ERROR ((stderr, "misplaced ( .\n"));
+ idx = sb_skip_white (idx + 1, in);
+ return idx;
+}
+
+/* skip along sb in starting at idx, suck off whitespace a ) and more
+ whitespace. return the idx of the next char */
+
+int
+skip_closep (idx, in)
+ int idx;
+ sb *in;
+{
+ idx = sb_skip_white (idx, in);
+ if (in->ptr[idx] != ')')
+ ERROR ((stderr, "misplaced ).\n"));
+ idx = sb_skip_white (idx + 1, in);
+ return idx;
+}
+
+/* .len */
+
+int
+dolen (idx, in, out)
+ int idx;
+ sb *in;
+ sb *out;
+{
+
+ sb stringout;
+ char buffer[10];
+
+ sb_new (&stringout);
+ idx = skip_openp (idx, in);
+ idx = get_and_process (idx, in, &stringout);
+ idx = skip_closep (idx, in);
+ sprintf (buffer, "%d", stringout.len);
+ sb_add_string (out, buffer);
+
+ sb_kill (&stringout);
+ return idx;
+}
+
+
+/* .instr */
+
+static
+int
+doinstr (idx, in, out)
+ int idx;
+ sb *in;
+ sb *out;
+{
+ sb string;
+ sb search;
+ int i;
+ int start;
+ int res;
+ char buffer[10];
+
+ sb_new (&string);
+ sb_new (&search);
+ idx = skip_openp (idx, in);
+ idx = get_and_process (idx, in, &string);
+ idx = sb_skip_comma (idx, in);
+ idx = get_and_process (idx, in, &search);
+ idx = sb_skip_comma (idx, in);
+ if (isdigit (in->ptr[idx]))
+ {
+ idx = exp_get_abs (".instr needs absolute expresson.\n", idx, in, &start);
+ }
+ else
+ {
+ start = 0;
+ }
+ idx = skip_closep (idx, in);
+ res = -1;
+ for (i = start; i < string.len; i++)
+ {
+ if (strncmp (string.ptr + i, search.ptr, search.len) == 0)
+ {
+ res = i;
+ break;
+ }
+ }
+ sprintf (buffer, "%d", res);
+ sb_add_string (out, buffer);
+ sb_kill (&string);
+ sb_kill (&search);
+ return idx;
+}
+
+
+static int
+dosubstr (idx, in, out)
+ int idx;
+ sb *in;
+ sb *out;
+{
+ sb string;
+ int pos;
+ int len;
+ sb_new (&string);
+
+ idx = skip_openp (idx, in);
+ idx = get_and_process (idx, in, &string);
+ idx = sb_skip_comma (idx, in);
+ idx = exp_get_abs ("need absolute position.\n", idx, in, &pos);
+ idx = sb_skip_comma (idx, in);
+ idx = exp_get_abs ("need absolute length.\n", idx, in, &len);
+ idx = skip_closep (idx, in);
+
+
+ if (len < 0 || pos < 0 ||
+ pos > string.len
+ || pos + len > string.len)
+ {
+ sb_add_string (out, " ");
+ }
+ else
+ {
+ sb_add_char (out, '"');
+ while (len > 0)
+ {
+ sb_add_char (out, string.ptr[pos++]);
+ len--;
+ }
+ sb_add_char (out, '"');
+ }
+ sb_kill(&string);
+ return idx;
+}
+
+/* scan line, change tokens in the hash table to their replacements */
+void
+process_assigns (idx, in, buf)
+ int idx;
+ sb *in;
+ sb *buf;
+{
+ while (idx < in->len)
+ {
+ hash_entry *ptr;
+ if (in->ptr[idx] == '\\'
+ && in->ptr[idx + 1] == '&')
+ {
+ idx = condass_lookup_name (in, idx + 2, buf);
+ }
+ else if (idx + 3 < in->len
+ && in->ptr[idx] == '.'
+ && in->ptr[idx + 1] == 'L'
+ && in->ptr[idx + 2] == 'E'
+ && in->ptr[idx + 3] == 'N')
+ idx = dolen (idx + 4, in, buf);
+ else if (idx + 6 < in->len
+ && in->ptr[idx] == '.'
+ && in->ptr[idx + 1] == 'I'
+ && in->ptr[idx + 2] == 'N'
+ && in->ptr[idx + 3] == 'S'
+ && in->ptr[idx + 4] == 'T'
+ && in->ptr[idx + 5] == 'R')
+ idx = doinstr (idx + 6, in, buf);
+ else if (idx + 7 < in->len
+ && in->ptr[idx] == '.'
+ && in->ptr[idx + 1] == 'S'
+ && in->ptr[idx + 2] == 'U'
+ && in->ptr[idx + 3] == 'B'
+ && in->ptr[idx + 4] == 'S'
+ && in->ptr[idx + 5] == 'T'
+ && in->ptr[idx + 6] == 'R')
+ idx = dosubstr (idx + 7, in, buf);
+ else if (ISFIRSTCHAR (in->ptr[idx]))
+ {
+ /* may be a simple name subsitution, see if we have a word */
+ sb acc;
+ int cur = idx + 1;
+ while (cur < in->len
+ && (ISNEXTCHAR (in->ptr[cur])))
+ cur++;
+
+ sb_new (&acc);
+ sb_add_buffer (&acc, in->ptr + idx, cur - idx);
+ ptr = hash_lookup (&assign_hash_table, &acc);
+ if (ptr)
+ {
+ /* Found a definition for it */
+ sb_add_sb (buf, &ptr->value.s);
+ }
+ else
+ {
+ /* No definition, just copy the word */
+ sb_add_sb (buf, &acc);
+ }
+ sb_kill (&acc);
+ idx = cur;
+ }
+ else
+ {
+ sb_add_char (buf, in->ptr[idx++]);
+ }
+ }
+}
+
+static int
+get_and_process (idx, in, out)
+ int idx;
+ sb *in;
+ sb *out;
+{
+ sb t;
+ sb_new (&t);
+ idx = get_any_string (idx, in, &t);
+ process_assigns (0, &t, out);
+ sb_kill (&t);
+ return idx;
+}
+
+static
+void
+process_file ()
+{
+ sb line;
+ sb t1, t2;
+ sb acc;
+ int more;
+
+ sb_new (&line);
+ sb_new (&t1);
+ sb_new (&t2);
+ sb_new(&acc);
+
+ sb_reset (&line);
+ more = get_line (&line);
+ while (more)
+ {
+ /* Find any label and pseudo op that we're intested in */
+ int l;
+ if (line.len == 0)
+ {
+ if (condass_on ())
+ fprintf (outfile, "\n");
+ }
+ else
+ {
+
+ l = grab_label (&line, &label);
+ if (line.ptr[l] == ':')
+ l++;
+ while (ISWHITE (line.ptr[l]) && l < line.len)
+ l++;
+
+ if (line.len)
+ {
+ if (process_pseudo_op (l, &line, &acc))
+ {
+
+
+
+ }
+ else if (condass_on ())
+ {
+ if (macro_op (l, &line))
+ {
+
+
+ }
+ else
+ {
+ {
+ if (label.len)
+ {
+ fprintf (outfile, "%s:\t", sb_name (&label));
+ }
+ else
+ fprintf (outfile, "\t");
+ sb_reset(&t1);
+ process_assigns (l, &line, &t1);
+ sb_reset (&t2);
+ change_base (0, &t1, &t2);
+ fprintf (outfile, "%s\n", sb_name (&t2));
+ }
+ }
+ }
+ }
+ }
+
+ if (had_end)
+ break;
+ sb_reset (&line);
+ more = get_line (&line);
+ }
+
+ if (!had_end)
+ WARNING ((stderr, ".END missing from end of file.\n"));
+}
+
+
+
+
+
+static void
+free_old_entry (ptr)
+ hash_entry *ptr;
+{
+ if (ptr)
+ {
+ if (ptr->type == hash_string)
+ sb_kill(&ptr->value.s);
+ }
+}
+
+/* name: .ASSIGNA <value> */
+
+void
+do_assigna (idx, in)
+ int idx;
+ sb *in;
+{
+ sb tmp;
+ int val;
+ sb_new (&tmp);
+
+ process_assigns (idx, in, &tmp);
+ idx = exp_get_abs (".ASSIGNA needs constant expression argument.\n", 0, &tmp, &val);
+
+ if (!label.len)
+ {
+ ERROR ((stderr, ".ASSIGNA without label.\n"));
+ }
+ else
+ {
+ hash_entry *ptr = hash_create (&vars, &label);
+ free_old_entry (ptr);
+ ptr->type = hash_integer;
+ ptr->value.i = val;
+ }
+ sb_kill (&tmp);
+}
+
+/* name: .ASSIGNC <string> */
+
+void
+do_assignc (idx, in)
+ int idx;
+ sb *in;
+{
+ sb acc;
+ sb_new (&acc);
+ idx = getstring (idx, in, &acc);
+
+ if (!label.len)
+ {
+ ERROR ((stderr, ".ASSIGNS without label.\n"));
+ }
+ else
+ {
+ hash_entry *ptr = hash_create (&vars, &label);
+ free_old_entry (ptr);
+ ptr->type = hash_string;
+ sb_new (&ptr->value.s);
+ sb_add_sb (&ptr->value.s, &acc);
+ }
+ sb_kill (&acc);
+}
+
+
+/* name: .REG (reg) */
+
+static void
+do_reg (idx, in)
+ int idx;
+ sb *in;
+{
+ /* remove reg stuff from inside parens */
+ sb what;
+ idx = skip_openp (idx, in);
+ sb_new (&what);
+ while (idx < in->len && in->ptr[idx] != ')')
+ {
+ sb_add_char (&what, in->ptr[idx]);
+ idx++;
+ }
+ hash_add_to_string_table (&assign_hash_table, &label, &what, 1);
+ sb_kill (&what);
+}
+
+
+static int
+condass_lookup_name (inbuf, idx, out)
+ sb *inbuf;
+ int idx;
+ sb *out;
+{
+ hash_entry *ptr;
+ sb condass_acc;
+ sb_new (&condass_acc);
+
+ while (idx < inbuf->len
+ && ISNEXTCHAR (inbuf->ptr[idx]))
+ {
+ sb_add_char (&condass_acc, inbuf->ptr[idx++]);
+ }
+
+ if (inbuf->ptr[idx] == '\'')
+ idx++;
+ ptr = hash_lookup (&vars, &condass_acc);
+ if (!ptr)
+ {
+ WARNING ((stderr, "Can't find preprocessor variable %s.\n", sb_name (&condass_acc)));
+ }
+ else
+ {
+ if (ptr->type == hash_integer)
+ {
+ char buffer[30];
+ sprintf (buffer, "%d", ptr->value.i);
+ sb_add_string (out, buffer);
+ }
+ else
+ {
+ sb_add_sb (out, &ptr->value.s);
+ }
+ }
+ sb_kill (&condass_acc);
+ return idx;
+}
+
+#define EQ 1
+#define NE 2
+#define GE 3
+#define LT 4
+#define LE 5
+#define GT 6
+#define NEVER 7
+
+int
+whatcond (idx, in, val)
+ int idx;
+ sb *in;
+ int *val;
+{
+ int cond;
+ char *p;
+ idx = sb_skip_white (idx, in);
+ p = in->ptr + idx;
+ if (p[0] == 'E' && p[1] == 'Q')
+ cond = EQ;
+ else if (p[0] == 'N' && p[1] == 'E')
+ cond = NE;
+ else if (p[0] == 'L' && p[1] == 'T')
+ cond = LT;
+ else if (p[0] == 'L' && p[1] == 'E')
+ cond = LE;
+ else if (p[0] == 'G' && p[1] == 'T')
+ cond = GT;
+ else if (p[0] == 'G' && p[1] == 'E')
+ cond = GE;
+ else
+ {
+ ERROR ((stderr, "Comparison operator must be one of EQ, NE, LT, LE, GT or GE"));
+ cond = NEVER;
+ }
+ idx = sb_skip_white (idx + 2, in);
+ *val = cond;
+ return idx;
+}
+
+int
+istrue (idx, in)
+ int idx;
+ sb *in;
+{
+ int res;
+ sb acc_a;
+ sb cond;
+ sb acc_b;
+ sb_new (&acc_a);
+ sb_new (&cond);
+ sb_new (&acc_b);
+ idx = sb_skip_white (idx, in);
+
+ if (in->ptr[idx] == '"')
+ {
+ int cond;
+ int same;
+ /* This is a string comparision */
+ idx = getstring (idx, in, &acc_a);
+ idx = whatcond (idx, in, &cond);
+ idx = getstring (idx, in, &acc_b);
+ same = acc_a.len == acc_b.len && (strncmp (acc_a.ptr, acc_b.ptr, acc_a.len) == 0);
+
+ if (cond != EQ && cond != NE)
+ {
+ ERROR ((stderr, "Comparison operator for strings must be EQ or NE"));
+ res = 0;
+ }
+ else
+ res = cond == EQ && same;
+ }
+ else
+ /* This is a numeric expression */
+ {
+ int vala;
+ int valb;
+ int cond;
+ idx = exp_get_abs ("Conditional operator must have absolute operands.\n", idx, in, &vala);
+ idx = whatcond (idx, in, &cond);
+ idx = sb_skip_white (idx, in);
+ if (in->ptr[idx] == '"')
+ {
+ WARNING ((stderr, "String compared against expression.\n"));
+ res = 0;
+ }
+ else
+ {
+ idx = exp_get_abs ("Conditional operator must have absolute operands.\n", idx, in, &valb);
+ switch (cond)
+ {
+ case EQ:
+ res = vala == valb;
+ break;
+ case NE:
+ res = vala != valb;
+ break;
+ case LT:
+ res = vala < valb;
+ break;
+ case LE:
+ res = vala <= valb;
+ break;
+ case GT:
+ res = vala > valb;
+ break;
+ case GE:
+ res = vala >= valb;
+ break;
+ case NEVER:
+ res = 0;
+ break;
+ }
+ }
+ }
+
+ sb_kill (&acc_a);
+ sb_kill (&cond);
+ sb_kill (&acc_b);
+ return res;
+}
+
+/* .AIF */
+static void
+do_aif (idx, in)
+ int idx;
+ sb *in;
+{
+ if (ifi >= IFNESTING)
+ {
+ FATAL ((stderr, "AIF nesting unreasonable.\n"));
+ }
+ ifi++;
+ ifstack[ifi].on = istrue (idx, in);
+ ifstack[ifi].hadelse = 0;
+}
+
+
+/* .AELSE */
+static void
+do_aelse ()
+{
+ ifstack[ifi].on = !ifstack[ifi].on;
+ if (ifstack[ifi].hadelse)
+ {
+ ERROR ((stderr, "Multiple AELSEs in AIF.\n"));
+ }
+ ifstack[ifi].hadelse = 1;
+}
+
+
+/* .AENDI */
+static void
+do_aendi ()
+{
+ if (ifi != 0)
+ {
+ ifi--;
+ }
+ else
+ {
+ ERROR ((stderr, "AENDI without AIF.\n"));
+ }
+}
+
+static int
+condass_on ()
+{
+ return ifstack[ifi].on;
+}
+
+
+/* Read input lines till we get to a TO string.
+ Increase nesting depth if we geta FROM string.
+ Put the results into sb at PTR. */
+
+static void
+buffer_and_nest (from, to, ptr)
+ char *from;
+ char *to;
+ sb *ptr;
+{
+ int from_len = strlen (from);
+ int to_len = strlen (to);
+ int depth = 1;
+ int line_start = ptr->len;
+ int line = linecount ();
+
+ int more = get_line (ptr);
+
+ while (more)
+ {
+ /* Try and find the first pseudo op on the line */
+ int i = line_start;
+
+ /* Skip leading whitespace */
+ while (i < ptr->len
+ && ISWHITE (ptr->ptr[i]))
+ i++;
+
+ /* Skip over a label */
+ while (i < ptr->len
+ && ISNEXTCHAR (ptr->ptr[i]))
+ i++;
+
+ /* And a colon */
+ if (i < ptr->len
+ && ptr->ptr[i] == ':')
+ i++;
+
+ /* Skip trailing whitespace */
+ while (i < ptr->len
+ && ISWHITE (ptr->ptr[i]))
+ i++;
+
+ if (i < ptr->len
+ && ptr->ptr[i] == '.')
+ {
+ if (strncmp (ptr->ptr + i, from, from_len) == 0)
+ depth++;
+ if (strncmp (ptr->ptr + i, to, to_len) == 0)
+ {
+ depth--;
+ if (depth == 0)
+ {
+ /* Reset the string to not include the ending rune */
+ ptr->len = line_start;
+ break;
+ }
+ }
+ }
+
+ /* Add a CR to the end and keep running */
+ sb_add_char (ptr, '\n');
+ line_start = ptr->len;
+ more = get_line (ptr);
+ }
+
+
+ if (depth)
+ FATAL ((stderr, "End of file whilst inside %s, started on line %d.\n", from, line));
+}
+
+
+/* .ENDR */
+void
+do_aendr ()
+{
+ ERROR ((stderr, "AENDR without a AREPEAT.\n"));
+}
+
+/* .AWHILE */
+
+static
+void
+do_awhile (idx, in)
+ int idx;
+ sb *in;
+{
+ sb exp;
+
+ sb sub;
+
+ int doit;
+ sb_new (&sub);
+ sb_new (&exp);
+
+ process_assigns (idx, in, &exp);
+ doit = istrue (0, &exp);
+
+ buffer_and_nest (".AWHILE", ".AENDW", &sub);
+
+ /* Turn
+ .AWHILE exp
+ foo
+ .AENDW
+ into
+ foo
+ .AWHILE exp
+ foo
+ .ENDW
+ */
+
+ if (doit)
+ {
+ int index = include_next_index ();
+
+ sb copy;
+ sb_new (©);
+ sb_add_sb (©, &sub);
+ sb_add_sb (©, in);
+ sb_add_string (©, "\n");
+ sb_add_sb (©, &sub);
+ sb_add_string (©, "\t.AENDW\n");
+ /* Push another WHILE */
+ include_buf (&exp, ©, include_while, index);
+ sb_kill (©);
+ }
+ sb_kill (&exp);
+ sb_kill (&sub);
+}
+
+
+/* .AENDW */
+
+static void
+do_aendw ()
+{
+ ERROR ((stderr, "AENDW without a AENDW.\n"));
+}
+
+
+/* .EXITM
+
+ Pop things off the include stack until the type and index changes */
+
+static void
+do_exitm ()
+{
+ include_type type = sp->type;
+ if (type == include_repeat
+ || type == include_while
+ || type == include_macro)
+ {
+ int index = sp->index;
+ include_pop ();
+ while (sp->index == index
+ && sp->type == type)
+ {
+ include_pop ();
+ }
+ }
+}
+
+/* .AREPEAT */
+
+static void
+do_arepeat (idx, in)
+ int idx;
+ sb *in;
+{
+ sb exp; /* buffer with expression in it */
+ sb copy; /* expanded repeat block */
+ sb sub; /* contents of AREPEAT */
+ int rc;
+ char buffer[30];
+ sb_new (&exp);
+ sb_new (©);
+ sb_new (&sub);
+ process_assigns (idx, in, &exp);
+ idx = exp_get_abs ("AREPEAT must have absolute operand.\n", 0, &exp, &rc);
+ buffer_and_nest (".AREPEAT", ".AENDR", &sub);
+ if (rc > 0)
+ {
+ /* Push back the text following the repeat, and another repeat block
+ so
+ .AREPEAT 20
+ foo
+ .AENDR
+ gets turned into
+ foo
+ .AREPEAT 19
+ foo
+ .AENDR
+ */
+ int index = include_next_index ();
+ sb_add_sb (©, &sub);
+ if (rc > 1)
+ {
+ sprintf (buffer, "\t.AREPEAT %d\n", rc - 1);
+ sb_add_string (©, buffer);
+ sb_add_sb (©, &sub);
+ sb_add_string (©, " .AENDR\n");
+ }
+
+ include_buf (&exp, ©, include_repeat, index);
+ }
+ sb_kill (&exp);
+ sb_kill (&sub);
+ sb_kill (©);
+}
+
+/* .ENDM */
+
+static void
+do_endm ()
+{
+ ERROR ((stderr, ".ENDM without a matching .MACRO.\n"));
+}
+
+
+/* MARRO PROCESSING */
+
+static int number;
+hash_table macro_table;
+
+/* Understand
+
+ .MACRO <name>
+ stuff
+ .ENDM
+*/
+
+static int
+do_formals (macro, idx, in)
+ macro_entry *macro;
+ int idx;
+ sb *in;
+{
+ formal_entry **p = ¯o->formals;
+ macro->formal_count = 0;
+ hash_new_table (5, ¯o->formal_hash);
+ while (idx < in->len)
+ {
+ formal_entry *formal;
+
+ formal = (formal_entry *) xmalloc (sizeof (formal_entry));
+
+ sb_new (&formal->name);
+ sb_new (&formal->def);
+ sb_new (&formal->actual);
+
+ idx = sb_skip_white (idx, in);
+ idx = get_token (idx, in, &formal->name);
+ if (formal->name.len == 0)
+ break;
+ idx = sb_skip_white (idx, in);
+ if (formal->name.len)
+ {
+ /* This is a formal */
+ if (idx < in->len && in->ptr[idx] == '=')
+ {
+ /* Got a default */
+ idx = get_any_string (idx + 1, in, &formal->def);
+ }
+ }
+
+ {
+ /* Add to macro's hash table */
+
+ hash_entry *p = hash_create (¯o->formal_hash, &formal->name);
+ p->type = hash_formal;
+ p->value.f = formal;
+ }
+
+ formal->index = macro->formal_count;
+ idx = sb_skip_comma (idx, in);
+ macro->formal_count++;
+ *p = formal;
+ p = &formal->next;
+ }
+ return idx;
+}
+
+static
+void
+do_macro (idx, in)
+ int idx;
+ sb *in;
+{
+ macro_entry *macro;
+ sb name;
+
+ macro = (macro_entry *) xmalloc (sizeof (macro_entry));
+ sb_new (¯o->sub);
+ sb_new (&name);
+
+ macro->formal_count = 0;
+ macro->formals = 0;
+
+ idx = sb_skip_white (idx, in);
+ buffer_and_nest (".MACRO", ".ENDM", ¯o->sub);
+ if (label.len)
+ {
+ /* It's the label: MACRO (formals,...) sort */
+ sb_add_sb (&name, &label);
+ if (in->ptr[idx] == '(')
+ {
+ /* Got some formals */
+ idx = do_formals (macro, idx + 1, in);
+ if (in->ptr[idx] != ')')
+ ERROR ((stderr, "Missing ) after formals.\n"));
+ }
+ }
+ else
+ {
+ idx = get_token (idx, in, &name);
+ idx = sb_skip_white (idx, in);
+ idx = do_formals (macro, idx, in);
+ }
+
+ /* and stick it in the macro hash table */
+ hash_create (¯o_table, &name)->value.m = macro;
+}
+
+static
+int
+get_token (idx, in, name)
+ int idx;
+ sb *in;
+ sb *name;
+{
+ if (idx < in->len
+ && ISFIRSTCHAR (in->ptr[idx]))
+ {
+ sb_add_char (name, in->ptr[idx++]);
+ while (idx < in->len
+ && ISNEXTCHAR (in->ptr[idx]))
+ {
+ sb_add_char (name, in->ptr[idx++]);
+ }
+ }
+ return idx;
+}
+
+/* Scan a token, but stop if a ' is seen */
+static int
+get_apost_token (idx, in, name, kind)
+ int idx;
+ sb *in;
+ sb *name;
+ int kind;
+{
+ idx = get_token (idx, in, name);
+ if (idx < in->len && in->ptr[idx] == kind)
+ idx++;
+ return idx;
+}
+
+static int
+sub_actual (src, in, t, m, kind, out)
+ int src;
+ sb *in;
+ sb *t;
+ macro_entry *m;
+ int kind;
+ sb *out;
+{
+ /* This is something to take care of */
+ hash_entry *ptr;
+ src = get_apost_token (src, in, t, kind);
+ /* See if it's in the macro's hash table */
+ ptr = hash_lookup (&m->formal_hash, t);
+ if (ptr)
+ {
+ if (ptr->value.f->actual.len)
+ {
+ sb_add_sb (out, &ptr->value.f->actual);
+ }
+ else
+ {
+ sb_add_sb (out, &ptr->value.f->def);
+ }
+ }
+ else
+ {
+ sb_add_char (out, '\\');
+ sb_add_sb (out, t);
+ }
+ return src;
+}
+
+static
+void
+macro_expand (name, idx, in, m)
+ sb *name;
+ int idx;
+ sb *in;
+ macro_entry *m;
+{
+ sb t;
+ sb out;
+ hash_entry *ptr;
+ formal_entry *f;
+
+ int is_positional = 0;
+ int is_keyword = 0;
+
+ sb_new (&t);
+ sb_new (&out);
+
+ /* Reset any old value the actuals may have */
+ for (f = m->formals; f; f = f->next)
+ sb_reset (&f->actual);
+ f = m->formals;
+ /* Peel off the actuals and store them away in the hash tables' actuals */
+ while (idx < in->len)
+ {
+ int scan;
+ idx = sb_skip_white (idx, in);
+ /* Look and see if it's a positional or keyword arg */
+ scan = idx;
+ while (scan < in->len
+ && !ISSEP (in->ptr[scan])
+ && in->ptr[scan] != '=')
+ scan++;
+ if (scan < in->len && in->ptr[scan] == '=')
+ {
+ is_keyword = 1;
+ if (is_positional)
+ {
+ ERROR ((stderr, "Can't mix positional and keyword arguments.\n"));
+ return;
+ }
+ /* This is a keyword arg, fetch the formal name and
+ then the actual stuff */
+ sb_reset (&t);
+ idx = get_token (idx, in, &t);
+ if (in->ptr[idx] != '=')
+ ERROR ((stderr, "confused about formal params.\n"));
+
+ /* Lookup the formal in the macro's list */
+ ptr = hash_lookup (&m->formal_hash, &t);
+ if (!ptr)
+ {
+ ERROR ((stderr, "MACRO formal argument %s does not exist.\n", sb_name (&t)));
+ return;
+ }
+ else
+ {
+ /* Insert this value into the right place */
+ sb_reset (&ptr->value.f->actual);
+ idx = get_any_string (idx + 1, in, &ptr->value.f->actual);
+ }
+ }
+ else
+ {
+ /* This is a positional arg */
+ is_positional = 1;
+ if (is_keyword)
+ {
+ ERROR ((stderr, "Can't mix positional and keyword arguments.\n"));
+ return;
+ }
+ if (!f)
+ {
+ ERROR ((stderr, "Too many positional arguments.\n"));
+ return;
+ }
+
+ sb_reset (&f->actual);
+ idx = get_any_string (idx, in, &f->actual);
+ f = f->next;
+ }
+ idx = sb_skip_comma (idx, in);
+ }
+
+ /* Copy the stuff from the macro buffer into a safe place and substitute any args */
+
+ {
+ int src = 0;
+ sb *in = &m->sub;
+ sb_reset (&out);
+
+ while (src < in->len)
+ {
+ if (in->ptr[src] == '&')
+ {
+ sb_reset (&t);
+ src = sub_actual (src + 1, in, &t, m, '&', &out);
+ }
+ else if (in->ptr[src] == '\\')
+ {
+ src++;
+ if (in->ptr[src] == ';')
+ {
+ /* This is a comment, just drop the rest of the line */
+ while (src < in->len
+ && in->ptr[src] != '\n')
+ src++;
+
+ }
+ else if (in->ptr[src] == '(')
+ {
+ /* Sub in till the next ')' literally */
+ src++;
+ while (src < in->len && in->ptr[src] != ')')
+ {
+ sb_add_char (&out, in->ptr[src++]);
+ }
+ if (in->ptr[src] == ')')
+ src++;
+ else
+ ERROR ((stderr, "Missplaced ).\n"));
+ }
+ else if (in->ptr[src] == '@')
+ {
+ /* Sub in the macro invocation number */
+
+ char buffer[6];
+ src++;
+ sprintf (buffer, "%05d", number);
+ sb_add_string (&out, buffer);
+ }
+ else if (in->ptr[src] == '&')
+ {
+ /* This is a preprocessor variable name, we don't do them
+ here */
+ sb_add_char (&out, '\\');
+ sb_add_char (&out, '&');
+ src++;
+ }
+ else
+ {
+ sb_reset (&t);
+ src = sub_actual (src, in, &t, m, '\'', &out);
+ }
+ }
+ else
+ {
+ sb_add_char (&out, in->ptr[src++]);
+ }
+ }
+ include_buf (name, &out, include_macro, include_next_index ());
+ }
+ sb_kill (&t);
+ sb_kill (&out);
+ number++;
+}
+
+static int
+macro_op (idx, in)
+ int idx;
+ sb *in;
+{
+ int res = 0;
+ /* The macro name must be the first thing on the line */
+ if (idx < in->len)
+ {
+ sb name;
+ hash_entry *ptr;
+ sb_new (&name);
+ idx = get_token (idx, in, &name);
+
+ if (name.len)
+ {
+ /* Got a name, look it up */
+
+ ptr = hash_lookup (¯o_table, &name);
+
+ if (ptr)
+ {
+ /* It's in the table, copy out the stuff and convert any macro args */
+ macro_expand (&name, idx, in, ptr->value.m);
+ res = 1;
+ }
+ }
+ sb_kill (&name);
+ }
+
+
+ return res;
+}
+
+
+/* STRING HANDLING */
+
+static int
+getstring (idx, in, acc)
+ int idx;
+ sb *in;
+ sb *acc;
+{
+ idx = sb_skip_white (idx, in);
+
+ while (idx < in->len
+ && (in->ptr[idx] == '"' || in->ptr[idx] == '<'))
+ {
+ if (in->ptr[idx] == '<')
+ {
+ int code;
+ idx++;
+ idx = exp_get_abs ("Character code in string must be absolute expression.\n",
+ idx, in, &code);
+ sb_add_char (acc, code);
+
+ if (in->ptr[idx] != '>')
+ ERROR ((stderr, "Missing > for character code.\n"));
+ idx++;
+ }
+ else if (in->ptr[idx] == '"')
+ {
+ idx++;
+ while (idx < in->len)
+ {
+ if (in->ptr[idx] == '"')
+ {
+ idx++;
+ if (idx >= in->len || in->ptr[idx] != '"')
+ break;
+ }
+ sb_add_char (acc, in->ptr[idx]);
+ idx++;
+ }
+ }
+ }
+
+ return idx;
+}
+
+/* .SDATA[C|Z] <string> */
+
+static
+void
+do_sdata (idx, in, type)
+ int idx;
+ sb *in;
+ char type;
+{
+ int nc = 0;
+ sb acc;
+ sb_new (&acc);
+ fprintf (outfile, ".byte\t");
+
+ while (idx < in->len)
+ {
+ int i;
+ sb_reset (&acc);
+ idx = sb_skip_white (idx, in);
+ while ((in->ptr[idx] == '"'
+ || in->ptr[idx] == '<')
+ && idx < in->len)
+ {
+ idx = getstring (idx, in, &acc);
+ if (type == 'c')
+ {
+ if (acc.len > 255)
+ {
+ ERROR ((stderr, "string for SDATAC longer than 255 characters (%d).\n", acc.len));
+ }
+ fprintf (outfile, "%d", acc.len);
+ nc = 1;
+ }
+
+ for (i = 0; i < acc.len; i++)
+ {
+ if (nc)
+ {
+ fprintf (outfile, ",");
+ }
+ fprintf (outfile, "%d", acc.ptr[i]);
+ nc = 1;
+ }
+
+ if (type == 'z')
+ {
+ if (nc)
+ fprintf (outfile, ",");
+ fprintf (outfile, "0");
+ }
+ idx = sb_skip_white (idx, in);
+ }
+ if (in->ptr[idx] != ',' && idx != in->len)
+ {
+ fprintf (outfile, "\n");
+ ERROR ((stderr, "illegal character in SDATA line (0x%x).\n", in->ptr[idx]));
+ break;
+ }
+ idx++;
+ }
+ sb_kill (&acc);
+ fprintf (outfile, "\n");
+}
+
+/* .SDATAB <count> <string> */
+
+static void
+do_sdatab (idx, in)
+ int idx;
+ sb *in;
+{
+ int repeat;
+ int i;
+ sb acc;
+ sb_new (&acc);
+
+ idx = exp_get_abs ("Must have absolute SDATAB repeat count.\n", idx, in, &repeat);
+ if (repeat <= 0)
+ {
+ ERROR ((stderr, "Must have positive SDATAB repeat count (%d).\n", repeat));
+ repeat = 1;
+ }
+
+ idx = sb_skip_comma (idx, in);
+ idx = getstring (idx, in, &acc);
+
+ for (i = 0; i < repeat; i++)
+ {
+ if (i)
+ fprintf (outfile, "\t");
+ fprintf (outfile, ".byte\t");
+ sb_print (&acc);
+ fprintf (outfile, "\n");
+ }
+ sb_kill (&acc);
+
+}
+
+int
+new_file (name)
+ char *name;
+{
+ FILE *newone = fopen (name, "r");
+ if (!newone)
+ return 0;
+
+ if (isp == MAX_INCLUDES)
+ FATAL ((stderr, "Unreasonable include depth (%d).\n", isp));
+
+ sp++;
+ sp->handle = newone;
+
+ sb_new (&sp->name);
+ sb_add_string (&sp->name, name);
+
+ sp->linecount = 1;
+ sp->pushback_index = 0;
+ sp->type = include_file;
+ sp->index = 0;
+ sb_new (&sp->pushback);
+ return 1;
+}
+
+static void
+do_include (idx, in)
+ int idx;
+ sb *in;
+{
+ sb t;
+ char *text;
+ sb_new (&t);
+ idx = getstring (idx, in, &t);
+ text = sb_name (&t);
+ if (!new_file (text))
+ {
+ FATAL ((stderr, "Can't open include file `%s'.\n", text));
+ }
+ sb_kill (&t);
+}
+
+static void
+include_pop ()
+{
+ if (sp != include_stack)
+ {
+ if (sp->handle)
+ fclose (sp->handle);
+ sp--;
+ }
+}
+
+/* Get the next character from the include stack. If there's anything
+ in the pushback buffer, take that first. If we're at eof, pop from
+ the stack and try again. Keep the linecount up to date. */
+
+static int
+get ()
+{
+ int r;
+
+ if (sp->pushback.len != sp->pushback_index)
+ {
+ r = (char) (sp->pushback.ptr[sp->pushback_index++]);
+ /* When they've all gone, reset the pointer */
+ if (sp->pushback_index == sp->pushback.len)
+ {
+ sp->pushback.len = 0;
+ sp->pushback_index = 0;
+ }
+ }
+ else if (sp->handle)
+ {
+ r = getc (sp->handle);
+ }
+ else
+ r = EOF;
+
+ if (r == EOF && isp)
+ {
+ include_pop ();
+ r = get ();
+ while (r == EOF && isp)
+ {
+ include_pop ();
+ r = get ();
+ }
+ return r;
+ }
+ if (r == '\n')
+ {
+ sp->linecount++;
+ }
+
+ return r;
+}
+
+static int
+linecount ()
+{
+ return sp->linecount;
+}
+
+static int
+include_next_index ()
+{
+ static int index;
+ if (!unreasonable
+ && index > MAX_REASONABLE)
+ FATAL ((stderr, "Unreasonable expansion (-u turns off check).\n"));
+ return ++index;
+}
+
+
+/* Initialize the chartype vector. */
+
+static void
+chartype_init ()
+{
+ int x;
+ for (x = 0; x < 256; x++)
+ {
+ if (isalpha (x) || x == '_' || x == '$')
+ chartype[x] |= FIRSTBIT;
+
+ if (isdigit (x) || isalpha (x) || x == '_' || x == '$')
+ chartype[x] |= NEXTBIT;
+
+ if (x == ' ' || x == '\t' || x == ',' || x == '"' || x == ';'
+ || x == '"' || x == '<' || x == '>' || x == ')' || x == '(')
+ chartype[x] |= SEPBIT;
+
+ if (x == ' ' || x == '\t')
+ chartype[x] |= WHITEBIT;
+ }
+}
+
+
+/* What to do with all the keywords */
+#define PROCESS 0x1000 /* Run substitution over the line */
+#define LAB 0x2000 /* Spit out the label */
+
+#define K_EQU PROCESS|1
+#define K_ASSIGN PROCESS|2
+#define K_REG PROCESS|3
+#define K_ORG PROCESS|4
+#define K_RADIX PROCESS|5
+#define K_DATA LAB|PROCESS|6
+#define K_DATAB LAB|PROCESS|7
+#define K_SDATA LAB|PROCESS|8
+#define K_SDATAB LAB|PROCESS|9
+#define K_SDATAC LAB|PROCESS|10
+#define K_SDATAZ LAB|PROCESS|11
+#define K_RES LAB|PROCESS|12
+#define K_SRES LAB|PROCESS|13
+#define K_SRESC LAB|PROCESS|14
+#define K_SRESZ LAB|PROCESS|15
+#define K_EXPORT LAB|PROCESS|16
+#define K_GLOBAL LAB|PROCESS|17
+#define K_PRINT LAB|PROCESS|19
+#define K_FORM LAB|PROCESS|20
+#define K_HEADING LAB|PROCESS|21
+#define K_PAGE LAB|PROCESS|22
+#define K_IMPORT LAB|PROCESS|23
+#define K_PROGRAM LAB|PROCESS|24
+#define K_END PROCESS|25
+#define K_INCLUDE PROCESS|26
+#define K_IGNORED PROCESS|27
+#define K_ASSIGNA 28
+#define K_ASSIGNC 29
+#define K_AIF PROCESS|30
+#define K_AELSE PROCESS|31
+#define K_AENDI PROCESS|32
+#define K_AREPEAT PROCESS|33
+#define K_AENDR PROCESS|34
+#define K_AWHILE 35
+#define K_AENDW PROCESS|36
+#define K_EXITM 37
+#define K_MACRO PROCESS|38
+#define K_ENDM 39
+#define K_ALIGN PROCESS|LAB|40
+
+
+static struct
+{
+ char *name;
+ int code;
+ int extra;
+}
+kinfo[] =
+{
+ "EQU", K_EQU, 0,
+ "ASSIGN", K_ASSIGN, 0,
+ "REG", K_REG, 0,
+ "ORG", K_ORG, 0,
+ "RADIX", K_RADIX, 0,
+ "DATA", K_DATA, 0,
+ "DATAB", K_DATAB, 0,
+ "SDATA", K_SDATA, 0,
+ "SDATAB", K_SDATAB, 0,
+ "SDATAZ", K_SDATAZ, 0,
+ "SDATAC", K_SDATAC, 0,
+ "RES", K_RES, 0,
+ "SRES", K_SRES, 0,
+ "SRESC", K_SRESC, 0,
+ "SRESZ", K_SRESZ, 0,
+ "EXPORT", K_EXPORT, 0,
+ "GLOBAL", K_GLOBAL, 0,
+ "PRINT", K_PRINT, 0,
+ "FORM", K_FORM, 0,
+ "HEADING", K_HEADING, 0,
+ "PAGE", K_PAGE, 0,
+ "PROGRAM", K_IGNORED, 0,
+ "END", K_END, 0,
+ "INCLUDE", K_INCLUDE, 0,
+ "ASSIGNA", K_ASSIGNA, 0,
+ "ASSIGNC", K_ASSIGNC, 0,
+ "AIF", K_AIF, 0,
+ "AELSE", K_AELSE, 0,
+ "AENDI", K_AENDI, 0,
+ "AREPEAT", K_AREPEAT, 0,
+ "AENDR", K_AENDR, 0,
+ "EXITM", K_EXITM, 0,
+ "MACRO", K_MACRO, 0,
+ "ENDM", K_ENDM, 0,
+ "AWHILE", K_AWHILE, 0,
+ "ALIGN", K_ALIGN, 0,
+ "AENDW", K_AENDW, 0,
+ 0
+};
+
+/* Look for a pseudo op on the line. If one's there then call
+ its handler. */
+
+static int
+process_pseudo_op (idx, line, acc)
+ int idx;
+ sb *line;
+ sb *acc;
+{
+ char *in = line->ptr + idx;
+
+ if (in[0] == '.')
+ {
+ /* Scan forward and find pseudo name */
+ hash_entry *ptr;
+
+ char *s = in + 1;
+ char *e = s;
+ sb_reset (acc);
+ idx++;
+ while (idx < line->len && *e && ISFIRSTCHAR (*e))
+ {
+ sb_add_char (acc, *e);
+ e++;
+ idx++;
+ }
+
+ ptr = hash_lookup (&keyword_hash_table, acc);
+
+ if (!ptr)
+ {
+ WARNING ((stderr, "Unrecognised pseudo op `%s'.\n", sb_name (acc)));
+ return 0;
+ }
+ if (ptr->value.i & LAB)
+ { /* output the label */
+ if (label.len)
+ {
+ fprintf (outfile, "%s:\t", sb_name (&label));
+ }
+ else
+ fprintf (outfile, "\t");
+ }
+
+ if (ptr->value.i & PROCESS)
+ {
+ /* Polish the rest of the line before handling the pseudo op */
+/* strip_comments(line);*/
+ sb_reset (acc);
+ process_assigns (idx, line, acc);
+ sb_reset(line);
+ change_base (0, acc, line);
+ idx = 0;
+ }
+ if (!condass_on ())
+ {
+ switch (ptr->value.i)
+ {
+ case K_AELSE:
+ do_aelse ();
+ break;
+ case K_AENDI:
+ do_aendi ();
+ break;
+ }
+ return 1;
+ }
+ else
+ {
+ switch (ptr->value.i)
+ {
+ case K_AELSE:
+ do_aelse ();
+ return 1;
+ case K_AENDI:
+ do_aendi ();
+ return 1;
+ case K_ORG:
+ ERROR ((stderr, "ORG command not allowed.\n"));
+ break;
+ case K_RADIX:
+ do_radix (line);
+ return 1;
+ case K_DATA:
+ do_data (idx, line);
+ return 1;
+ case K_DATAB:
+ do_datab (idx, line);
+ return 1;
+ case K_SDATA:
+ do_sdata (idx, line, 0);
+ return 1;
+ case K_SDATAB:
+ do_sdatab (idx, line);
+ return 1;
+ case K_SDATAC:
+ do_sdata (idx, line, 'c');
+ return 1;
+ case K_SDATAZ:
+ do_sdata (idx, line, 'z');
+ return 1;
+ case K_ASSIGN:
+ do_assign (1, 0, line);
+ return 1;
+ case K_AIF:
+ do_aif (idx, line);
+ return 1;
+ case K_AREPEAT:
+ do_arepeat (idx, line);
+ return 1;
+ case K_AENDW:
+ do_aendw ();
+ return 1;
+ case K_AWHILE:
+ do_awhile (idx, line);
+ return 1;
+ case K_AENDR:
+ do_aendr ();
+ return 1;
+ case K_EQU:
+ do_assign (0, idx, line);
+ return 1;
+ case K_ALIGN:
+ do_align (idx, line);
+ return 1;
+ case K_RES:
+ do_res (idx, line, 0);
+ return 1;
+ case K_SRES:
+ do_res (idx, line, 's');
+ return 1;
+ case K_INCLUDE:
+ do_include (idx, line);
+ return 1;
+ case K_MACRO:
+ do_macro (idx, line);
+ return 1;
+ case K_ENDM:
+ do_endm ();
+ return 1;
+ case K_SRESC:
+ do_res (idx, line, 'c');
+ return 1;
+ case K_PRINT:
+ do_print (idx, line);
+ return 1;
+ case K_FORM:
+ do_form (idx, line);
+ return 1;
+ case K_HEADING:
+ do_heading (idx, line);
+ return 1;
+ case K_PAGE:
+ do_page ();
+ return 1;
+ case K_GLOBAL:
+ case K_EXPORT:
+ do_export (line);
+ return 1;
+ case K_IMPORT:
+ return 1;
+ case K_SRESZ:
+ do_res (idx, line, 'z');
+ return 1;
+ case K_IGNORED:
+ return 1;
+ case K_END:
+ do_end ();
+ return 1;
+ case K_ASSIGNA:
+ do_assigna (idx, line);
+ return 1;
+ case K_ASSIGNC:
+ do_assignc (idx, line);
+ return 1;
+ case K_EXITM:
+ do_exitm ();
+ return 1;
+ case K_REG:
+ do_reg (idx, line);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+
+/* Build the keyword hash table - put each keyword in the table twice,
+ once upper and once lower case.*/
+
+static void
+process_init ()
+{
+ int i;
+
+ for (i = 0; kinfo[i].name; i++)
+ {
+ sb label;
+ int j;
+ sb_new (&label);
+ sb_add_string (&label, kinfo[i].name);
+
+ hash_add_to_int_table (&keyword_hash_table, &label, kinfo[i].code);
+
+ sb_reset (&label);
+ for (j = 0; kinfo[i].name[j]; j++)
+ sb_add_char (&label, kinfo[i].name[j] - 'A' + 'a');
+ hash_add_to_int_table (&keyword_hash_table, &label, kinfo[i].code);
+
+ sb_kill (&label);
+ }
+}
+
+int
+main (ac, av)
+ int ac;
+ char **av;
+{
+ int i;
+
+ sp = include_stack;
+
+ ifstack[0].on = 1;
+ ifi = 0;
+
+ chartype_init ();
+
+ hash_new_table (101, ¯o_table);
+ hash_new_table (101, &keyword_hash_table);
+ hash_new_table (101, &assign_hash_table);
+ hash_new_table (101, &vars);
+
+ sb_new (&label);
+ process_init ();
+
+ /* Find the output file */
+ for (i = 1; i < ac; i++)
+ {
+ if (av[i][0] == '-')
+ {
+ if (av[i][1] == 'c' && av[i][2] == 0)
+ {
+ copysource = 1;
+ }
+ else if (av[i][1] == 'p' && av[i][2] == 0)
+ {
+ print_line_number = 1;
+ }
+ else if (av[i][1] == 'u' && av[i][2] == 0)
+ {
+ unreasonable = 1;
+ }
+ else if (av[i][1] == 's' && av[i][2] == 0)
+ {
+ stats = 1;
+ }
+ else if (av[i][1] == 'o' && av[i][2] == 0 & i + 1 < ac)
+ {
+ /* Got output file name */
+ i++;
+ outfile = fopen (av[i], "w");
+ if (!outfile)
+ {
+ fprintf (stderr, "%s: Can't open output file `%s'.\n",
+ av[0], av[i]);
+ exit (1);
+ }
+ }
+ else
+ {
+ fprintf (stderr, "Usage: %s [-o filename] infile1 infile2...\n",
+ av[0]);
+ exit (1);
+ }
+ }
+ }
+
+ if (!outfile)
+ outfile = stdout;
+
+ /* Process all the input files */
+
+ for (i = 1; i < ac; i++)
+ {
+ if (av[i][0] == '-')
+ {
+ if (av[i][1] == 'o')
+ i++;
+ }
+ else
+ {
+ if (new_file (av[i]))
+ {
+ process_file ();
+ }
+ else
+ {
+ fprintf (stderr, "%s: Can't open input file `%s'.\n",
+ av[0], av[i]);
+ exit (1);
+ }
+ }
+ }
+ quit ();
+ return 0;
+}