/* Smaller operators for assemble */ #include #include #include #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); }