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