diff options
Diffstat (limited to 'gas/gasp.c')
-rw-r--r-- | gas/gasp.c | 3745 |
1 files changed, 3745 insertions, 0 deletions
diff --git a/gas/gasp.c b/gas/gasp.c new file mode 100644 index 0000000..fbb65e3 --- /dev/null +++ b/gas/gasp.c @@ -0,0 +1,3745 @@ +/* gasp.c - Gnu assembler preprocessor main program. + Copyright (C) 1994, 95, 96, 97, 98, 1999 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, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* + +This program translates the input macros and stuff into a form +suitable for gas to consume. + + + gasp [-sdhau] [-c char] [-o <outfile>] <infile>* + + -s copy source to output + -c <char> comments are started with <char> instead of ! + -u allow unreasonable stuff + -p print line numbers + -d print debugging stats + -s semi colons start comments + -a use alternate syntax + Pseudo ops can start with or without a . + Labels have to be in first column. + -I specify include dir + Macro arg parameters subsituted by name, don't need the &. + String can start with ' too. + Strings can be surrounded by <..> + A %<exp> in a string evaluates the expression + Literal char in a string with ! + + +*/ + +#include "config.h" + +#include <stdio.h> +#include <string.h> +#include <getopt.h> +#include <ctype.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef NEED_MALLOC_DECLARATION +extern char *malloc (); +#endif + +#include "ansidecl.h" +#include "libiberty.h" +#include "sb.h" +#include "macro.h" +#include "asintl.h" + +char *program_version = "1.2"; + +/* This is normally declared in as.h, but we don't include that. We + need the function because other files linked with gasp.c might call + it. */ +extern void as_abort PARAMS ((const char *, int, const char *)); + +#define MAX_INCLUDES 30 /* Maximum include depth */ +#define MAX_REASONABLE 1000 /* Maximum number of expansions */ + +int unreasonable; /* -u on command line */ +int stats; /* -d 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 alternate = 0; /* -a on command line */ +int mri = 0; /* -M on command line */ +char comment_char = '!'; +int radix = 10; /* Default radix */ + +int had_end; /* Seen .END */ + +/* The output stream */ +FILE *outfile; + +/* 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 COMMENTBIT 16 +#define BASEBIT 32 +#define ISCOMMENTCHAR(x) (chartype[(unsigned char)(x)] & COMMENTBIT) +#define ISFIRSTCHAR(x) (chartype[(unsigned char)(x)] & FIRSTBIT) +#define ISNEXTCHAR(x) (chartype[(unsigned char)(x)] & NEXTBIT) +#define ISSEP(x) (chartype[(unsigned char)(x)] & SEPBIT) +#define ISWHITE(x) (chartype[(unsigned char)(x)] & WHITEBIT) +#define ISBASE(x) (chartype[(unsigned char)(x)] & BASEBIT) +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) + +/* Include file list */ + +typedef struct include_path +{ + struct include_path *next; + sb path; +} include_path; + +include_path *paths_head; +include_path *paths_tail; + + +static void quit PARAMS ((void)); +static void hash_new_table PARAMS ((int, hash_table *)); +static int hash PARAMS ((sb *)); +static hash_entry *hash_create PARAMS ((hash_table *, sb *)); +static void hash_add_to_string_table PARAMS ((hash_table *, sb *, sb *, int)); +static void hash_add_to_int_table PARAMS ((hash_table *, sb *, int)); +static hash_entry *hash_lookup PARAMS ((hash_table *, sb *)); +static void checkconst PARAMS ((int, exp_t *)); +static int sb_strtol PARAMS ((int, sb *, int, int *)); +static int level_0 PARAMS ((int, sb *, exp_t *)); +static int level_1 PARAMS ((int, sb *, exp_t *)); +static int level_2 PARAMS ((int, sb *, exp_t *)); +static int level_3 PARAMS ((int, sb *, exp_t *)); +static int level_4 PARAMS ((int, sb *, exp_t *)); +static int level_5 PARAMS ((int, sb *, exp_t *)); +static int exp_parse PARAMS ((int, sb *, exp_t *)); +static void exp_string PARAMS ((exp_t *, sb *)); +static int exp_get_abs PARAMS ((const char *, int, sb *, int *)); +#if 0 +static void strip_comments PARAMS ((sb *)); +#endif +static void unget PARAMS ((int)); +static void include_buf PARAMS ((sb *, sb *, include_type, int)); +static void include_print_where_line PARAMS ((FILE *)); +static void include_print_line PARAMS ((FILE *)); +static int get_line PARAMS ((sb *)); +static int grab_label PARAMS ((sb *, sb *)); +static void change_base PARAMS ((int, sb *, sb *)); +static void do_end PARAMS ((sb *)); +static void do_assign PARAMS ((int, int, sb *)); +static void do_radix PARAMS ((sb *)); +static int get_opsize PARAMS ((int, sb *, int *)); +static int eol PARAMS ((int, sb *)); +static void do_data PARAMS ((int, sb *, int)); +static void do_datab PARAMS ((int, sb *)); +static void do_align PARAMS ((int, sb *)); +static void do_res PARAMS ((int, sb *, int)); +static void do_export PARAMS ((sb *)); +static void do_print PARAMS ((int, sb *)); +static void do_heading PARAMS ((int, sb *)); +static void do_page PARAMS ((void)); +static void do_form PARAMS ((int, sb *)); +static int get_any_string PARAMS ((int, sb *, sb *, int, int)); +static int skip_openp PARAMS ((int, sb *)); +static int skip_closep PARAMS ((int, sb *)); +static int dolen PARAMS ((int, sb *, sb *)); +static int doinstr PARAMS ((int, sb *, sb *)); +static int dosubstr PARAMS ((int, sb *, sb *)); +static void process_assigns PARAMS ((int, sb *, sb *)); +static int get_and_process PARAMS ((int, sb *, sb *)); +static void process_file PARAMS ((void)); +static void free_old_entry PARAMS ((hash_entry *)); +static void do_assigna PARAMS ((int, sb *)); +static void do_assignc PARAMS ((int, sb *)); +static void do_reg PARAMS ((int, sb *)); +static int condass_lookup_name PARAMS ((sb *, int, sb *, int)); +static int whatcond PARAMS ((int, sb *, int *)); +static int istrue PARAMS ((int, sb *)); +static void do_aif PARAMS ((int, sb *)); +static void do_aelse PARAMS ((void)); +static void do_aendi PARAMS ((void)); +static int condass_on PARAMS ((void)); +static void do_if PARAMS ((int, sb *, int)); +static int get_mri_string PARAMS ((int, sb *, sb *, int)); +static void do_ifc PARAMS ((int, sb *, int)); +static void do_aendr PARAMS ((void)); +static void do_awhile PARAMS ((int, sb *)); +static void do_aendw PARAMS ((void)); +static void do_exitm PARAMS ((void)); +static void do_arepeat PARAMS ((int, sb *)); +static void do_endm PARAMS ((void)); +static void do_irp PARAMS ((int, sb *, int)); +static void do_local PARAMS ((int, sb *)); +static void do_macro PARAMS ((int, sb *)); +static int macro_op PARAMS ((int, sb *)); +static int getstring PARAMS ((int, sb *, sb *)); +static void do_sdata PARAMS ((int, sb *, int)); +static void do_sdatab PARAMS ((int, sb *)); +static int new_file PARAMS ((const char *)); +static void do_include PARAMS ((int, sb *)); +static void include_pop PARAMS ((void)); +static int get PARAMS ((void)); +static int linecount PARAMS ((void)); +static int include_next_index PARAMS ((void)); +static void chartype_init PARAMS ((void)); +static int process_pseudo_op PARAMS ((int, sb *, sb *)); +static void add_keyword PARAMS ((const char *, int)); +static void process_init PARAMS ((void)); +static void do_define PARAMS ((const char *)); +static void show_usage PARAMS ((FILE *, int)); +static void show_help PARAMS ((void)); + +#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. */ +static void +quit () +{ + int exitcode; + if (fatals + errors) + exitcode = 1; + else + exitcode = 0; + + if (stats) + { + int i; + for (i = 0; i < sb_max_power_two; i++) + { + fprintf (stderr, "strings size %8d : %d\n", 1<<i, string_count[i]); + } + } + exit (exitcode); +} + +/* 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; +{ + int i; + ptr->size = size; + ptr->table = (hash_entry **) xmalloc (size * (sizeof (hash_entry *))); + /* Fill with null-pointer, not zero-bit-pattern. */ + for (i = 0; i < size; i++) + ptr->table[i] = 0; +} + +/* 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, _("redefinition not allowed\n"))); + } + + ptr->type = hash_string; + 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) + int 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_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 ((unsigned char) 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) + const 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 +static void +strip_comments (out) + sb *out; +{ + char *s = out->ptr; + int i = 0; + for (i = 0; i < out->len; i++) + { + if (ISCOMMENTCHAR(s[i])) + { + out->len = i; + return; + } + } +} +#endif + +/* push back character ch so that it can be read again. */ + +static 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 - 1); + 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 (comment_char, 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); + ch = '\n'; + } + else + more = 0; + break; + } + + if (copysource) + { + putc (ch, outfile); + } + + if (ch == '\n') + { + ch = get (); + online = 0; + if (ch == '+') + { + /* continued line */ + if (copysource) + { + putc (comment_char, 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]) || in->ptr[i] == '\\') + { + sb_add_char (out, in->ptr[i]); + i++; + while ((ISNEXTCHAR (in->ptr[i]) + || in->ptr[i] == '\\' + || 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 (in->ptr[idx] == '\\' + && idx + 1 < in->len + && in->ptr[idx + 1] == '(') + { + idx += 2; + while (idx < in->len + && in->ptr[idx] != ')') + { + sb_add_char (out, in->ptr[idx]); + idx++; + } + if (idx < in->len) + idx++; + } + else if (idx < in->len - 1 && in->ptr[idx + 1] == '\'' && ! mri) + { + 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 ((unsigned char) 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 if (in->ptr[idx] == '"' || in->ptr[idx] == '\'') + { + char tchar = in->ptr[idx]; + /* copy entire names through quickly */ + sb_add_char (out, in->ptr[idx]); + idx++; + while (idx < in->len && in->ptr[idx] != tchar) + { + 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 (in) + sb *in; +{ + had_end = 1; + if (mri) + fprintf (outfile, "%s\n", sb_name (in)); +} + +/* .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; + case ' ': + case '\t': + break; + default: + ERROR ((stderr, _("size must be one of b, w or l, is %c.\n"), in->ptr[idx])); + break; + } + idx++; + + return idx; +} + +static +int eol(idx, line) + int idx; + sb *line; +{ + idx = sb_skip_white (idx, line); + if (idx < line->len + && ISCOMMENTCHAR(line->ptr[idx])) + return 1; + if (idx >= line->len) + return 1; + return 0; +} + +/* .data [.b|.w|.l] <data>* + or d[bwl] <data>* */ + +static void +do_data (idx, in, size) + int idx; + sb *in; + int size; +{ + int opsize = 4; + char *opname = ".yikes!"; + sb acc; + sb_new (&acc); + + if (!size) + { + idx = get_opsize (idx, in, &opsize); + } + else { + opsize = size; + } + switch (opsize) + { + case 4: + opname = ".long"; + break; + case 2: + opname = ".short"; + break; + case 1: + opname = ".byte"; + break; + } + + + fprintf (outfile, "%s\t", opname); + + idx = sb_skip_white (idx, in); + + if (alternate + && idx < in->len + && in->ptr[idx] == '"') + { + int i; + idx = getstring (idx, in, &acc); + for (i = 0; i < acc.len; i++) + { + if (i) + fprintf(outfile,","); + fprintf (outfile, "%d", acc.ptr[i]); + } + } + else + { + while (!eol (idx, in)) + { + 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); + sb_print_at (outfile, idx, in); + 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> */ + +static void +do_align (idx, in) + int idx; + sb *in; +{ + int al, have_fill, fill; + + idx = exp_get_abs (_("align needs absolute expression.\n"), idx, in, &al); + idx = sb_skip_white (idx, in); + have_fill = 0; + fill = 0; + if (! eol (idx, in)) + { + idx = sb_skip_comma (idx, in); + idx = exp_get_abs (_(".align needs absolute fill value.\n"), idx, in, + &fill); + have_fill = 1; + } + + if (al != 1 + && al != 2 + && al != 4) + WARNING ((stderr, _("alignment must be one of 1, 2 or 4.\n"))); + + fprintf (outfile, ".align %d", al); + if (have_fill) + fprintf (outfile, ",%d", fill); + fprintf (outfile, "\n"); +} + +/* .res[.b|.w|.l] <size> */ + +static void +do_res (idx, in, type) + int idx; + sb *in; + int type; +{ + int size = 4; + int count = 0; + + idx = get_opsize (idx, in, &size); + while (!eol(idx, in)) + { + 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 */ + +static void +do_export (in) + sb *in; +{ + fprintf (outfile, ".global %s\n", sb_name (in)); +} + +/* .print [list] [nolist] */ + +static void +do_print (idx, in) + int idx; + sb *in; +{ + idx = sb_skip_white (idx, in); + while (idx < in->len) + { + if (strncasecmp (in->ptr + idx, "LIST", 4) == 0) + { + fprintf (outfile, ".list\n"); + idx += 4; + } + else if (strncasecmp (in->ptr + idx, "NOLIST", 6) == 0) + { + fprintf (outfile, ".nolist\n"); + idx += 6; + } + idx++; + } +} + +/* .head */ +static 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 */ + +static void +do_page () +{ + fprintf (outfile, ".eject\n"); +} + +/* .form [lin=<value>] [col=<value>] */ +static 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 (strncasecmp (in->ptr + idx, "LIN=", 4) == 0) + { + idx += 4; + idx = exp_get_abs (_("form LIN= needs absolute expresssion.\n"), idx, in, &lines); + } + + if (strncasecmp (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); + +} + + +/* Fetch string from the input stream, + rules: + 'Bxyx<whitespace> -> return 'Bxyza + %<char> -> return string of decimal value of x + "<string>" -> return string + xyx<whitespace> -> return xyz +*/ +static int +get_any_string (idx, in, out, expand, pretend_quoted) + int idx; + sb *in; + sb *out; + int expand; + int pretend_quoted; +{ + sb_reset (out); + idx = sb_skip_white (idx, in); + + if (idx < in->len) + { + if (in->len > 2 && in->ptr[idx+1] == '\'' && ISBASE (in->ptr[idx])) + { + while (!ISSEP (in->ptr[idx])) + sb_add_char (out, in->ptr[idx++]); + } + else if (in->ptr[idx] == '%' + && alternate + && expand) + { + int val; + char buf[20]; + /* Turns the next expression into a string */ + idx = exp_get_abs (_("% operator needs absolute expression"), + idx + 1, + in, + &val); + sprintf(buf, "%d", val); + sb_add_string (out, buf); + } + else if (in->ptr[idx] == '"' + || in->ptr[idx] == '<' + || (alternate && in->ptr[idx] == '\'')) + { + if (alternate && expand) + { + /* Keep the quotes */ + sb_add_char (out, '\"'); + + idx = getstring (idx, in, out); + sb_add_char (out, '\"'); + + } + else { + idx = getstring (idx, in, out); + } + } + else + { + while (idx < in->len + && (in->ptr[idx] == '"' + || in->ptr[idx] == '\'' + || pretend_quoted + || !ISSEP (in->ptr[idx]))) + { + if (in->ptr[idx] == '"' + || in->ptr[idx] == '\'') + { + char tchar = in->ptr[idx]; + sb_add_char (out, in->ptr[idx++]); + while (idx < in->len + && in->ptr[idx] != tchar) + sb_add_char (out, in->ptr[idx++]); + if (idx == in->len) + return 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 */ + +static 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 */ + +static 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 */ + +static 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 ((unsigned char) 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 */ +static void +process_assigns (idx, in, buf) + int idx; + sb *in; + sb *buf; +{ + while (idx < in->len) + { + hash_entry *ptr; + if (in->ptr[idx] == '\\' + && idx + 1 < in->len + && in->ptr[idx + 1] == '(') + { + do + { + sb_add_char (buf, in->ptr[idx]); + idx++; + } + while (idx < in->len && in->ptr[idx - 1] != ')'); + } + else if (in->ptr[idx] == '\\' + && idx + 1 < in->len + && in->ptr[idx + 1] == '&') + { + idx = condass_lookup_name (in, idx + 2, buf, 1); + } + else if (in->ptr[idx] == '\\' + && idx + 1 < in->len + && in->ptr[idx + 1] == '$') + { + idx = condass_lookup_name (in, idx + 2, buf, 0); + } + else if (idx + 3 < in->len + && in->ptr[idx] == '.' + && toupper ((unsigned char) in->ptr[idx + 1]) == 'L' + && toupper ((unsigned char) in->ptr[idx + 2]) == 'E' + && toupper ((unsigned char) in->ptr[idx + 3]) == 'N') + idx = dolen (idx + 4, in, buf); + else if (idx + 6 < in->len + && in->ptr[idx] == '.' + && toupper ((unsigned char) in->ptr[idx + 1]) == 'I' + && toupper ((unsigned char) in->ptr[idx + 2]) == 'N' + && toupper ((unsigned char) in->ptr[idx + 3]) == 'S' + && toupper ((unsigned char) in->ptr[idx + 4]) == 'T' + && toupper ((unsigned char) in->ptr[idx + 5]) == 'R') + idx = doinstr (idx + 6, in, buf); + else if (idx + 7 < in->len + && in->ptr[idx] == '.' + && toupper ((unsigned char) in->ptr[idx + 1]) == 'S' + && toupper ((unsigned char) in->ptr[idx + 2]) == 'U' + && toupper ((unsigned char) in->ptr[idx + 3]) == 'B' + && toupper ((unsigned char) in->ptr[idx + 4]) == 'S' + && toupper ((unsigned char) in->ptr[idx + 5]) == 'T' + && toupper ((unsigned char) 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, 1, 0); + process_assigns (0, &t, out); + sb_kill (&t); + return idx; +} + +static +void +process_file () +{ + sb line; + sb t1, t2; + sb acc; + sb label_in; + int more; + + sb_new (&line); + sb_new (&t1); + sb_new (&t2); + sb_new(&acc); + sb_new (&label_in); + 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 if (mri + && (line.ptr[0] == '*' + || line.ptr[0] == '!')) + { + /* MRI line comment. */ + fprintf (outfile, sb_name (&line)); + } + else + { + l = grab_label (&line, &label_in); + sb_reset (&label); + + if (line.ptr[l] == ':') + l++; + while (ISWHITE (line.ptr[l]) && l < line.len) + l++; + + if (label_in.len) + { + int do_assigns; + + /* Munge the label, unless this is EQU or ASSIGN. */ + do_assigns = 1; + if (l < line.len + && (line.ptr[l] == '.' || alternate || mri)) + { + int lx = l; + + if (line.ptr[lx] == '.') + ++lx; + if (lx + 3 <= line.len + && strncasecmp ("EQU", line.ptr + lx, 3) == 0 + && (lx + 3 == line.len + || ! ISFIRSTCHAR (line.ptr[lx + 3]))) + do_assigns = 0; + else if (lx + 6 <= line.len + && strncasecmp ("ASSIGN", line.ptr + lx, 6) == 0 + && (lx + 6 == line.len + || ! ISFIRSTCHAR (line.ptr[lx + 6]))) + do_assigns = 0; + } + + if (do_assigns) + process_assigns (0, &label_in, &label); + else + sb_add_sb (&label, &label_in); + } + + if (l < 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)); + } + } + } + } + else { + /* Only a label on this line */ + if (label.len && condass_on()) + { + fprintf (outfile, "%s:\n", sb_name (&label)); + } + } + } + + if (had_end) + break; + sb_reset (&line); + more = get_line (&line); + } + + if (!had_end && !mri) + 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> */ + +static 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> */ + +static 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; + if (!mri) + idx = skip_openp (idx, in); + else + idx = sb_skip_white (idx, in); + sb_new (&what); + while (idx < in->len + && (mri + ? ! eol (idx, in) + : 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, warn) + sb *inbuf; + int idx; + sb *out; + int warn; +{ + 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) + { + if (warn) + { + WARNING ((stderr, _("Can't find preprocessor variable %s.\n"), sb_name (&condass_acc))); + } + else + { + sb_add_string (out, "0"); + } + } + 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 + +static int +whatcond (idx, in, val) + int idx; + sb *in; + int *val; +{ + int cond; + + idx = sb_skip_white (idx, in); + cond = NEVER; + if (idx + 1 < in->len) + { + char *p; + char a, b; + + p = in->ptr + idx; + a = toupper ((unsigned char) p[0]); + b = toupper ((unsigned char) p[1]); + if (a == 'E' && b == 'Q') + cond = EQ; + else if (a == 'N' && b == 'E') + cond = NE; + else if (a == 'L' && b == 'T') + cond = LT; + else if (a == 'L' && b == 'E') + cond = LE; + else if (a == 'G' && b == 'T') + cond = GT; + else if (a == 'G' && b == 'E') + cond = GE; + } + if (cond == NEVER) + { + ERROR ((stderr, _("Comparison operator must be one of EQ, NE, LT, LE, GT or GE.\n"))); + cond = NEVER; + } + idx = sb_skip_white (idx + 2, in); + *val = cond; + return idx; +} + +static 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\n"))); + 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) + { + default: + res = 42; + break; + 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 = ifstack[ifi-1].on ? istrue (idx, in) : 0; + ifstack[ifi].hadelse = 0; +} + + +/* .AELSE */ +static void +do_aelse () +{ + ifstack[ifi].on = ifstack[ifi-1].on ? !ifstack[ifi].on : 0; + 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; +} + +/* MRI IFEQ, IFNE, IFLT, IFLE, IFGE, IFGT. */ + +static void +do_if (idx, in, cond) + int idx; + sb *in; + int cond; +{ + int val; + int res; + + if (ifi >= IFNESTING) + { + FATAL ((stderr, _("IF nesting unreasonable.\n"))); + } + + idx = exp_get_abs (_("Conditional operator must have absolute operands.\n"), + idx, in, &val); + switch (cond) + { + default: + case EQ: res = val == 0; break; + case NE: res = val != 0; break; + case LT: res = val < 0; break; + case LE: res = val <= 0; break; + case GE: res = val >= 0; break; + case GT: res = val > 0; break; + } + + ifi++; + ifstack[ifi].on = ifstack[ifi-1].on ? res: 0; + ifstack[ifi].hadelse = 0; +} + +/* Get a string for the MRI IFC or IFNC pseudo-ops. */ + +static int +get_mri_string (idx, in, val, terminator) + int idx; + sb *in; + sb *val; + int terminator; +{ + idx = sb_skip_white (idx, in); + + if (idx < in->len + && in->ptr[idx] == '\'') + { + sb_add_char (val, '\''); + for (++idx; idx < in->len; ++idx) + { + sb_add_char (val, in->ptr[idx]); + if (in->ptr[idx] == '\'') + { + ++idx; + if (idx >= in->len + || in->ptr[idx] != '\'') + break; + } + } + idx = sb_skip_white (idx, in); + } + else + { + int i; + + while (idx < in->len + && in->ptr[idx] != terminator) + { + sb_add_char (val, in->ptr[idx]); + ++idx; + } + i = val->len - 1; + while (i >= 0 && ISWHITE (val->ptr[i])) + --i; + val->len = i + 1; + } + + return idx; +} + +/* MRI IFC, IFNC. */ + +static void +do_ifc (idx, in, ifnc) + int idx; + sb *in; + int ifnc; +{ + sb first; + sb second; + int res; + + if (ifi >= IFNESTING) + { + FATAL ((stderr, _("IF nesting unreasonable.\n"))); + } + + sb_new (&first); + sb_new (&second); + + idx = get_mri_string (idx, in, &first, ','); + + if (idx >= in->len || in->ptr[idx] != ',') + { + ERROR ((stderr, _("Bad format for IF or IFNC.\n"))); + return; + } + + idx = get_mri_string (idx + 1, in, &second, ';'); + + res = (first.len == second.len + && strncmp (first.ptr, second.ptr, first.len) == 0); + res ^= ifnc; + + ifi++; + ifstack[ifi].on = ifstack[ifi-1].on ? res : 0; + ifstack[ifi].hadelse = 0; +} + +/* .ENDR */ +static void +do_aendr () +{ + if (!mri) + ERROR ((stderr, _("AENDR without a AREPEAT.\n"))); + else + ERROR ((stderr, _("ENDR without a REPT.\n"))); +} + +/* .AWHILE */ + +static +void +do_awhile (idx, in) + int idx; + sb *in; +{ + int line = linecount (); + sb exp; + sb sub; + int doit; + + sb_new (&sub); + sb_new (&exp); + + process_assigns (idx, in, &exp); + doit = istrue (0, &exp); + + if (! buffer_and_nest ("AWHILE", "AENDW", &sub, get_line)) + FATAL ((stderr, _("AWHILE without a AENDW at %d.\n"), line - 1)); + + /* 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; +{ + int line = linecount (); + sb exp; /* buffer with expression in it */ + sb copy; /* expanded repeat block */ + sb sub; /* contents of AREPEAT */ + int rc; + int ret; + 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); + if (!mri) + ret = buffer_and_nest ("AREPEAT", "AENDR", &sub, get_line); + else + ret = buffer_and_nest ("REPT", "ENDR", &sub, get_line); + if (! ret) + FATAL ((stderr, _("AREPEAT without a AENDR at %d.\n"), line - 1)); + 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) + { + if (!mri) + sprintf (buffer, "\t.AREPEAT %d\n", rc - 1); + else + sprintf (buffer, "\tREPT %d\n", rc - 1); + sb_add_string (©, buffer); + sb_add_sb (©, &sub); + if (!mri) + sb_add_string (©, " .AENDR\n"); + else + sb_add_string (©, " ENDR\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"))); +} + +/* MRI IRP pseudo-op. */ + +static void +do_irp (idx, in, irpc) + int idx; + sb *in; + int irpc; +{ + const char *err; + sb out; + + sb_new (&out); + + err = expand_irp (irpc, idx, in, &out, get_line, comment_char); + if (err != NULL) + ERROR ((stderr, "%s\n", err)); + + fprintf (outfile, "%s", sb_terminate (&out)); + + sb_kill (&out); +} + +/* MACRO PROCESSING */ + +/* Parse off LOCAL n1, n2,... Invent a label name for it */ +static +void +do_local (idx, line) + int idx; + sb *line; +{ + ERROR ((stderr, _("LOCAL outside of MACRO"))); +} + +static void +do_macro (idx, in) + int idx; + sb *in; +{ + const char *err; + int line = linecount (); + + err = define_macro (idx, in, &label, get_line, (const char **) NULL); + if (err != NULL) + ERROR ((stderr, _("macro at line %d: %s\n"), line - 1, err)); +} + +static int +macro_op (idx, in) + int idx; + sb *in; +{ + const char *err; + sb out; + sb name; + + if (! macro_defined) + return 0; + + sb_terminate (in); + if (! check_macro (in->ptr + idx, &out, comment_char, &err)) + return 0; + + if (err != NULL) + ERROR ((stderr, "%s\n", err)); + + sb_new (&name); + sb_add_string (&name, _("macro expansion")); + + include_buf (&name, &out, include_macro, include_next_index ()); + + sb_kill (&name); + sb_kill (&out); + + return 1; +} + +/* 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] == '<' + || (in->ptr[idx] == '\'' && alternate))) + { + if (in->ptr[idx] == '<') + { + if (alternate || mri) + { + int nest = 0; + idx++; + while ((in->ptr[idx] != '>' || nest) + && idx < in->len) + { + if (in->ptr[idx] == '!') + { + idx++ ; + sb_add_char (acc, in->ptr[idx++]); + } + else { + if (in->ptr[idx] == '>') + nest--; + if (in->ptr[idx] == '<') + nest++; + sb_add_char (acc, in->ptr[idx++]); + } + } + idx++; + } + else { + 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] == '"' || in->ptr[idx] == '\'') + { + char tchar = in->ptr[idx]; + idx++; + while (idx < in->len) + { + if (alternate && in->ptr[idx] == '!') + { + idx++ ; + sb_add_char (acc, in->ptr[idx++]); + } + else { + if (in->ptr[idx] == tchar) + { + idx++; + if (idx >= in->len || in->ptr[idx] != tchar) + 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; + int type; +{ + int nc = 0; + int pidx = -1; + sb acc; + sb_new (&acc); + fprintf (outfile, ".byte\t"); + + while (!eol (idx, in)) + { + int i; + sb_reset (&acc); + idx = sb_skip_white (idx, in); + while (!eol (idx, in)) + { + pidx = idx = get_any_string (idx, in, &acc, 0, 1); + 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_comma (idx, in); + if (idx == pidx) break; + } + if (!alternate && 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 (outfile, &acc); + fprintf (outfile, "\n"); + } + sb_kill (&acc); + +} + +static int +new_file (name) + const char *name; +{ + FILE *newone = fopen (name, "r"); + if (!newone) + return 0; + + if (isp == MAX_INCLUDES) + FATAL ((stderr, _("Unreasonable include depth (%ld).\n"), (long) 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; + sb cat; + include_path *includes; + + sb_new (&t); + sb_new (&cat); + + if (! mri) + idx = getstring (idx, in, &t); + else + { + idx = sb_skip_white (idx, in); + while (idx < in->len && ! ISWHITE (in->ptr[idx])) + { + sb_add_char (&t, in->ptr[idx]); + ++idx; + } + } + + for (includes = paths_head; includes; includes = includes->next) + { + sb_reset (&cat); + sb_add_sb (&cat, &includes->path); + sb_add_char (&cat, '/'); + sb_add_sb (&cat, &t); + if (new_file (sb_name (&cat))) + { + break; + } + } + if (!includes) + { + if (! new_file (sb_name (&t))) + FATAL ((stderr, _("Can't open include file `%s'.\n"), sb_name (&t))); + } + sb_kill (&cat); + 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 (mri && 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 == 'b' || x == 'B' + || x == 'q' || x == 'Q' + || x == 'h' || x == 'H' + || x == 'd' || x == 'D') + chartype [x] |= BASEBIT; + + if (x == ' ' || x == '\t') + chartype[x] |= WHITEBIT; + + if (x == comment_char) + chartype[x] |= COMMENTBIT; + } +} + + + +/* 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 (PROCESS|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) +#define K_ALTERNATE (41) +#define K_DB (LAB|PROCESS|42) +#define K_DW (LAB|PROCESS|43) +#define K_DL (LAB|PROCESS|44) +#define K_LOCAL (45) +#define K_IFEQ (PROCESS|46) +#define K_IFNE (PROCESS|47) +#define K_IFLT (PROCESS|48) +#define K_IFLE (PROCESS|49) +#define K_IFGE (PROCESS|50) +#define K_IFGT (PROCESS|51) +#define K_IFC (PROCESS|52) +#define K_IFNC (PROCESS|53) +#define K_IRP (PROCESS|54) +#define K_IRPC (PROCESS|55) + + +struct keyword +{ + char *name; + int code; + int extra; +}; + +static struct keyword kinfo[] = +{ + { "EQU", K_EQU, 0 }, + { "ALTERNATE", K_ALTERNATE, 0 }, + { "ASSIGN", K_ASSIGN, 0 }, + { "REG", K_REG, 0 }, + { "ORG", K_ORG, 0 }, + { "RADIX", K_RADIX, 0 }, + { "DATA", K_DATA, 0 }, + { "DB", K_DB, 0 }, + { "DW", K_DW, 0 }, + { "DL", K_DL, 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 }, + { "ALTERNATE", K_ALTERNATE, 0 }, + { "LOCAL", K_LOCAL, 0 }, + { NULL, 0, 0 } +}; + +/* Although the conditional operators are handled by gas, we need to + handle them here as well, in case they are used in a recursive + macro to end the recursion. */ + +static struct keyword mrikinfo[] = +{ + { "IFEQ", K_IFEQ, 0 }, + { "IFNE", K_IFNE, 0 }, + { "IFLT", K_IFLT, 0 }, + { "IFLE", K_IFLE, 0 }, + { "IFGE", K_IFGE, 0 }, + { "IFGT", K_IFGT, 0 }, + { "IFC", K_IFC, 0 }, + { "IFNC", K_IFNC, 0 }, + { "ELSEC", K_AELSE, 0 }, + { "ENDC", K_AENDI, 0 }, + { "MEXIT", K_EXITM, 0 }, + { "REPT", K_AREPEAT, 0 }, + { "IRP", K_IRP, 0 }, + { "IRPC", K_IRPC, 0 }, + { "ENDR", K_AENDR, 0 }, + { NULL, 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; +{ + int oidx = idx; + + if (line->ptr[idx] == '.' || alternate || mri) + { + /* Scan forward and find pseudo name */ + char *in; + hash_entry *ptr; + + char *s; + char *e; + if (line->ptr[idx] == '.') + idx++; + in = line->ptr + idx; + s = in; + e = s; + sb_reset (acc); + + while (idx < line->len && *e && ISFIRSTCHAR (*e)) + { + sb_add_char (acc, *e); + e++; + idx++; + } + + ptr = hash_lookup (&keyword_hash_table, acc); + + if (!ptr) + { +#if 0 + /* This one causes lots of pain when trying to preprocess + ordinary code */ + WARNING ((stderr, _("Unrecognised pseudo op `%s'.\n"), sb_name (acc))); +#endif + 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 (mri && ptr->value.i == K_END) + { + sb t; + + sb_new (&t); + sb_add_buffer (&t, line->ptr + oidx, idx - oidx); + fprintf (outfile, "\t%s", sb_name (&t)); + sb_kill (&t); + } + + if (ptr->value.i & PROCESS) + { + /* Polish the rest of the line before handling the pseudo op */ +#if 0 + strip_comments(line); +#endif + 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_AIF: + do_aif (idx, line); + break; + case K_AELSE: + do_aelse (); + break; + case K_AENDI: + do_aendi (); + break; + } + return 1; + } + else + { + switch (ptr->value.i) + { + case K_ALTERNATE: + alternate = 1; + macro_init (1, mri, 0, exp_get_abs); + return 1; + 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_DB: + do_data (idx, line, 1); + return 1; + case K_DW: + do_data (idx, line, 2); + return 1; + case K_DL: + do_data (idx, line, 4); + return 1; + case K_DATA: + do_data (idx, line, 0); + 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 (0, 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 (1, 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_LOCAL: + do_local (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 (line); + 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; + case K_IFEQ: + do_if (idx, line, EQ); + return 1; + case K_IFNE: + do_if (idx, line, NE); + return 1; + case K_IFLT: + do_if (idx, line, LT); + return 1; + case K_IFLE: + do_if (idx, line, LE); + return 1; + case K_IFGE: + do_if (idx, line, GE); + return 1; + case K_IFGT: + do_if (idx, line, GT); + return 1; + case K_IFC: + do_ifc (idx, line, 0); + return 1; + case K_IFNC: + do_ifc (idx, line, 1); + return 1; + case K_IRP: + do_irp (idx, line, 0); + return 1; + case K_IRPC: + do_irp (idx, line, 1); + return 1; + } + } + } + return 0; +} + + + +/* Add a keyword to the hash table. */ + +static void +add_keyword (name, code) + const char *name; + int code; +{ + sb label; + int j; + + sb_new (&label); + sb_add_string (&label, name); + + hash_add_to_int_table (&keyword_hash_table, &label, code); + + sb_reset (&label); + for (j = 0; name[j]; j++) + sb_add_char (&label, name[j] - 'A' + 'a'); + hash_add_to_int_table (&keyword_hash_table, &label, code); + + sb_kill (&label); +} + +/* 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++) + add_keyword (kinfo[i].name, kinfo[i].code); + + if (mri) + { + for (i = 0; mrikinfo[i].name; i++) + add_keyword (mrikinfo[i].name, mrikinfo[i].code); + } +} + + +static void +do_define (string) + const char *string; +{ + sb label; + int res = 1; + hash_entry *ptr; + sb_new (&label); + + + while (*string) + { + if (*string == '=') + { + sb value; + sb_new (&value); + string++; + while (*string) + { + sb_add_char (&value, *string); + string++; + } + exp_get_abs (_("Invalid expression on command line.\n"), 0, &value, &res); + sb_kill (&value); + break; + } + sb_add_char (&label, *string); + + string ++; + } + + ptr = hash_create (&vars, &label); + free_old_entry (ptr); + ptr->type = hash_integer; + ptr->value.i = res; + sb_kill (&label); +} +char *program_name; + +/* The list of long options. */ +static struct option long_options[] = +{ + { "alternate", no_argument, 0, 'a' }, + { "include", required_argument, 0, 'I' }, + { "commentchar", required_argument, 0, 'c' }, + { "copysource", no_argument, 0, 's' }, + { "debug", no_argument, 0, 'd' }, + { "help", no_argument, 0, 'h' }, + { "mri", no_argument, 0, 'M' }, + { "output", required_argument, 0, 'o' }, + { "print", no_argument, 0, 'p' }, + { "unreasonable", no_argument, 0, 'u' }, + { "version", no_argument, 0, 'v' }, + { "define", required_argument, 0, 'd' }, + { NULL, no_argument, 0, 0 } +}; + +/* Show a usage message and exit. */ +static void +show_usage (file, status) + FILE *file; + int status; +{ + fprintf (file, _("\ +Usage: %s \n\ + [-a] [--alternate] enter alternate macro mode\n\ + [-c char] [--commentchar char] change the comment character from !\n\ + [-d] [--debug] print some debugging info\n\ + [-h] [--help] print this message\n\ + [-M] [--mri] enter MRI compatibility mode\n\ + [-o out] [--output out] set the output file\n\ + [-p] [--print] print line numbers\n"), program_name); + fprintf (file, _("\ + [-s] [--copysource] copy source through as comments \n\ + [-u] [--unreasonable] allow unreasonable nesting\n\ + [-v] [--version] print the program version\n\ + [-Dname=value] create preprocessor variable called name, with value\n\ + [-Ipath] add to include path list\n\ + [in-file]\n")); + if (status == 0) + printf (_("\nReport bugs to bug-gnu-utils@gnu.org\n")); + exit (status); +} + +/* Display a help message and exit. */ +static void +show_help () +{ + printf (_("%s: Gnu Assembler Macro Preprocessor\n"), + program_name); + show_usage (stdout, 0); +} + +int +main (argc, argv) + int argc; + char **argv; +{ + int opt; + char *out_name = 0; + sp = include_stack; + + ifstack[0].on = 1; + ifi = 0; + +#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) + setlocale (LC_MESSAGES, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + program_name = argv[0]; + xmalloc_set_program_name (program_name); + + hash_new_table (101, &keyword_hash_table); + hash_new_table (101, &assign_hash_table); + hash_new_table (101, &vars); + + sb_new (&label); + + while ((opt = getopt_long (argc, argv, "I:sdhavc:upo:D:M", long_options, + (int *) NULL)) + != EOF) + { + switch (opt) + { + case 'o': + out_name = optarg; + break; + case 'u': + unreasonable = 1; + break; + case 'I': + { + include_path *p = (include_path *) xmalloc (sizeof (include_path)); + p->next = NULL; + sb_new (&p->path); + sb_add_string (&p->path, optarg); + if (paths_tail) + paths_tail->next = p; + else + paths_head = p; + paths_tail = p; + } + break; + case 'p': + print_line_number = 1; + break; + case 'c': + comment_char = optarg[0]; + break; + case 'a': + alternate = 1; + break; + case 's': + copysource = 1; + break; + case 'd': + stats = 1; + break; + case 'D': + do_define (optarg); + break; + case 'M': + mri = 1; + comment_char = ';'; + break; + case 'h': + show_help (); + /*NOTREACHED*/ + case 'v': + /* This output is intended to follow the GNU standards document. */ + printf (_("GNU assembler pre-processor %s\n"), program_version); + printf (_("Copyright 1996 Free Software Foundation, Inc.\n")); + printf (_("\ +This program is free software; you may redistribute it under the terms of\n\ +the GNU General Public License. This program has absolutely no warranty.\n")); + exit (0); + /*NOTREACHED*/ + case 0: + break; + default: + show_usage (stderr, 1); + /*NOTREACHED*/ + } + } + + process_init (); + + macro_init (alternate, mri, 0, exp_get_abs); + + if (out_name) { + outfile = fopen (out_name, "w"); + if (!outfile) + { + fprintf (stderr, _("%s: Can't open output file `%s'.\n"), + program_name, out_name); + exit (1); + } + } + else { + outfile = stdout; + } + + chartype_init (); + if (!outfile) + outfile = stdout; + + /* Process all the input files */ + + while (optind < argc) + { + if (new_file (argv[optind])) + { + process_file (); + } + else + { + fprintf (stderr, _("%s: Can't open input file `%s'.\n"), + program_name, argv[optind]); + exit (1); + } + optind++; + } + + quit (); + return 0; +} + +/* This function is used because an abort in some of the other files + may be compiled into as_abort because they include as.h. */ + +void +as_abort (file, line, fn) + const char *file, *fn; + int line; +{ + fprintf (stderr, _("Internal error, aborting at %s line %d"), file, line); + if (fn) + fprintf (stderr, " in %s", fn); + fprintf (stderr, _("\nPlease report this bug.\n")); + exit (1); +} |