mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-13 15:27:18 +00:00
Fold the functionality of pull_up_reg() into evaluate_rec(). pull_up_reg() essentially returned a flag (EX_REG at the top, or not) which can be represented with the outgoing flags word. The check for recursion depth is made unneeded by doing the "exceptional" case outside the recursion; luckily it is actually the common case.
834 lines
22 KiB
C
834 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, ¤t_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, ¤t_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 || !value)
|
|
return; /* Only do this in first pass */
|
|
|
|
switch (num_subtrees(value)) {
|
|
case 0:
|
|
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;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
implicit_gbl(value->data.child.right);
|
|
/* FALLS THROUGH */
|
|
case 1:
|
|
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 if (sym->section->type == SECTION_REGISTER) {
|
|
/* Delayed action: evaluate() excludes SECTION_REGISTER when
|
|
* turning symbols into EX_LIT. Do it here now. */
|
|
store_word(str, tr, 2, sym->value + offset);
|
|
} 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->type == SECTION_REGISTER) {
|
|
/* Delayed action: evaluate() excludes SECTION_REGISTER when
|
|
* turning symbols into EX_LIT. Do it here now. */
|
|
store_word(stack->top, tr, size, sym->value + offset);
|
|
} 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);
|
|
}
|