/* MIX simulator, copyright 1994 by Darius Bacon */
#include "mix.h"
#include "asm.h" /* this is a sign of bad factoring */
#include "symbol.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
Here's the theory on forward references.
We need to handle 3 sorts of references: ordinary symbols, local symbols
like 7B and 7F, and implicit constants like =123=. First let's discuss
ordinary symbols. Any symbol that's been referred to but not yet defined
has a list of all locations, in the object program built so far, that
refer to it. That list is added to by the forward_reference() function.
When the symbol is defined, we fix up all the references to it, using the
list. After assembly is done, we check if there are any unresolved
references, in resolve_generated_futures() (whose main job I'll get to
shortly). A minor problem: we don't hold onto the source-line number of a
reference, so if it's unresolved we get a misleading error message.
The forward_reference() function returns a meaningless value just for
historical reasons: originally the list of unresolved references for a
symbol was threaded through the simulated memory locations themselves,
and so forward_reference() had to return the value to put in the
latest referencing location to maintain the list. Now that I've come
to my senses, I suppose I should get rid of the return value.
Local symbols work just like regular ones, except that a reference to,
say, 7B, gets the value defined in the latest definition of 7H, and
similarly a reference to 7F is always a forward ref to the next definition
of 7H. This can be arranged by having define_symbol() adjust 7B and 7F
appropriately when it sees 7H.
We handle implicit constants like =123= by generating a unique symbol for
each value that occurs in such a constant. (It's of the form "=123", which
can't conflict with ordinary or local symbols.) Then a reference to such a
constant is simply a forward reference to the generated symbol. After
assembly is complete resolve_generated_futures() resolves all the generated
symbols with appropriate addresses. Note that this scheme uniquifies the
constant references for free.
*/
/* Pre: size != 0 */
static void *allot(size_t size)
{
void *result = malloc(size);
if (!result)
fatal_error("Out of heap space");
return result;
}
static int stricmp(const char *s, const char *t)
{
while (*s && *t)
if (tolower(*s) != tolower(*t))
return 1;
else
s++, t++;
return tolower(*s) != tolower(*t);
}
/* --- The symbol table --- */
static Symbol symbol_table[17];
/* Apply proc to each symbol in the symbol table. */
static void for_each_symbol(void (*proc)(Symbol))
{
unsigned i;
for (i = 0; i < sizeof symbol_table / sizeof symbol_table[0]; ++i) {
Symbol s = symbol_table[i];
for (; s; s = s->next)
proc(s);
}
}
static unsigned hash(const char *s)
{
unsigned h = 0;
for (; *s; s++)
h = (h << 5) - h + toupper(*s);
return h % (sizeof symbol_table / sizeof symbol_table[0]);
}
Symbol string_to_symbol(const char *name)
{
Symbol *bucket = symbol_table + hash(name);
Symbol p = *bucket;
for (; p; p = p->next)
if (stricmp(name, p->name) == 0)
return p;
{ /* Symbol doesn't exist yet. */
Symbol result = allot(sizeof(struct Symbol));
strcpy(result->name = allot(strlen(name) + 1), name);
result->defined = false;
result->references = NULL;
result->next = *bucket;
result->cell = zero;
*bucket = result;
return result;
}
}
Flag is_defined(Symbol symbol) { return symbol->defined; }
Cell symbol_value(Symbol symbol)
{
assert(symbol->defined);
return symbol->cell;
}
void forward_reference(Symbol symbol, Address address)
{
assert(!symbol->defined);
{
AddressList refs = allot(sizeof(struct AddressList));
refs->address = address;
refs->next = symbol->references;
symbol->references = refs;
}
}
/* Pre: !is_defined(symbol)
Set the address field of each cell in symbol's set of references
to definition. Frees the set of references. */
static void resolve_references(Symbol symbol, Cell definition)
{
AddressList refs, rest;
assert(!symbol->defined);
for (refs = symbol->references; refs; refs = rest) {
asm_store_field(refs->address, 0, 2, definition);
rest = refs->next;
free(refs);
}
}
void define_symbol(Symbol symbol, Cell cell)
{
{ /* handle local symbols */
const char *name = symbol->name;
if (isdigit(name[0]) && strlen(name) == 2) {
char c = toupper(name[1]);
if (c == 'F' || c == 'B') {
error("Definition of local ref: %s", name);
return;
} else if (c == 'H') {
char new_name[3];
Symbol nB, nF;
strcpy(new_name, name);
new_name[1] = 'B'; nB = string_to_symbol(new_name);
nB->defined = true, nB->cell = cell;
new_name[1] = 'F'; nF = string_to_symbol(new_name);
resolve_references(nF, cell);
nF->defined = false, nF->references = NULL;
return;
} else
; /* not a local; fall through */
}
}
if (symbol->defined)
warn("'%s' multiply-defined", symbol->name);
else
resolve_references(symbol, cell);
symbol->defined = true, symbol->cell = cell;
}
Symbol generate_future_sym(Cell value)
{
char name[12];
sprintf(name, "=%ld", cell_to_long(value));
return string_to_symbol(name);
}
static void resolve_if_dangling(Symbol symbol)
{
if (symbol->defined)
return;
if (symbol->name[0] == '=') { /* it's a generated symbol */
define_symbol(symbol, address_to_cell(here));
assemble(long_to_cell(atol(symbol->name + 1)));
} else { /* it's a user symbol */
if (symbol->references)
error("Unresolved reference: %s", symbol->name);
/* it would be nice to print the reference list, too */
}
}
void resolve_generated_futures(void)
{
for_each_symbol(resolve_if_dangling);
}
syntax highlighted by Code2HTML, v. 0.9.1