open-simh.simtools/assemble_aux.c
2021-11-14 19:20:02 +01:00

831 lines
22 KiB
C

/*
Smaller operators for assemble
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "assemble_aux.h" /* my own definitions */
#include "assemble_globals.h"
#include "macros.h"
#include "assemble.h"
#include "listing.h"
#include "symbols.h"
#include "parse.h"
/* Allocate a new section */
SECTION *new_section(
void)
{
SECTION *sect = memcheck(malloc(sizeof(SECTION)));
sect->flags = 0;
sect->size = 0;
sect->pc = 0;
sect->type = 0;
sect->sector = 0;
sect->label = NULL;
return sect;
}
/* This is called by places that are about to store some code, or
which want to manually update DOT. */
void change_dot(
TEXT_RLD *tr,
int size)
{
if (size > 0) {
if (last_dot_section != current_pc->section) {
text_define_location(tr, current_pc->section->label, &current_pc->value);
last_dot_section = current_pc->section;
last_dot_addr = current_pc->value;
}
if (last_dot_addr != current_pc->value) {
text_modify_location(tr, &current_pc->value);
last_dot_addr = current_pc->value;
}
/* Update for next time */
last_dot_addr += size;
}
if (DOT + size > current_pc->section->size)
current_pc->section->size = DOT + size;
}
/* store_word stores a word to the object file and lists it to the
listing file */
int store_word(
STREAM *str,
TEXT_RLD *tr,
int size,
unsigned word)
{
change_dot(tr, size);
list_word(str, DOT, word, size, "");
return text_word(tr, &DOT, size, word);
}
/* store_word stores a word to the object file and lists it to the
listing file */
static int store_displaced_word(
STREAM *str,
TEXT_RLD *tr,
int size,
unsigned word)
{
change_dot(tr, size);
list_word(str, DOT, word, size, "'");
return text_displaced_word(tr, &DOT, size, word);
}
static int store_global_displaced_offset_word(
STREAM *str,
TEXT_RLD *tr,
int size,
unsigned word,
char *global)
{
change_dot(tr, size);
list_word(str, DOT, word, size, "G");
return text_global_displaced_offset_word(tr, &DOT, size, word, global);
}
static int store_global_offset_word(
STREAM *str,
TEXT_RLD *tr,
int size,
unsigned word,
char *global)
{
change_dot(tr, size);
list_word(str, DOT, word, size, "G");
return text_global_offset_word(tr, &DOT, size, word, global);
}
static int store_internal_word(
STREAM *str,
TEXT_RLD *tr,
int size,
unsigned word)
{
change_dot(tr, size);
list_word(str, DOT, word, size, "'");
return text_internal_word(tr, &DOT, size, word);
}
static int store_psect_displaced_offset_word(
STREAM *str,
TEXT_RLD *tr,
int size,
unsigned word,
char *name)
{
change_dot(tr, size);
list_word(str, DOT, word, size, "'");
return text_psect_displaced_offset_word(tr, &DOT, size, word, name);
}
static int store_psect_offset_word(
STREAM *str,
TEXT_RLD *tr,
int size,
unsigned word,
char *name)
{
change_dot(tr, size);
list_word(str, DOT, word, size, "'");
return text_psect_offset_word(tr, &DOT, size, word, name);
}
int store_limits(
STREAM *str,
TEXT_RLD *tr)
{
change_dot(tr, 4);
list_word(str, DOT, 0, 2, "");
list_word(str, DOT + 2, 0, 2, "");
return text_limits(tr, &DOT);
}
/* free_addr_mode frees the storage consumed by an addr_mode */
void free_addr_mode(
ADDR_MODE *mode)
{
if (mode->offset)
free_tree(mode->offset);
mode->offset = NULL;
}
/* Get the register indicated by the expression */
unsigned get_register(
EX_TREE *expr)
{
unsigned reg;
if (expr->type == EX_LIT && expr->data.lit <= 7) {
reg = expr->data.lit;
return reg;
}
if (expr->type == EX_SYM && expr->data.symbol->section->type == SECTION_REGISTER) {
reg = expr->data.symbol->value;
return reg;
}
return NO_REG;
}
/*
implicit_gbl is a self-recursive routine that adds undefined symbols
to the "implicit globals" symbol table, or alternatively adds the
symbol as an UNDEFINED symbol.
*/
void implicit_gbl(
EX_TREE *value)
{
if (pass)
return; /* Only do this in first pass */
switch (value->type) {
case EX_UNDEFINED_SYM:
{
if (!(value->data.symbol->flags & SYMBOLFLAG_LOCAL)) {
/* Unless it's a local symbol, */
if (enabl_gbl) {
/* either make the undefined symbol into an
implicit global */
add_sym(value->data.symbol->label, 0, SYMBOLFLAG_GLOBAL,
&absolute_section, &implicit_st);
} else {
/* or add it to the undefined symbol table,
purely for listing purposes.
It also works to add it to symbol_st,
all code is carefully made for that. */
#define ADD_UNDEFINED_SYMBOLS_TO_MAIN_SYMBOL_TABLE 0
#if ADD_UNDEFINED_SYMBOLS_TO_MAIN_SYMBOL_TABLE
add_sym(value->data.symbol->label, 0, SYMBOLFLAG_UNDEFINED,
&absolute_section, &symbol_st);
#else
add_sym(value->data.symbol->label, 0, SYMBOLFLAG_UNDEFINED,
&absolute_section, &undefined_st);
#endif
}
}
}
break;
case EX_LIT:
case EX_SYM:
case EX_TEMP_SYM: // Impossible on this pass
return;
case EX_ADD:
case EX_SUB:
case EX_MUL:
case EX_DIV:
case EX_AND:
case EX_OR:
case EX_LSH:
implicit_gbl(value->data.child.right);
/* FALLS THROUGH */
case EX_COM:
case EX_NEG:
implicit_gbl(value->data.child.left);
break;
case EX_ERR:
if (value->data.child.left)
implicit_gbl(value->data.child.left);
break;
}
}
/* Done between the first and second passes */
/* Migrates the symbols from the "implicit" table into the main table. */
void migrate_implicit(
void)
{
SYMBOL_ITER iter;
SYMBOL *isym,
*sym;
for (isym = first_sym(&implicit_st, &iter); isym != NULL; isym = next_sym(&implicit_st, &iter)) {
sym = lookup_sym(isym->label, &symbol_st);
if (sym) {
continue; // It's already in there. Great.
}
isym->flags |= SYMBOLFLAG_IMPLICIT_GLOBAL;
sym = add_sym(isym->label, isym->value, isym->flags, isym->section, &symbol_st);
// Just one other thing - migrate the stmtno
sym->stmtno = isym->stmtno;
}
}
/* Done between second pass and listing */
/* Migrates the symbols from the "undefined" table into the main table. */
void migrate_undefined(
void)
{
SYMBOL_ITER iter;
SYMBOL *isym,
*sym;
for (isym = first_sym(&undefined_st, &iter); isym != NULL; isym = next_sym(&undefined_st, &iter)) {
sym = lookup_sym(isym->label, &symbol_st);
if (sym) {
continue; /* It's already in there. Great. */
}
isym->flags |= SYMBOLFLAG_UNDEFINED; /* Just in case */
sym = add_sym(isym->label, isym->value, isym->flags, isym->section, &symbol_st);
/* Just one other thing - migrate the stmtno */
sym->stmtno = isym->stmtno;
}
}
int express_sym_offset(
EX_TREE *value,
SYMBOL **sym,
unsigned *offset)
{
implicit_gbl(value); /* Translate tree's undefined syms
into global syms */
/* Internally relocatable symbols will have been summed down into
EX_TEMP_SYM's. */
if (value->type == EX_SYM || value->type == EX_TEMP_SYM) {
*sym = value->data.symbol;
*offset = 0;
return 1;
}
/* What remains is external symbols. */
if (value->type == EX_ADD) {
EX_TREE *left = value->data.child.left;
EX_TREE *right = value->data.child.right;
if ((left->type != EX_SYM && left->type != EX_UNDEFINED_SYM) || right->type != EX_LIT)
return 0; /* Failed. */
*sym = left->data.symbol;
*offset = right->data.lit;
return 1;
}
if (value->type == EX_SUB) {
EX_TREE *left = value->data.child.left;
EX_TREE *right = value->data.child.right;
if ((left->type != EX_SYM && left->type != EX_UNDEFINED_SYM) || right->type != EX_LIT)
return 0; /* Failed. */
*sym = left->data.symbol;
*offset = (unsigned) -(int) (right->data.lit);
return 1;
}
return 0;
}
/*
Translate an EX_TREE into a TEXT_COMPLEX suitable for encoding
into the object file. */
int complex_tree(
TEXT_COMPLEX *tx,
EX_TREE *tree)
{
switch (tree->type) {
case EX_LIT:
text_complex_lit(tx, tree->data.lit);
return 1;
case EX_TEMP_SYM:
case EX_SYM:
{
SYMBOL *sym = tree->data.symbol;
/* This check may not be needed; so far it made no difference. */
if (sym->flags & SYMBOLFLAG_UNDEFINED) {
return 0;
}
if (SYM_IS_IMPORTED(sym)) {
text_complex_global(tx, sym->label);
} else {
text_complex_psect(tx, sym->section->sector, sym->value);
}
}
return 1;
case EX_COM:
if (!complex_tree(tx, tree->data.child.left))
return 0;
text_complex_com(tx);
return 1;
case EX_NEG:
if (!complex_tree(tx, tree->data.child.left))
return 0;
text_complex_neg(tx);
return 1;
case EX_ADD:
if (!complex_tree(tx, tree->data.child.left))
return 0;
if (!complex_tree(tx, tree->data.child.right))
return 0;
text_complex_add(tx);
return 1;
case EX_SUB:
if (!complex_tree(tx, tree->data.child.left))
return 0;
if (!complex_tree(tx, tree->data.child.right))
return 0;
text_complex_sub(tx);
return 1;
case EX_MUL:
if (!complex_tree(tx, tree->data.child.left))
return 0;
if (!complex_tree(tx, tree->data.child.right))
return 0;
text_complex_mul(tx);
return 1;
case EX_DIV:
if (!complex_tree(tx, tree->data.child.left))
return 0;
if (!complex_tree(tx, tree->data.child.right))
return 0;
text_complex_div(tx);
return 1;
case EX_AND:
if (!complex_tree(tx, tree->data.child.left))
return 0;
if (!complex_tree(tx, tree->data.child.right))
return 0;
text_complex_and(tx);
return 1;
case EX_OR:
if (!complex_tree(tx, tree->data.child.left))
return 0;
if (!complex_tree(tx, tree->data.child.right))
return 0;
text_complex_or(tx);
return 1;
default:
return 0;
}
}
/* store a word which is represented by a complex expression. */
static void store_complex(
STREAM *refstr,
TEXT_RLD *tr,
int size,
EX_TREE *value)
{
TEXT_COMPLEX tx;
change_dot(tr, size); /* About to store - update DOT */
implicit_gbl(value); /* Turn undefined symbols into globals */
text_complex_begin(&tx); /* Open complex expression */
if (!complex_tree(&tx, value)) { /* Translate */
report(refstr, "Invalid expression (complex relocation)\n");
store_word(refstr, tr, size, 0);
} else {
list_word(refstr, DOT, 0, size, "C");
text_complex_commit(tr, &DOT, size, &tx, 0);
}
}
/* store_complex_displaced is the same as store_complex but uses the
"displaced" RLD code */
static void store_complex_displaced(
STREAM *refstr,
TEXT_RLD *tr,
int size,
EX_TREE *value)
{
TEXT_COMPLEX tx;
change_dot(tr, size);
implicit_gbl(value); /* Turn undefined symbols into globals */
text_complex_begin(&tx);
if (!complex_tree(&tx, value)) {
report(refstr, "Invalid expression (complex displaced relocation)\n");
store_word(refstr, tr, size, 0);
} else {
list_word(refstr, DOT, 0, size, "C");
text_complex_commit_displaced(tr, &DOT, size, &tx, 0);
}
}
/*
mode_extension - writes the extension word required by an addressing
mode */
void mode_extension(
TEXT_RLD *tr,
ADDR_MODE *mode,
STREAM *str)
{
EX_TREE *value = mode->offset;
SYMBOL *sym;
unsigned offset;
/* Also frees the mode. */
if (value == NULL) {
free_addr_mode(mode);
return;
}
if (value->type == EX_LIT) {
if (mode->pcrel) { /* PC-relative? */
if (current_pc->section->flags & PSECT_REL) {
store_displaced_word(str, tr, 2, value->data.lit);
} else {
/* I can compute this myself. */
store_word(str, tr, 2, value->data.lit - DOT - 2);
}
} else {
store_word(str, tr, 2, value->data.lit); /* Just a
known
value. */
}
} else if (express_sym_offset(value, &sym, &offset)) {
if (SYM_IS_IMPORTED(sym)) {
/* Reference to a global symbol. */
/* Global symbol plus offset */
if (mode->pcrel)
store_global_displaced_offset_word(str, tr, 2, offset, sym->label);
else
store_global_offset_word(str, tr, 2, offset, sym->label);
} else {
/* Relative to non-external symbol. */
if (current_pc->section == sym->section) {
/* In the same section */
if (mode->pcrel) {
/* I can compute this myself. */
store_word(str, tr, 2, sym->value + offset - DOT - 2);
} else
store_internal_word(str, tr, 2, sym->value + offset);
} else {
/* In a different section */
if (mode->pcrel)
store_psect_displaced_offset_word(str, tr, 2, sym->value + offset, sym->section->label);
else
store_psect_offset_word(str, tr, 2, sym->value + offset, sym->section->label);
}
}
} else {
/* Complex relocation */
if (mode->pcrel)
store_complex_displaced(str, tr, 2, mode->offset);
else
store_complex(str, tr, 2, mode->offset);
}
free_addr_mode(mode);
}
/* eval_defined - take an EX_TREE and returns TRUE if the tree
represents "defined" symbols. */
int eval_defined(
EX_TREE *value)
{
switch (value->type) {
case EX_LIT:
return 1;
case EX_SYM:
return 1;
case EX_UNDEFINED_SYM:
return 0;
case EX_AND:
return eval_defined(value->data.child.left) && eval_defined(value->data.child.right);
case EX_OR:
return eval_defined(value->data.child.left) || eval_defined(value->data.child.right);
default:
return 0;
}
}
/* eval_undefined - take an EX_TREE and returns TRUE if it represents
"undefined" symbols. */
int eval_undefined(
EX_TREE *value)
{
switch (value->type) {
case EX_UNDEFINED_SYM:
return 1;
case EX_SYM:
return 0;
case EX_AND:
return eval_undefined(value->data.child.left) && eval_undefined(value->data.child.right);
case EX_OR:
return eval_undefined(value->data.child.left) || eval_undefined(value->data.child.right);
default:
return 0;
}
}
/* push_cond - a new conditional (.IF) block has been activated. Push
its context. */
void push_cond(
int ok,
STREAM *str)
{
last_cond++;
assert(last_cond < MAX_CONDS);
conds[last_cond].ok = ok;
conds[last_cond].file = memcheck(strdup(str->name));
conds[last_cond].line = str->line;
}
/*
pop_cond - pop stacked conditionals. */
void pop_cond(
int to)
{
while (last_cond > to) {
free(conds[last_cond].file);
last_cond--;
}
}
/* go_section - sets current_pc to a new program section */
void go_section(
TEXT_RLD *tr,
SECTION *sect)
{
(void)tr;
if (current_pc->section == sect)
return; /* This is too easy */
/* save current PC value for old section */
current_pc->section->pc = DOT;
/* Set current section and PC value */
current_pc->section = sect;
DOT = sect->pc;
}
/*
store_value - used to store a value represented by an expression
tree into the object file. Used by do_word and .ASCII/.ASCIZ.
*/
void store_value(
STACK *stack,
TEXT_RLD *tr,
int size,
EX_TREE *value)
{
SYMBOL *sym;
unsigned offset;
implicit_gbl(value); /* turn undefined symbols into globals */
if (value->type == EX_LIT) {
store_word(stack->top, tr, size, value->data.lit);
} else if (!express_sym_offset(value, &sym, &offset)) {
store_complex(stack->top, tr, size, value);
} else {
if (SYM_IS_IMPORTED(sym)) {
store_global_offset_word(stack->top, tr, size, sym->value + offset, sym->label);
} else if (sym->section != current_pc->section) {
store_psect_offset_word(stack->top, tr, size, sym->value + offset, sym->section->label);
} else {
store_internal_word(stack->top, tr, size, sym->value + offset);
}
}
}
/* do_word - used by .WORD, .BYTE, and implied .WORD. */
int do_word(
STACK *stack,
TEXT_RLD *tr,
char *cp,
int size)
{
int comma;
if (size == 2 && (DOT & 1)) {
report(stack->top, ".WORD on odd boundary\n");
store_word(stack->top, tr, 1, 0); /* Align it */
}
cp = skipwhite(cp);
do {
if (cp[0] == ',') {
/* Empty expressions count as 0 */
store_word(stack->top, tr, size, 0);
} else {
EX_TREE *value = parse_expr(cp, 0);
if (value->type != EX_ERR && value->cp > cp) {
store_value(stack, tr, size, value);
cp = value->cp;
} else {
report(stack->top, "Invalid expression in .WORD\n");
cp = ""; /* force loop to end */
}
free_tree(value);
}
} while (cp = skipdelim_comma(cp, &comma), !EOL(*cp));
if (comma) {
/* Trailing empty expressions count as 0 */
store_word(stack->top, tr, size, 0);
}
return 1;
}
/*
check_branch - check branch distance.
*/
int check_branch(
STACK *stack,
unsigned offset,
int min,
int max)
{
int s_offset;
if (offset & 1) {
report(stack->top, "Bad branch target (odd address)\n");
}
/* Sign-extend */
if (offset & 0100000)
s_offset = offset | ~0177777;
else
s_offset = offset & 077777;
if (s_offset > max || s_offset < min) {
char temp[16];
/* printf can't do signed octal. */
my_ltoa(s_offset, temp, 8);
report(stack->top, "Branch target out of range (distance=%s)\n", temp);
return 0;
}
return 1;
}
/* write_globals writes out the GSD prior to the second assembly pass */
void write_globals(
FILE *obj)
{
GSD gsd;
SYMBOL *sym;
SECTION *psect;
SYMBOL_ITER sym_iter;
int isect;
if (obj == NULL) {
for (isect = 0; isect < sector; isect++) {
psect = sections[isect];
psect->sector = isect; /* Assign it a sector */
psect->pc = 0; /* Reset its PC for second pass */
}
return; /* Nothing more to do if no OBJ file. */
}
gsd_init(&gsd, obj);
gsd_mod(&gsd, module_name);
if (ident)
gsd_ident(&gsd, ident);
/* write out each PSECT with its global stuff */
/* Sections must be written out in the order that they
appear in the assembly file. */
for (isect = 0; isect < sector; isect++) {
psect = sections[isect];
gsd_psect(&gsd, psect->label, psect->flags, psect->size);
psect->sector = isect; /* Assign it a sector */
psect->pc = 0; /* Reset its PC for second pass */
sym = first_sym(&symbol_st, &sym_iter);
while (sym) {
if ((sym->flags & SYMBOLFLAG_GLOBAL) && sym->section == psect) {
gsd_global(&gsd, sym->label,
((sym->flags & SYMBOLFLAG_DEFINITION) ? GLOBAL_DEF : 0) |
((sym->flags & SYMBOLFLAG_WEAK) ? GLOBAL_WEAK : 0) |
((sym->section->flags & PSECT_REL) ? GLOBAL_REL : 0) |
0100,
/* Looks undefined, but add it in anyway */
sym->value);
}
sym = next_sym(&symbol_st, &sym_iter);
}
}
/* Now write out the transfer address */
if (xfer_address->type == EX_LIT) {
gsd_xfer(&gsd, ". ABS.", xfer_address->data.lit);
} else {
SYMBOL *lsym;
unsigned offset;
if (!express_sym_offset(xfer_address, &lsym, &offset)) {
report(NULL, "Illegal program transfer address\n");
} else {
gsd_xfer(&gsd, lsym->section->label, lsym->value + offset);
}
}
gsd_flush(&gsd);
gsd_end(&gsd);
}