#define ASSEMBLE__C #include #include #include "assemble.h" /* my own definitions */ #include "assemble_globals.h" #include "assemble_aux.h" #include "util.h" #include "mlb.h" #include "object.h" #include "listing.h" #include "parse.h" #include "symbols.h" #include "extree.h" #include "macros.h" #include "rept_irpc.h" #include "rad50.h" /* assemble - read a line from the input stack, assemble it. */ /* This function is way way too large, because I just coded most of the operation code and pseudo-op handling right in line. */ static int assemble( STACK *stack, TEXT_RLD *tr) { char *cp; /* Parse character pointer */ char *opcp; /* Points to operation mnemonic text */ char *ncp; /* "next" cp */ char *label; /* A label */ char *line; /* The whole line */ SYMBOL *op; /* The operation SYMBOL */ int local; /* Whether a label is a local label or not */ line = stack_gets(stack); if (line == NULL) return -1; /* Return code for EOF. */ if (!enabl_lc) { /* If lower case disabled, */ upcase(line); /* turn it into upper case. */ } cp = line; /* Frankly, I don't need to keep "line." But I found it quite handy during debugging, to see what the whole operation was, when I'm down to parsing the second operand and things aren't going right. */ stmtno++; /* Increment statement number */ list_source(stack->top, line); /* List source */ if (suppressed) { /* Assembly is suppressed by unsatisfied conditional. Look for ending and enabling statements. */ op = get_op(cp, &cp); /* Look at operation code */ /* FIXME: this code will blindly look into .REM commentary and find operation codes. Incidentally, so will read_body. */ if (op == NULL) return 1; /* Not found. Don't care. */ if (op->section->type != SECTION_PSEUDO) return 1; /* Not a pseudo-op. */ switch (op->value) { case P_IF: case P_IFDF: suppressed++; /* Nested. Suppressed. */ break; case P_IFTF: if (suppressed == 1) /* Reduce suppression from 1 to 0. */ suppressed = 0; break; case P_IFF: if (suppressed == 1) { /* Can reduce suppression from 1 to 0. */ if (!conds[last_cond].ok) suppressed = 0; } break; case P_IFT: if (suppressed == 1) { /* Can reduce suppression from 1 to 0. */ if (conds[last_cond].ok) suppressed = 0; } break; case P_ENDC: suppressed--; /* Un-nested. */ if (suppressed == 0) pop_cond(last_cond - 1); /* Re-enabled. */ break; } return 1; } /* The line may begin with "label:[:]" */ opcp = cp; if ((label = get_symbol(cp, &ncp, &local)) != NULL) { int flag = SYMBOLFLAG_PERMANENT | SYMBOLFLAG_DEFINITION | local; SYMBOL *sym; ncp = skipwhite(ncp); if (*ncp == ':') { /* Colon, for symbol definition? */ ncp++; /* maybe it's a global definition */ if (*ncp == ':') { flag |= SYMBOLFLAG_GLOBAL; /* Yes, include global flag */ ncp++; } sym = add_sym(label, DOT, flag, current_pc->section, &symbol_st); cp = ncp; if (sym == NULL) report(stack->top, "Illegal symbol definition %s\n", label); free(label); /* See if local symbol block should be incremented */ if (!enabl_lsb && !local) { lsb = get_next_lsb(); } cp = skipwhite(ncp); opcp = cp; label = get_symbol(cp, &ncp, NULL); /* Now, get what follows */ } } /* PSEUDO P_IIF jumps here. */ reassemble: cp = skipwhite(cp); if (EOL(*cp)) return 1; /* It's commentary. All done. */ if (label) { /* Something looks like a label. */ /* detect assignment */ ncp = skipwhite(ncp); /* The pointer to the text that follows the symbol */ if (*ncp == '=') { unsigned flags; EX_TREE *value; SYMBOL *sym; cp = ncp; /* Symbol assignment. */ flags = SYMBOLFLAG_DEFINITION | local; cp++; if (*cp == '=') { flags |= SYMBOLFLAG_GLOBAL; /* Global definition */ cp++; } if (*cp == ':') { flags |= SYMBOLFLAG_PERMANENT; cp++; } cp = skipwhite(cp); value = parse_expr(cp, 0); /* Special code: if the symbol is the program counter, this is harder. */ if (strcmp(label, ".") == 0) { if (current_pc->section->flags & PSECT_REL) { SYMBOL *symb; unsigned offset; /* Express the given expression as a symbol and an offset. The symbol must not be global, the section must = current. */ if (!express_sym_offset(value, &symb, &offset)) { report(stack->top, "Illegal ORG\n"); } else if ((symb->flags & (SYMBOLFLAG_GLOBAL | SYMBOLFLAG_DEFINITION)) == SYMBOLFLAG_GLOBAL) { report(stack->top, "Can't ORG to external location\n"); } else if (symb->flags & SYMBOLFLAG_UNDEFINED) { report(stack->top, "Can't ORG to undefined sym\n"); } else if (symb->section != current_pc->section) { report(stack->top, "Can't ORG to alternate section " "(use PSECT)\n"); } else { DOT = symb->value + offset; list_value(stack->top, DOT); change_dot(tr, 0); } } else { /* If the current section is absolute, the value must be a literal */ if (value->type != EX_LIT) { report(stack->top, "Can't ORG to non-absolute location\n"); free_tree(value); free(label); return 0; } DOT = value->data.lit; list_value(stack->top, DOT); change_dot(tr, 0); } free_tree(value); free(label); return 1; } /* regular symbols */ if (value->type == EX_LIT) { sym = add_sym(label, value->data.lit, flags, &absolute_section, &symbol_st); } else if (value->type == EX_SYM || value->type == EX_TEMP_SYM) { sym = add_sym(label, value->data.symbol->value, flags, value->data.symbol->section, &symbol_st); } else { report(stack->top, "Complex expression cannot be assigned " "to a symbol\n"); if (!pass) { /* This may work better in pass 2 - something in RT-11 monitor needs the symbol to apear to be defined even if I can't resolve its value. */ sym = add_sym(label, 0, SYMBOLFLAG_UNDEFINED, &absolute_section, &symbol_st); } else sym = NULL; } if (sym != NULL) list_value(stack->top, sym->value); free_tree(value); free(label); return sym != NULL; } /* Try to resolve macro */ op = lookup_sym(label, ¯o_st); if (op /*&& op->stmtno < stmtno*/) { STREAM *macstr; free(label); list_location(stack->top, DOT); macstr = expandmacro(stack->top, (MACRO *) op, ncp); stack_push(stack, macstr); /* Push macro expansion onto input stream */ return 1; } /* Try to resolve instruction or pseudo */ op = lookup_sym(label, &system_st); if (op) { cp = ncp; free(label); /* Don't need this hanging around anymore */ switch (op->section->type) { case SECTION_PSEUDO: switch (op->value) { case P_ENDR: case P_ENDM: case P_SBTTL: case P_LIST: case P_NLIST: case P_PRINT: return 1; /* Accepted, ignored. (An obvious need: get assembly listing controls working. ) */ case P_IDENT: { char endc[6]; int len; cp = skipwhite(cp); endc[0] = *cp++; endc[1] = '\n'; endc[2] = 0; len = (int) strcspn(cp, endc); if (len > 6) len = 6; if (ident) /* An existing ident? */ free(ident); /* Discard it. */ ident = memcheck(malloc(len + 1)); memcpy(ident, cp, len); ident[len] = 0; upcase(ident); return 1; } case P_RADIX: { int old_radix = radix; radix = strtoul(cp, &cp, 10); if (radix != 8 && radix != 10 && radix != 16 && radix != 2) { radix = old_radix; report(stack->top, "Illegal radix\n"); return 0; } return 1; } case P_FLT4: case P_FLT2: { int ok = 1; while (!EOL(*cp)) { unsigned flt[4]; if (parse_float(cp, &cp, (op->value == P_FLT4 ? 4 : 2), flt)) { /* Store the word values */ store_word(stack->top, tr, 2, flt[0]); store_word(stack->top, tr, 2, flt[1]); if (op->value == P_FLT4) { store_word(stack->top, tr, 2, flt[2]); store_word(stack->top, tr, 2, flt[3]); } } else { report(stack->top, "Bad floating point format\n"); ok = 0; } cp = skipdelim(cp); } return ok; } case P_ERROR: report(stack->top, "%.*s\n", strcspn(cp, "\n"), cp); return 0; case P_SAVE: if (sect_sp >= SECT_STACK_SIZE - 1) { report(stack->top, "Too many saved sections for .SAVE\n"); return 0; } sect_sp++; sect_stack[sect_sp] = current_pc->section; dot_stack[sect_sp] = DOT; return 1; case P_RESTORE: if (sect_sp < 0) { report(stack->top, "No saved section for .RESTORE\n"); return 0; } else { go_section(tr, sect_stack[sect_sp]); DOT = dot_stack[sect_sp]; list_location(stack->top, DOT); if (!enabl_lsb) { lsb = get_next_lsb(); } sect_sp--; } return 1; case P_NARG: { STREAM *str; MACRO_STREAM *mstr; int islocal; label = get_symbol(cp, &cp, &islocal); if (label == NULL) { report(stack->top, "Bad .NARG syntax\n"); return 0; } /* Walk up the stream stack to find the topmost macro stream */ for (str = stack->top; str != NULL && str->vtbl != ¯o_stream_vtbl; str = str->next) ; if (!str) { report(str, ".NARG not within macro expansion\n"); free(label); return 0; } mstr = (MACRO_STREAM *) str; add_sym(label, mstr->nargs, SYMBOLFLAG_DEFINITION | islocal, &absolute_section, &symbol_st); free(label); list_value(stack->top, mstr->nargs); return 1; } case P_NCHR: { char *string; int islocal; label = get_symbol(cp, &cp, &islocal); if (label == NULL) { report(stack->top, "Bad .NCHR syntax\n"); return 0; } cp = skipdelim(cp); string = getstring(cp, &cp); add_sym(label, strlen(string), SYMBOLFLAG_DEFINITION | islocal, &absolute_section, &symbol_st); free(label); free(string); return 1; } case P_NTYPE: { ADDR_MODE mode; int islocal; label = get_symbol(cp, &cp, &islocal); if (label == NULL) { report(stack->top, "Bad .NTYPE syntax\n"); return 0; } cp = skipdelim(cp); if (!get_mode(cp, &cp, &mode)) { report(stack->top, "Bad .NTYPE addressing mode\n"); free(label); return 0; } add_sym(label, mode.type, SYMBOLFLAG_DEFINITION | islocal, &absolute_section, &symbol_st); free_addr_mode(&mode); free(label); return 1; } case P_INCLUDE: { char *name = getstring_fn(cp, &cp); STREAM *incl; char hitfile[FILENAME_MAX]; if (name == NULL) { report(stack->top, "Bad .INCLUDE file name\n"); return 0; } my_searchenv(name, "INCLUDE", hitfile, sizeof(hitfile)); free(name); if (hitfile[0] == '\0') { report(stack->top, "Unable to find .INCLUDE file \"%s\"\n", name); return 0; } incl = new_file_stream(hitfile); if (incl == NULL) { report(stack->top, "Unable to open .INCLUDE file \"%s\"\n", hitfile); return 0; } stack_push(stack, incl); return 1; } case P_REM: { char quote[4]; /* Read and discard lines until one with a closing quote */ cp = skipwhite(cp); quote[0] = *cp++; quote[1] = '\n'; quote[2] = 0; for (;;) { cp += strcspn(cp, quote); if (*cp == quote[0]) break; /* Found closing quote */ cp = stack_gets(stack); /* Read next input line */ if (cp == NULL) break; /* EOF */ } } return 1; case P_IRP: { STREAM *str = expand_irp(stack, cp); if (str) stack_push(stack, str); return str != NULL; } case P_IRPC: { STREAM *str = expand_irpc(stack, cp); if (str) stack_push(stack, str); return str != NULL; } case P_LIBRARY: if (pass == 0) { char hitfile[FILENAME_MAX]; char *name = getstring_fn(cp, &cp); my_searchenv(name, "MCALL", hitfile, sizeof(hitfile)); if (hitfile[0]) { mlbs[nr_mlbs] = mlb_open(hitfile, 0); if (mlbs[nr_mlbs] == NULL) { report(stack->top, "Unable to register macro library \"%s\"\n", hitfile); } else { nr_mlbs++; } } else { report(stack->top, "Unable to locale macro library \"%s\"\n", name); } free(name); } return 1; case P_MCALL: { STREAM *macstr; BUFFER *macbuf; char *maccp; int saveline; MACRO *mac; int i; char macfile[FILENAME_MAX]; char hitfile[FILENAME_MAX]; for (;;) { cp = skipdelim(cp); if (EOL(*cp)) return 1; label = get_symbol(cp, &cp, NULL); if (!label) { report(stack->top, "Illegal .MCALL format\n"); return 0; } /* See if that macro's already defined */ if (lookup_sym(label, ¯o_st)) { free(label); /* Macro already registered. No prob. */ cp = skipdelim(cp); continue; } /* Find the macro in the list of included macro libraries */ macbuf = NULL; for (i = 0; i < nr_mlbs; i++) if ((macbuf = mlb_entry(mlbs[i], label)) != NULL) break; if (macbuf != NULL) { macstr = new_buffer_stream(macbuf, label); buffer_free(macbuf); } else { strncpy(macfile, label, sizeof(macfile)); strncat(macfile, ".MAC", sizeof(macfile) - strlen(macfile) - 1); my_searchenv(macfile, "MCALL", hitfile, sizeof(hitfile)); if (hitfile[0]) macstr = new_file_stream(hitfile); else macstr = NULL; } if (macstr != NULL) { for (;;) { char *mlabel; maccp = macstr->vtbl->gets(macstr); if (maccp == NULL) break; mlabel = get_symbol(maccp, &maccp, NULL); if (mlabel == NULL) continue; op = lookup_sym(mlabel, &system_st); free(mlabel); if (op == NULL) continue; if (op->value == P_MACRO) break; } if (maccp != NULL) { STACK macstack = { macstr }; int savelist = list_level; saveline = stmtno; list_level = -1; mac = defmacro(maccp, &macstack, TRUE); if (mac == NULL) { report(stack->top, "Failed to define macro " "called %s\n", label); } stmtno = saveline; list_level = savelist; } macstr->vtbl->delete(macstr); } else report(stack->top, "MACRO %s not found\n", label); free(label); } } return 1; case P_MACRO: { MACRO *mac = defmacro(cp, stack, FALSE); return mac != NULL; } case P_MEXIT: { STREAM *macstr; /* Pop a stream from the input. */ /* It must be the first stream, and it must be */ /* a macro, rept, irp, or irpc. */ macstr = stack->top; if (macstr->vtbl != ¯o_stream_vtbl && macstr->vtbl != &rept_stream_vtbl && macstr->vtbl != &irp_stream_vtbl && macstr->vtbl != &irpc_stream_vtbl) { report(stack->top, ".MEXIT not within a macro\n"); return 0; } /* and finally, pop the macro */ stack_pop(stack); return 1; } case P_REPT: { STREAM *reptstr = expand_rept(stack, cp); if (reptstr) stack_push(stack, reptstr); return reptstr != NULL; } case P_ENABL: /* FIXME - add all the rest of the options. */ while (!EOL(*cp)) { label = get_symbol(cp, &cp, NULL); if (strcmp(label, "AMA") == 0) enabl_ama = 1; else if (strcmp(label, "LSB") == 0) { enabl_lsb = 1; lsb = get_next_lsb(); } else if (strcmp(label, "GBL") == 0) { enabl_gbl = 1; } else if (strcmp(label, "LC") == 0) { enabl_lc = 1; } free(label); cp = skipdelim(cp); } return 1; case P_DSABL: /* FIXME Ditto as for .ENABL */ while (!EOL(*cp)) { label = get_symbol(cp, &cp, NULL); if (strcmp(label, "AMA") == 0) enabl_ama = 0; else if (strcmp(label, "LSB") == 0) { lsb = get_next_lsb(); enabl_lsb = 0; } else if (strcmp(label, "GBL") == 0) { enabl_gbl = 0; } else if (strcmp(label, "LC") == 0) { enabl_lc = 0; } free(label); cp = skipdelim(cp); } return 1; case P_LIMIT: store_limits(stack->top, tr); return 1; case P_TITLE: /* accquire module name */ if (module_name != NULL) { free(module_name); } module_name = get_symbol(cp, &cp, NULL); return 1; case P_END: /* Accquire transfer address */ cp = skipwhite(cp); if (!EOL(*cp)) { if (xfer_address) free_tree(xfer_address); xfer_address = parse_expr(cp, 0); } return 1; case P_IFDF: opcp = skipwhite(opcp); cp = opcp + 3; /* Point cp at the "DF" or "NDF" part */ /* Falls into... */ case P_IIF: case P_IF: { EX_TREE *value; int ok; label = get_symbol(cp, &cp, NULL); /* Get condition */ cp = skipdelim(cp); if (strcmp(label, "DF") == 0) { value = parse_expr(cp, 1); cp = value->cp; ok = eval_defined(value); free_tree(value); } else if (strcmp(label, "NDF") == 0) { value = parse_expr(cp, 1); cp = value->cp; ok = eval_undefined(value); free_tree(value); } else if (strcmp(label, "B") == 0 || strcmp(label, "NB") == 0) { cp = skipwhite(cp); if (EOL(*cp)) { ok = 1; } else { char *thing, *end; thing = getstring(cp, &cp); end = skipwhite(thing); ok = (*end == 0); free(thing); } if (label[0] == 'N') { ok = !ok; } } else if (strcmp(label, "IDN") == 0) { char *thing1, *thing2; thing1 = getstring(cp, &cp); cp = skipdelim(cp); if (!EOL(*cp)) thing2 = getstring(cp, &cp); else thing2 = memcheck(strdup("")); ok = (strcmp(thing1, thing2) == 0); free(thing1); free(thing2); } else if (strcmp(label, "DIF") == 0) { char *thing1, *thing2; thing1 = getstring(cp, &cp); cp = skipdelim(cp); if (!EOL(*cp)) thing2 = getstring(cp, &cp); else thing2 = memcheck(strdup("")); ok = (strcmp(thing1, thing2) != 0); free(thing1); free(thing2); } else { int sword; unsigned uword; EX_TREE *tvalue = parse_expr(cp, 0); cp = tvalue->cp; if (tvalue->type != EX_LIT) { report(stack->top, "Bad .IF expression\n"); list_value(stack->top, 0); free_tree(tvalue); ok = FALSE; /* Pick something. */ } else { unsigned word; /* Convert to signed and unsigned words */ sword = tvalue->data.lit & 0x7fff; /* FIXME I don't know if the following is portable enough. */ if (tvalue->data.lit & 0x8000) sword |= ~0xFFFF; /* Render negative */ /* Reduce unsigned value to 16 bits */ uword = tvalue->data.lit & 0xffff; if (strcmp(label, "EQ") == 0 || strcmp(label, "Z") == 0) ok = (uword == 0), word = uword; else if (strcmp(label, "NE") == 0 || strcmp(label, "NZ") == 0) ok = (uword != 0), word = uword; else if (strcmp(label, "GT") == 0 || strcmp(label, "G") == 0) ok = (sword > 0), word = sword; else if (strcmp(label, "GE") == 0) ok = (sword >= 0), word = sword; else if (strcmp(label, "LT") == 0 || strcmp(label, "L") == 0) ok = (sword < 0), word = sword; else if (strcmp(label, "LE") == 0) ok = (sword <= 0), word = sword; else ok = 0, word = 0; list_value(stack->top, word); free_tree(tvalue); } } free(label); if (op->value == P_IIF) { stmtno++; /* the second half is a separate statement */ if (ok) { /* The "immediate if" */ /* Only slightly tricky. */ cp = skipdelim(cp); label = get_symbol(cp, &ncp, &local); goto reassemble; } return 1; } push_cond(ok, stack->top); if (!ok) suppressed++; /* Assembly suppressed until .ENDC */ } return 1; case P_IFF: if (last_cond < 0) { report(stack->top, "No conditional block active\n"); return 0; } if (conds[last_cond].ok) /* Suppress if last cond is true */ suppressed++; return 1; case P_IFT: if (last_cond < 0) { report(stack->top, "No conditional block active\n"); return 0; } if (!conds[last_cond].ok) /* Suppress if last cond is false */ suppressed++; return 1; case P_IFTF: if (last_cond < 0) { report(stack->top, "No conditional block active\n"); return 0; } return 1; /* Don't suppress. */ case P_ENDC: if (last_cond < 0) { report(stack->top, "No conditional block active\n"); return 0; } pop_cond(last_cond - 1); return 1; case P_EVEN: if (DOT & 1) { list_word(stack->top, DOT, 0, 1, ""); DOT++; change_dot(tr, 0); } return 1; case P_ODD: if (!(DOT & 1)) { list_word(stack->top, DOT, 0, 1, ""); DOT++; change_dot(tr, 0); } return 1; case P_ASECT: if (!enabl_lsb) { lsb = get_next_lsb(); } go_section(tr, &absolute_section); list_location(stack->top, DOT); return 1; case P_CSECT: case P_PSECT: { SYMBOL *sectsym; SECTION *sect; unsigned int old_flags = ~0u; int unnamed_csect = 0; label = get_symbol(cp, &cp, NULL); if (label == NULL) { if (op->value == P_CSECT) { label = memcheck(strdup(". BLK.")); unnamed_csect = 1; } else { label = memcheck(strdup("")); /* Allow blank */ } } sectsym = lookup_sym(label, §ion_st); if (sectsym) { sect = sectsym->section; free(label); old_flags = sect->flags; } else { sect = new_section(); sect->label = label; sect->flags = 0; sect->pc = 0; sect->size = 0; sect->type = SECTION_USER; sections[sector++] = sect; sectsym = add_sym(label, 0, 0, sect, §ion_st); /* page 6-41 table 6-5 */ if (op->value == P_PSECT) { sect->flags |= PSECT_REL; } else if (op->value == P_CSECT) { if (unnamed_csect) { sect->flags |= PSECT_REL; } else { sect->flags |= PSECT_REL | PSECT_COM | PSECT_GBL; } } } cp = skipdelim(cp); if (!EOL(*cp)) { while (cp = skipdelim(cp), !EOL(*cp)) { /* Parse section options */ label = get_symbol(cp, &cp, NULL); if (strcmp(label, "ABS") == 0) { sect->flags &= ~PSECT_REL; /* Not relative */ sect->flags |= PSECT_COM; /* implies common */ } else if (strcmp(label, "REL") == 0) { sect->flags |= PSECT_REL; /* Is relative */ } else if (strcmp(label, "SAV") == 0) { sect->flags |= PSECT_SAV; /* Is root */ } else if (strcmp(label, "NOSAV") == 0) { sect->flags &= ~PSECT_SAV; /* Is not root */ } else if (strcmp(label, "OVR") == 0) { sect->flags |= PSECT_COM; /* Is common */ } else if (strcmp(label, "CON") == 0) { sect->flags &= ~PSECT_COM; /* Concatenated */ } else if (strcmp(label, "RW") == 0) { sect->flags &= ~PSECT_RO; /* Not read-only */ } else if (strcmp(label, "RO") == 0) { sect->flags |= PSECT_RO; /* Is read-only */ } else if (strcmp(label, "I") == 0) { sect->flags &= ~PSECT_DATA; /* Not data */ } else if (strcmp(label, "D") == 0) { sect->flags |= PSECT_DATA; /* data */ } else if (strcmp(label, "GBL") == 0) { sect->flags |= PSECT_GBL; /* Global */ } else if (strcmp(label, "LCL") == 0) { sect->flags &= ~PSECT_GBL; /* Local */ } else { report(stack->top, "Unknown flag %s given to " ".PSECT directive\n", label); free(label); return 0; } free(label); } /* If a section is declared a second time, and flags * are given, then they must be identical to the * first time. * See page 6-38 of AA-KX10A-TC_PDP-11_MACRO-11_Reference_Manual_May88.pdf . */ if (old_flags != ~0u && sect->flags != old_flags) { /* The manual also says that any different * flags are ignored, and an error issued. * Apparently, that isn't true. * Kermit seems to do this in k11cmd.mac: * .psect $pdata ; line 16 * .psect $pdata ,ro,d,lcl,rel,con * ; k11mac.mac, first pass only * .psect $PDATA ,D ; line 1083 * and ends up with * $PDATA 001074 003 (RO,D,LCL,REL,CON) */ /* sect->flags = old_flags; report(stack->top, "Program section flags not identical\n"); */ } } if (!enabl_lsb) { lsb = get_next_lsb(); } go_section(tr, sect); list_location(stack->top, DOT); return 1; } /* end PSECT code */ break; case P_WEAK: case P_GLOBL: { SYMBOL *sym; while (!EOL(*cp)) { /* Loop and make definitions for comma-separated symbols */ label = get_symbol(cp, &ncp, NULL); if (label == NULL) { report(stack->top, "Illegal .GLOBL/.WEAK " "syntax\n"); return 0; } sym = lookup_sym(label, &symbol_st); if (sym) { sym->flags |= SYMBOLFLAG_GLOBAL | (op->value == P_WEAK ? SYMBOLFLAG_WEAK : 0); } else sym = add_sym(label, 0, SYMBOLFLAG_GLOBAL | (op->value == P_WEAK ? SYMBOLFLAG_WEAK : 0), &absolute_section, &symbol_st); free(label); cp = skipdelim(ncp); } } return 1; case P_WORD: { /* .WORD might be followed by nothing, which is an implicit .WORD 0 */ if (EOL(*cp)) { if (DOT & 1) { report(stack->top, ".WORD on odd " "boundary\n"); DOT++; /* Fix it, too */ } store_word(stack->top, tr, 2, 0); return 1; } else return do_word(stack, tr, cp, 2); } case P_BYTE: if (EOL(*cp)) { /* Blank .BYTE. Same as .BYTE 0 */ store_word(stack->top, tr, 1, 0); return 1; } else return do_word(stack, tr, cp, 1); case P_BLKW: case P_BLKB: { EX_TREE *value = parse_expr(cp, 0); int ok = 1; if (value->type != EX_LIT) { report(stack->top, "Argument to .BLKB/.BLKW " "must be constant\n"); ok = 0; } else { list_value(stack->top, DOT); DOT += value->data.lit * (op->value == P_BLKW ? 2 : 1); change_dot(tr, 0); } free_tree(value); return ok; } case P_ASCIZ: case P_ASCII: { EX_TREE *value; do { cp = skipwhite(cp); if (*cp == '<' || *cp == '^') { /* A byte value */ value = parse_unary_expr(cp, 0); cp = value->cp; store_value(stack, tr, 1, value); free_tree(value); } else { char quote = *cp++; while (*cp && *cp != '\n' && *cp != quote) store_word(stack->top, tr, 1, *cp++); cp++; /* Skip closing quote */ } cp = skipwhite(cp); } while (!EOL(*cp)); if (op->value == P_ASCIZ) { store_word(stack->top, tr, 1, 0); } return 1; } case P_RAD50: if (DOT & 1) { report(stack->top, ".RAD50 on odd " "boundary\n"); DOT++; /* Fix it */ } while (!EOL(*cp)) { char endstr[6]; int len; char *radstr; char *radp; endstr[0] = *cp++; endstr[1] = '\n'; endstr[2] = 0; len = strcspn(cp, endstr); radstr = memcheck(malloc(len + 1)); memcpy(radstr, cp, len); radstr[len] = 0; cp += len; if (*cp && *cp != '\n') cp++; for (radp = radstr; *radp;) { unsigned rad; rad = rad50(radp, &radp); store_word(stack->top, tr, 2, rad); } free(radstr); cp = skipwhite(cp); } return 1; default: report(stack->top, "Unimplemented directive %s\n", op->label); return 0; } /* end switch (PSEUDO operation) */ case SECTION_INSTRUCTION: { /* The PC must always be even. */ if (DOT & 1) { report(stack->top, "Instruction on odd address\n"); DOT++; /* ...and fix it... */ } switch (op->flags & OC_MASK) { case OC_NONE: /* No operands. */ store_word(stack->top, tr, 2, op->value); return 1; case OC_MARK: /* MARK, EMT, TRAP */ { EX_TREE *value; unsigned word; cp = skipwhite(cp); if (*cp == '#') cp++; /* Allow the hash, but don't require it */ value = parse_expr(cp, 0); if (value->type != EX_LIT) { report(stack->top, "Instruction requires " "simple literal operand\n"); word = op->value; } else { word = op->value | value->data.lit; } store_word(stack->top, tr, 2, word); free_tree(value); } return 1; case OC_1GEN: /* One general addressing mode */ { ADDR_MODE mode; unsigned word; if (!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); return 0; } if (op->value == 0100 && (mode.type & 070) == 0) { report(stack->top, "JMP Rn is illegal\n"); /* But encode it anyway... */ } /* Build instruction word */ word = op->value | mode.type; store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); } return 1; case OC_2GEN: /* Two general addressing modes */ { ADDR_MODE left, right; unsigned word; if (!get_mode(cp, &cp, &left)) { report(stack->top, "Illegal addressing mode\n"); return 0; } cp = skipwhite(cp); if (*cp++ != ',') { report(stack->top, "Illegal syntax\n"); free_addr_mode(&left); return 0; } if (!get_mode(cp, &cp, &right)) { report(stack->top, "Illegal addressing mode\n"); free_addr_mode(&left); return 0; } /* Build instruction word */ word = op->value | left.type << 6 | right.type; store_word(stack->top, tr, 2, word); mode_extension(tr, &left, stack->top); mode_extension(tr, &right, stack->top); } return 1; case OC_BR: /* branches */ { EX_TREE *value; unsigned offset; value = parse_expr(cp, 0); cp = value->cp; /* Relative PSECT or absolute? */ if (current_pc->section->flags & PSECT_REL) { SYMBOL *sym = NULL; /* Can't branch unless I can calculate the offset. */ /* You know, I *could* branch between sections if I feed the linker a complex relocation expression to calculate the offset. But I won't. */ if (!express_sym_offset(value, &sym, &offset) || sym->section != current_pc->section) { report(stack->top, "Bad branch target (%s)\n", sym ? "not same section" : "can't express offset"); store_word(stack->top, tr, 2, op->value); free_tree(value); return 0; } /* Compute the branch offset and check for addressability */ offset += sym->value; offset -= DOT + 2; } else { if (value->type != EX_LIT) { report(stack->top, "Bad branch target (not literal; ABS section)\n"); store_word(stack->top, tr, 2, op->value); free_tree(value); return 0; } offset = value->data.lit - (DOT + 2); } if (!check_branch(stack, offset, -256, 255)) offset = 0; /* Emit the branch code */ offset &= 0777; /* Reduce to 9 bits */ offset >>= 1; /* Shift to become word offset */ store_word(stack->top, tr, 2, op->value | offset); free_tree(value); } return 1; case OC_SOB: { EX_TREE *value; unsigned reg; unsigned offset; value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); free_tree(value); if (reg == NO_REG) { report(stack->top, "Illegal addressing mode\n"); return 0; } cp = skipwhite(cp); if (*cp++ != ',') { report(stack->top, "Illegal syntax\n"); return 0; } value = parse_expr(cp, 0); cp = value->cp; /* Relative PSECT or absolute? */ if (current_pc->section->flags & PSECT_REL) { SYMBOL *sym; if (!express_sym_offset(value, &sym, &offset)) { report(stack->top, "Bad branch target (can't express offset)\n"); free_tree(value); return 0; } /* Must be same section */ if (sym->section != current_pc->section) { report(stack->top, "Bad branch target (different section)\n"); free_tree(value); offset = 0; } else { /* Calculate byte offset */ offset += DOT + 2; offset -= sym->value; } } else { if (value->type != EX_LIT) { report(stack->top, "Bad branch target (not a literal)\n"); offset = 0; } else { offset = DOT + 2 - value->data.lit; } } if (!check_branch(stack, offset, 0, 126)) offset = 0; offset &= 0177; /* Reduce to 7 bits */ offset >>= 1; /* Shift to become word offset */ store_word(stack->top, tr, 2, op->value | offset | (reg << 6)); free_tree(value); } return 1; case OC_ASH: /* First op is gen, second is register. */ { ADDR_MODE mode; EX_TREE *value; unsigned reg; unsigned word; if (!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); return 0; } cp = skipwhite(cp); if (*cp++ != ',') { report(stack->top, "Illegal addressing mode\n"); free_addr_mode(&mode); return 0; } value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if (reg == NO_REG) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); free_addr_mode(&mode); return 0; } /* Instruction word */ word = op->value | mode.type | (reg << 6); store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); free_tree(value); } return 1; case OC_JSR: /* First op is register, second is gen. */ { ADDR_MODE mode; EX_TREE *value; unsigned reg; unsigned word; value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if (reg == NO_REG) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); return 0; } cp = skipwhite(cp); if (*cp++ != ',') { report(stack->top, "Illegal addressing mode\n"); return 0; } if (!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); return 0; } if ((mode.type & 070) == 0) { report(stack->top, "JSR Rn,Rm is illegal\n"); /* But encode it anyway... */ } word = op->value | mode.type | (reg << 6); store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); free_tree(value); } return 1; case OC_1REG: /* One register (RTS) */ { EX_TREE *value; unsigned reg; value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if (reg == NO_REG) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); reg = 0; } store_word(stack->top, tr, 2, op->value | reg); free_tree(value); } return 1; case OC_1FIS: /* One one gen and one reg 0-3 */ { ADDR_MODE mode; EX_TREE *value; unsigned reg; unsigned word; if (!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); return 0; } cp = skipwhite(cp); if (*cp++ != ',') { report(stack->top, "Illegal addressing mode\n"); free_addr_mode(&mode); return 0; } value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if (reg == NO_REG || reg > 4) { report(stack->top, "Invalid destination register\n"); reg = 0; } word = op->value | mode.type | (reg << 6); store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); free_tree(value); } return 1; case OC_2FIS: /* One reg 0-3 and one gen */ { ADDR_MODE mode; EX_TREE *value; unsigned reg; unsigned word; value = parse_expr(cp, 0); cp = value->cp; reg = get_register(value); if (reg == NO_REG || reg > 4) { report(stack->top, "Illegal source register\n"); reg = 0; } cp = skipwhite(cp); if (*cp++ != ',') { report(stack->top, "Illegal addressing mode\n"); free_tree(value); return 0; } if (!get_mode(cp, &cp, &mode)) { report(stack->top, "Illegal addressing mode\n"); free_tree(value); return 0; } word = op->value | mode.type | (reg << 6); store_word(stack->top, tr, 2, word); mode_extension(tr, &mode, stack->top); free_tree(value); } return 1; default: report(stack->top, "Unimplemented instruction format\n"); return 0; } /* end(handle an instruction) */ } break; } /* end switch(section type) */ } /* end if (op is a symbol) */ } /* Only thing left is an implied .WORD directive */ /*JH: fall through in case of illegal opcode, illegal label! */ free(label); return do_word(stack, tr, cp, 2); } int get_next_lsb( void) { if (lsb_used) { lsb_used = 0; if (enabl_debug && lstfile) { fprintf(lstfile, "get_next_lsb: lsb: %d becomes %d (= next_lsb)\n", lsb, next_lsb); } return next_lsb++; } else { if (enabl_debug && lstfile) { fprintf(lstfile, "get_next_lsb: lsb: stays %d\n", lsb); } return lsb; } } /* assemble_stack assembles the input stack. It returns the error count. */ int assemble_stack( STACK *stack, TEXT_RLD *tr) { int res; int errcount = 0; while ((res = assemble(stack, tr)) >= 0) { list_flush(); if (res == 0) errcount++; /* Count an error */ } return errcount; }