mirror of
https://github.com/open-simh/simtools.git
synced 2026-01-13 15:27:18 +00:00
831 lines
22 KiB
C
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, ¤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)
|
|
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);
|
|
}
|