darling-gdb/gas/gasp.c
1994-04-20 22:48:12 +00:00

3519 lines
67 KiB
C

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