/* gasp.c - Gnu assembler preprocessor main program. Copyright (C) 1994, 1995 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 ] * -s copy source to output -c comments are started with 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 % in a string evaluates the expression Literal char in a string with ! */ #include "config.h" #include #include #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef NEED_MALLOC_DECLARATION extern char *malloc (); #endif #include "ansidecl.h" #include "libiberty.h" char *program_version = "1.2"; #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; /* 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<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 /* 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 sb_build PARAMS ((sb *, int)); static void sb_new PARAMS ((sb *)); static void sb_kill PARAMS ((sb *)); static void sb_add_sb PARAMS ((sb *, sb *)); static void sb_check PARAMS ((sb *, int)); static void sb_reset PARAMS ((sb *)); static void sb_add_char PARAMS ((sb *, int)); static void sb_add_string PARAMS ((sb *, const char *)); static void sb_add_buffer PARAMS ((sb *, const char *, int)); static void sb_print PARAMS ((sb *)); static void sb_print_at PARAMS ((int, sb *)); static char *sb_name PARAMS ((sb *)); static int sb_skip_white PARAMS ((int, sb *)); static int sb_skip_comma PARAMS ((int, sb *)); 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 buffer_and_nest PARAMS ((const char *, const char *, sb *)); 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 int do_formals PARAMS ((macro_entry *, int, sb *)); static void do_local PARAMS ((int, sb *)); static void do_macro PARAMS ((int, sb *)); static int get_token PARAMS ((int, sb *, sb *)); static int get_apost_token PARAMS ((int, sb *, sb *, int)); static int sub_actual PARAMS ((int, sb *, sb *, hash_table *, int, sb *, int)); static void macro_expand_body PARAMS ((sb *, sb *, sb *, formal_entry *, hash_table *)); static void macro_expand PARAMS ((sb *, int, sb *, macro_entry *)); 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 < max_power_two; i++) { fprintf (stderr, "strings size %8d : %d\n", 1<ptr[*]; sb_kill (&foo); */ /* initializes an sb. */ static 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. */ static void sb_add_char (ptr, c) sb *ptr; int 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; const 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; const 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; } } static void sb_print_at (idx, ptr) int idx; sb *ptr; { int i; for (i = idx; i < ptr->len; i++) putc (ptr->ptr[i], outfile); } /* 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; { 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, "redefintion not allowed")); } 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 (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])) { 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 (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 (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] * or d[bwl] * */ 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 (idx, in); fprintf (outfile, "\n"); } /* .datab [.b|.w|.l] , */ 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 */ static 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] */ 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=] [col=] */ 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 -> return 'Bxyza % -> return string of decimal value of x "" -> return string xyx -> 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 (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] == '\\' && in->ptr[idx + 1] == '&') { idx = condass_lookup_name (in, idx + 2, buf, 1); } else if (in->ptr[idx] == '\\' && 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 (label_in.len) { /* Munge any label */ process_assigns (0, &label_in, &label); } if (line.ptr[l] == ':') l++; while (ISWHITE (line.ptr[l]) && l < line.len) l++; 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 */ 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 */ 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; } /* 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) const char *from; const 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; if (!alternate && !mri) { /* With normal syntax we can suck what we want till we get to the dot. With the alternate, labels have to start in the first column, since we cant tell what's a label and whats a pseudoop */ /* 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] == '.' || alternate || mri)) { if (ptr->ptr[i] == '.') i++; if (strncasecmp (ptr->ptr + i, from, from_len) == 0) depth++; if (strncasecmp (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 */ 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; { 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); if (!mri) buffer_and_nest ("AREPEAT", "AENDR", &sub); else buffer_and_nest ("REPT", "ENDR", &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) { 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 *mn; sb sub; formal_entry f; hash_table h; hash_entry *p; sb name; sb out; if (irpc) mn = "IRPC"; else mn = "IRP"; idx = sb_skip_white (idx, in); sb_new (&sub); buffer_and_nest (mn, "ENDR", &sub); sb_new (&f.name); sb_new (&f.def); sb_new (&f.actual); idx = get_token (idx, in, &f.name); if (f.name.len == 0) { ERROR ((stderr, "Missing model parameter in %s", mn)); return; } hash_new_table (1, &h); p = hash_create (&h, &f.name); p->type = hash_formal; p->value.f = &f; f.index = 1; f.next = NULL; sb_new (&name); sb_add_string (&name, mn); sb_new (&out); idx = sb_skip_comma (idx, in); if (eol (idx, in)) { /* Expand once with a null string. */ macro_expand_body (&name, &sub, &out, &f, &h); fprintf (outfile, "%s", sb_name (&out)); } else { while (!eol (idx, in)) { if (!irpc) idx = get_any_string (idx, in, &f.actual, 1, 0); else { sb_reset (&f.actual); sb_add_char (&f.actual, in->ptr[idx]); ++idx; } sb_reset (&out); macro_expand_body (&name, &sub, &out, &f, &h); fprintf (outfile, "%s", sb_name (&out)); if (!irpc) idx = sb_skip_comma (idx, in); else idx = sb_skip_white (idx, in); } } sb_kill (&sub); sb_kill (&name); sb_kill (&out); } /* MACRO PROCESSING */ static int number; hash_table macro_table; /* Understand .MACRO 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, 1, 0); } } { /* 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; *p = NULL; } if (mri) { formal_entry *formal; /* Add a special NARG formal, which macro_expand will set to the number of arguments. */ formal = (formal_entry *) xmalloc (sizeof (formal_entry)); sb_new (&formal->name); sb_new (&formal->def); sb_new (&formal->actual); sb_add_string (&formal->name, "NARG"); { /* 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 = -2; *p = formal; formal->next = NULL; } return idx; } /* Parse off LOCAL n1, n2,... Invent a label name for it */ static void do_local (idx, line) int idx; sb *line; { static int ln; sb acc; sb sub; char subs[10]; sb_new (&acc); sb_new (&sub); idx = sb_skip_white (idx, line); while (!eol(idx, line)) { sb_reset (&acc); sb_reset (&sub); ln++; sprintf(subs, "LL%04x", ln); idx = get_token(idx, line, &acc); sb_add_string (&sub, subs); hash_add_to_string_table (&assign_hash_table, &acc, &sub, 1); idx = sb_skip_comma (idx, line); } sb_kill (&sub); sb_kill (&acc); } 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) { sb_add_sb (&name, &label); if (in->ptr[idx] == '(') { /* It's the label: MACRO (formals,...) sort */ idx = do_formals (macro, idx + 1, in); if (in->ptr[idx] != ')') ERROR ((stderr, "Missing ) after formals.\n")); } else { /* It's the label: MACRO formals,... sort */ idx = do_formals (macro, idx, in); } } 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++]); } } /* Ignore trailing & */ if (alternate && idx < in->len && in->ptr[idx] == '&') 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, formal_hash, kind, out, copyifnotthere) int src; sb *in; sb *t; hash_table *formal_hash; int kind; sb *out; int copyifnotthere; { /* 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 (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 if (copyifnotthere) { sb_add_sb (out, t); } else { sb_add_char (out, '\\'); sb_add_sb (out, t); } return src; } /* Copy the body from the macro buffer into a safe place and substitute any args. */ static void macro_expand_body (name, in, out, formals, formal_hash) sb *name; sb *in; sb *out; formal_entry *formals; hash_table *formal_hash; { sb t; int src = 0; int inquote = 0; sb_new (&t); while (src < in->len) { if (in->ptr[src] == '&') { sb_reset (&t); if (mri && src + 1 < in->len && in->ptr[src + 1] == '&') { src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1); } else { src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0); } } else if (in->ptr[src] == '\\') { src++; if (in->ptr[src] == comment_char) { /* 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 if (mri && isalnum ((unsigned char) in->ptr[src])) { int ind; formal_entry *f; if (isdigit ((unsigned char) in->ptr[src])) ind = in->ptr[src] - '0'; else if (isupper ((unsigned char) in->ptr[src])) ind = in->ptr[src] - 'A' + 10; else ind = in->ptr[src] - 'a' + 10; ++src; for (f = formals; f != NULL; f = f->next) { if (f->index == ind - 1) { if (f->actual.len != 0) sb_add_sb (out, &f->actual); else sb_add_sb (out, &f->def); break; } } } else { sb_reset (&t); src = sub_actual (src, in, &t, formal_hash, '\'', out, 0); } } else if (ISFIRSTCHAR (in->ptr[src]) && (alternate || mri)) { sb_reset (&t); src = sub_actual (src, in, &t, formal_hash, '\'', out, 1); } else if (ISCOMMENTCHAR (in->ptr[src]) && src + 1 < in->len && ISCOMMENTCHAR (in->ptr[src+1]) && !inquote) { /* Two comment chars in a row cause the rest of the line to be dropped. */ while (src < in->len && in->ptr[src] != '\n') src++; } else if (in->ptr[src] == '"' || (mri && in->ptr[src] == '\'')) { inquote = !inquote; sb_add_char (out, in->ptr[src++]); } else if (mri && in->ptr[src] == '=' && src + 1 < in->len && in->ptr[src + 1] == '=') { hash_entry *ptr; sb_reset (&t); src = get_token (src + 2, in, &t); ptr = hash_lookup (formal_hash, &t); if (ptr == NULL) { ERROR ((stderr, "MACRO formal argument %s does not exist.\n", sb_name (&t))); } else { if (ptr->value.f->actual.len) { sb_add_string (out, "-1"); } else { sb_add_char (out, '0'); } } } else { sb_add_char (out, in->ptr[src++]); } } sb_kill (&t); } 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; int narg = 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; if (mri) { /* The macro may be called with an optional qualifier, which may be referred to in the macro body as \0. */ if (idx < in->len && in->ptr[idx] == '.') { formal_entry *n; n = (formal_entry *) xmalloc (sizeof (formal_entry)); sb_new (&n->name); sb_new (&n->def); sb_new (&n->actual); n->index = -1; n->next = m->formals; m->formals = n; idx = get_any_string (idx + 1, in, &n->actual, 1, 0); } } /* Peel off the actuals and store them away in the hash tables' actuals */ while (!eol(idx, in)) { 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]) && (!alternate && in->ptr[scan] != '=')) scan++; if (scan < in->len && (!alternate) && 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, 0, 0); if (ptr->value.f->actual.len > 0) ++narg; } } 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) { formal_entry **pf; int c; if (!mri) { ERROR ((stderr, "Too many positional arguments.\n")); return; } f = (formal_entry *) xmalloc (sizeof (formal_entry)); sb_new (&f->name); sb_new (&f->def); sb_new (&f->actual); f->next = NULL; c = -1; for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next) if ((*pf)->index >= c) c = (*pf)->index + 1; *pf = f; f->index = c; } sb_reset (&f->actual); idx = get_any_string (idx, in, &f->actual, 1, 0); if (f->actual.len > 0) ++narg; do { f = f->next; } while (f != NULL && f->index < 0); } idx = sb_skip_comma (idx, in); } if (mri) { char buffer[20]; sb_reset (&t); sb_add_string (&t, "NARG"); ptr = hash_lookup (&m->formal_hash, &t); sb_reset (&ptr->value.f->actual); sprintf (buffer, "%d", narg); sb_add_string (&ptr->value.f->actual, buffer); } macro_expand_body (name, &m->sub, &out, m->formals, &m->formal_hash); include_buf (name, &out, include_macro, include_next_index ()); if (mri) { formal_entry **pf; /* Discard any unnamed formal arguments. */ pf = &m->formals; while (*pf != NULL) { if ((*pf)->name.len != 0) pf = &(*pf)->next; else { sb_kill (&(*pf)->name); sb_kill (&(*pf)->def); sb_kill (&(*pf)->actual); f = (*pf)->next; free (*pf); *pf = f; } } } 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] == '<' || (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] */ 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 */ 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); } 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; 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 (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_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\ [-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", program_name); 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; program_name = argv[0]; xmalloc_set_program_name (program_name); 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); 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)); 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': printf ("GNU %s version %s\n", program_name, program_version); exit (0); /*NOTREACHED*/ case 0: break; default: show_usage (stderr, 1); /*NOTREACHED*/ } } process_init (); 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; }