/************************************************************************* Copyright (c) 1984 by Nick de Smith This software is supplied for interest and non-profit making purposes only. Under no circumstance shall it be lent, copied or otherwise used for profit. All rights regarding the use and ownership of this software shall at all times remain with the author, who does not guarantee the accuracy or reliabilty of this software and who will not accept any liability for its use. This software may not be copied or distributed without the inclusion of the above copyright notice. January 31st 1984 *************************************************************************/ /************************************************************************* Program : CAM Module : PASS3.C Author : Nick de Smith November/December 1982 Description : Third pass of the object module disassembler *************************************************************************/ #define MODULE #include #include "cam.h" #include "camtbl.h" /************************************************************************* * * * p a s s _ 3 * ----------- * * Third pass through the object file. This does the actual output. * *************************************************************************/ global pass_3() { pass = 3; rewind(ip); rec_num = 0; b_init(); /* Initialise buffers */ eom = FALSE; dbg("Started PASS_3\n"); p_reset(); /* Reset the psect table flags */ l_name(); /* Give real names to labels */ if (debug) /* And why not, may I ask? */ s_dump(); do_header(); /* Output a meaningful header */ do_title(); /* Dump the module name */ do_ident(); /* Dump the .ident */ if (q_flag) do_p_defns(); /* Dump the psect definitions */ if (ref_g) do_g_ref(); /* Dump global references */ if (abs_g) do_g_abs(); /* Dump global absolutes */ if (flt_f) do_f_names(); /* Define accumulator names */ if (psect_f) do_p_table(); /* Output the psects in order */ if (code) decode(); /* Do the dis-assembly */ do_end(); /* Do the .end statement */ dbg("Ended PASS_3\n"); } /************************************************************************* * * * d o _ h e a d e r * ----------------- * * Print a meaningfull header to the output file. * *************************************************************************/ local do_header() { char *ctime(); putl(F_DIRECT, ".nlist"); putl(F_DIRECT, ".enabl\tlc"); putl(F_DIRECT, ".list"); putl(F_LNUM | F_LABEL, ";+"); sprintf(wbuf, "; \"%s\" dis-assembled on %s", file, ctime(0)); putl(F_LNUM | F_LABEL, wbuf); putl(F_LNUM | F_LABEL, ";"); sprintf(wbuf, "; %s", version); putl(F_LNUM | F_LABEL, wbuf); putl(F_LNUM | F_LABEL, ";"); sprintf(wbuf, "; Options : %s", strlen(option) ? option : ""); putl(F_LNUM | F_LABEL, wbuf); putl(F_LNUM | F_LABEL, ";-"); } /************************************************************************* * * * d o _ t i t l e * --------------- * * Print the .title directive if there was one. * *************************************************************************/ local do_title() { char *radmin(); if (*modnam) { sprintf(wbuf, ".title\t%s", radmin(modnam)); putl(F_LNUM | F_DIRECT, wbuf); } } /************************************************************************* * * * d o _ i d e n t * --------------- * * Do the .ident directive if there was one. * *************************************************************************/ local do_ident() { char *radnam(); if (ident_f) { sprintf(wbuf, ".ident\t\"%s\"", radnam(_ident)); putl(F_LNUM | F_DIRECT, wbuf); } } /************************************************************************* * * * d o _ p _ d e f n s * ------------------- * * Print out the list of defined psects along with their flags. * *************************************************************************/ local do_p_defns() { register int i; register PSECT_PTR p_ptr; char *radnam(); if (seg_max < 0) { putl(F_PBL | F_LNUM | F_LABEL | F_TBL, "; There are no psects in this module"); return; } putl(F_LNUM | F_LABEL | F_PBL, ";+"); putl(F_LNUM | F_LABEL , "; All defined psects"); putl(F_LNUM | F_LABEL , ";"); putl(F_LNUM | F_LABEL , "; Psect Length Name Type"); set_ptr(F_LABEL, wbuf); for (i = 0; i <= seg_max; i++) { arg_i(); arg_s("; "); arg_3o(i); arg_s(" "); arg_6o((p_ptr = &segtbl[i])->p_mlength); arg_s(" "); arg_s(radnam(p_ptr->p_name)); arg_s(" "); p_type(p_ptr->p_flags); line_out(F_LNUM | F_LABEL); } putl(F_LNUM | F_LABEL | F_TBL, ";-"); } /************************************************************************* * * * d o _ p _ t a b l e * ------------------- * * Output the list of psects in the order in which they were defined * in the GSD. * *************************************************************************/ local do_p_table() { register int i; if (seg_max < 0) { putl(F_PBL | F_LNUM | F_LABEL | F_TBL, "; No psects found in this module"); return; } putl(F_LNUM | F_LABEL | F_PBL, ";+"); putl(F_LNUM | F_LABEL , "; All defined psects in order"); putl(F_LNUM | F_LABEL | F_TBL, ";-"); dot.d_value = 0; line_new(); for (i = 0; i <= seg_max; i++) { psect_desc(&segtbl[i]); line_out(F_LNUM | F_PC | F_INST | F_ARGS); } putb(); } /************************************************************************* * * * d o _ g _ r e f * --------------- * * Output the list of referenced global symbols. We output only * four per line so that the listing is a respectable width. * *************************************************************************/ local do_g_ref() { register SYMBOL_PTR s_ptr; register int col; char *radnam(); if (!(s_ptr = ref_st)) { putl(F_PBL | F_LNUM | F_LABEL | F_TBL, "; There are no global references"); return; } putl(F_LNUM | F_LABEL | F_PBL, ";+"); putl(F_LNUM | F_LABEL , "; All global references"); putl(F_LNUM | F_LABEL | F_TBL, ";-"); set_ptr(F_DIRECT, ".globl"); set_ptr(F_ARGS, wbuf); arg_i(); col = 0; while (s_ptr) { if (col++ & 3) arg_s(",\t"); else if (col > 1) { line_out(F_LNUM | F_DIRECT | F_ARGS); arg_i(); } arg_s(radnam(s_ptr->s_name)); s_ptr = s_ptr->s_next; } if (arg_n()) line_out(F_LNUM | F_DIRECT | F_ARGS); putb(); } /************************************************************************* * * * d o _ g _ a b s * --------------- * * Output the list of all defined absolute global symbols. If we * requested sorted absolute global definitions then we output the * name of the psect that we are printing. * *************************************************************************/ local do_g_abs() { register PSECT_PTR p_ptr; register SYMBOL_PTR s_ptr; register int i; char *radmin(); if (!abs_f) { putl(F_LNUM | F_LABEL | F_PBL | F_TBL, "; There are no absolute globals"); return; } putl(F_LNUM | F_LABEL | F_PBL, ";+"); putl(F_LNUM | F_LABEL , "; All absolute global definitions"); putl(F_LNUM | F_LABEL | F_TBL, ";-"); if (sort_g) { for (i = 0; i <= seg_max; i++) if ((p_ptr = &segtbl[i])->p_symb && !(p_ptr->p_flags & P_REL)) { sprintf(wbuf, "; Psect %03o \"%s\"", i, radmin(p_ptr->p_name)); putl(F_LNUM | F_LABEL, wbuf); abs_print(p_ptr->p_symb); } } /* Tie in the next 'else' */ else abs_print(abs_st); putb(); } /************************************************************************* * * * a b s _ p r i n t * ----------------- * * Output a list of absolute definitions. * *************************************************************************/ local abs_print(s_ptr) register SYMBOL_PTR s_ptr; { char *radmin(); IR->d_flags = A_VALID; set_ptr(F_LABEL, buff); set_ptr(F_INST, wbuf); while (s_ptr) { if ((s_ptr->s_flags & (S_STRONG | S_GLOBAL | S_STB)) == S_STRONG | S_GLOBAL ) { radmin(s_ptr->s_name); arg_i(); arg_s("==\t"); arg_o(IR->d_value = s_ptr->s_offset); line_out(F_LNUM | F_IR | F_LABEL | F_INST); } s_ptr = s_ptr->s_next; } } /************************************************************************* * * * d o _ f _ n a m e s * ------------------- * * Define the floating accumulator names. This is only done if * pass two detected any opcodes reqiring use of an FAC. * *************************************************************************/ local do_f_names() { register int i; putl(F_LNUM | F_LABEL | F_PBL, ";+"); putl(F_LNUM | F_LABEL , "; Define floating accumulators"); putl(F_LNUM | F_LABEL | F_TBL, ";-"); IR->d_flags = A_VALID; set_ptr(F_LABEL, lbuff); set_ptr(F_INST, wbuf); for (i = 0; i < 6; i++) { cpystr(lbuff, fltnam[i]); arg_i(); arg_s("=\t%"); arg_o(IR->d_value = i); line_out(F_LNUM | F_IR | F_LABEL | F_INST); } } /************************************************************************* * * * d e c o d e * ----------- * * Final pass of the object module decode. This bit actually does * the instruction decode. * *************************************************************************/ local decode() { int name[2]; register int flags, l_flags, temp; int blkb; char *radmin(); dbg("In PASS_3 DECODE\n"); blkb = 0; do { line_new(); dot.d_value = seg_ptr->p_dot; do_labels(); if (blkb) { arg_i(); arg_o(blkb); set_ptr(F_INST, ".blkb"); set_ptr(F_ARGS, wbuf); l_flags = F_ALL; line_out(l_flags | F_PBL); dot.d_value = (seg_ptr->p_dot += blkb); blkb = 0; line_new(); do_labels(); } l_flags = F_ALL; switch ((flags = get_item(IR)) & A_TYPE) { case A_EOM: eom = TRUE; break; case A_LMOD: arg_i(); arg_o((temp = next_two_bytes(b_current)) - seg_ptr->p_dot); set_ptr(F_ARGS, wbuf); set_ptr(F_INST, ".blkb"); seg_ptr->p_dot = temp; r_next(b_current); break; case A_LDEF: name[0] = next_two_bytes(b_current); name[1] = next_two_bytes(b_current); seg_ptr = &segtbl[seg_cur = find(name)]; blkb = next_two_bytes(b_current) - (dot.d_value = seg_ptr->p_dot); r_next(b_current); l_flags |= (F_PBL | F_TBL); l_flags &= ~(F_LABEL | F_COM); psect_desc(seg_ptr); break; case A_DATA: if (flags & A_B) { byte_(); break; } if (flags & A_RELA) { if (flags & A_LIMIT) limit_(); else word_(); break; } if (b_flag || !proc_ir()) word_(); break; default: bug("Strange flags in pass 3 DECODE, %06o", flags); } if (!eom) line_out(l_flags); } while (!eom); dbg("Leaving PASS_3 DECODE\n"); } /************************************************************************* * * * p r o c _ i r * ------------- * * Process the IR (instruction register). Return TRUE is all was * well with the decode, FALSE else. * *************************************************************************/ local proc_ir() { register TREE_PTR node; register int _ir; register int offset; char *sep_txt; char *radmin(); SYMBOL_PTR lookup(); sep_txt = ", "; offset = 014; node = ((_ir = IR->d_value) & 0100000 ? x1 : x0); for (;;) { if ((node = &node[(_ir & (007 << offset)) >> offset])->t_flags & T_END) break; node = node->t_next; offset -= 3; } arg_i(); set_ptr(F_INST, node->t_next); set_ptr(F_ARGS, wbuf); switch (node->t_flags & T_TYPE) { case T_NOPS: /* No operands */ break; case T_SSDD: /* SS,DD type (MOV etc) */ return (do_2_args(_ir)); case T_RDD: /* R,DD reg, dest (JSR + XOR) */ arg_s(regnam[(_ir & 0700) >> 6]); arg_s(sep_txt); return (do_1_arg(_ir)); case T_SSR: /* SS,R src, reg (MUL etc) */ if (!do_1_arg(_ir)) return (FALSE); arg_s(sep_txt); arg_s(regnam[(_ir & 0700) >> 6]); break; case T_SS: /* SS src (JMP, CLR etc) */ return (do_1_arg(_ir)); case T_R: /* R reg (RTS, FADD etc) */ arg_s(regnam[_ir & 07]); break; case T_ROO: /* R,OO reg, 6 bit off (SOB) */ if (!bounded(offset = seg_ptr->p_dot - (2 * (_ir & 077)))) return (FALSE); arg_s(regnam[(_ir & 0700) >> 6]); arg_s(sep_txt); arg_s(radmin((lookup(seg_cur, offset))->s_name)); break; case T_OO: /* OOO 8 bit off (branches) */ offset = _ir & 0377; if (_ir & 0200) /* Extend sign if needed */ offset |= 0177400; offset *= 2; if (!bounded(offset += seg_ptr->p_dot)) return (FALSE); arg_s(radmin((lookup(seg_cur, offset))->s_name)); break; case T_NNNNNN: /* NNNNNN 16 bit data (.WORD) */ arg_o(_ir); break; case T_NNN: /* NNN 8 bit data (EMT + TRAP) */ arg_o(_ir & 0377); break; case T_NN: /* NN 6 bit data (MARK) */ arg_o(_ir & 077); break; case T_N: /* N 3 bit data (SPL) */ arg_o(_ir & 07); break; case T_AFSS: /* Fsrc, acc (MULF, MODF etc) */ if (!fltmod(_ir)) { on_stack(D1); return (FALSE); } arg_s(sep_txt); arg_s(fltnam[(_ir & 0300) >> 6]); break; case T_AFDD: /* Acc, Fdst (STF + STCFD) */ arg_s(fltnam[(_ir & 0300) >> 6]); arg_s(sep_txt); if (!fltmod(_ir)) { on_stack(D1); return (FALSE); } break; case T_ASS: /* Src, acc (LDCIF + LDEXP) */ if (!do_1_arg(_ir)) return (FALSE); arg_s(sep_txt); arg_s(fltnam[(_ir & 0300) >> 6]); break; case T_ADD: /* Acc, dst (STCFI + STEXP) */ arg_s(fltnam[(_ir & 0300) >> 6]); arg_s(sep_txt); return (do_1_arg(_ir)); case T_FSS: /* Fsrc (CLRF etc) */ if (!fltmod(_ir)) { on_stack(D1); return (FALSE); } break; case M_JMPX: /* JMP type macros */ if (!do_1_arg(_ir)) return (FALSE); if ((_ir & 037) == 037 && D1->d_flags & A_G) { set_ptr(F_INST, "jmpx"); arg_i(); arg_s(D1->d_text); } break; case M_RETURN: /* RTS => RETURN macro */ if ((_ir & 007) != 007) arg_s(regnam[_ir & 007]); break; case M_CALL: /* JSR => CALL/CALLX macro */ if (!do_1_arg(_ir)) return (FALSE); if ((_ir & 077) == 037 && D1->d_flags & A_G) { set_ptr(F_INST, "callx"); arg_i(); arg_s(D1->d_text); } if ((_ir & 0700) != 0700) { arg_s(sep_txt); arg_s(regnam[(_ir & 0700) >> 6]); } break; case M_PUSH: /* clr -(sp) 0005046 */ /* mov x, -(sp) 001xx46 */ if (!(_ir & 0010000)) { /* Must be a CLR type */ if ((_ir & 077) == 046) break; /* Its PUSH */ set_ptr(F_INST, "clr"); /* Its a real CLR */ return (do_1_arg(_ir)); } if ((_ir & 077) == 046) /* Is it a PUSH ? */ return (do_1_arg(_ir >> 6)); set_ptr(F_INST, "mov"); /* Its a real MOV */ return (do_2_args(_ir)); case M_POP: /* tst (sp)+ 0005726 */ /* mov (sp)+, x 00126xx */ if (!(_ir & 0010000)) { /* Must be a TST type */ if ((_ir & 077) == 026) break; /* Its POP */ set_ptr(F_INST, "tst"); /* Its a real TST */ return (do_1_arg(_ir)); } if ((_ir & 07700) == 02600) /* Is it a POP ? */ return (do_1_arg(_ir)); set_ptr(F_INST, "mov"); /* Must be a MOV */ return (do_2_args(_ir)); } return (TRUE); } /************************************************************************* * * * d o _ 1 _ a r g * --------------- * * Handle a "regmod" type argument. We assume that the argument is * in the low 6 bits of the passed IR. Also, we assume that the only * place to put the data (if any) is in D1. These are both perfectly * safe assumptions. * *************************************************************************/ local do_1_arg(_ir) register _ir; { if (!regmod(D1, _ir)) { on_stack(D1); return (FALSE); } return (TRUE); } /************************************************************************* * * * d o _ 2 _ a r g s * ----------------- * * Handle a 'regmod, regmod' type argument. General form here is * for T_SSDD type opcodes. This is separated out as the 'macro' * types include a 'mov' in 'push' and 'pull' that may need to be * treated as a 'mov' and not a macro. * *************************************************************************/ local do_2_args(_ir) register _ir; { register DATA_PTR d_ptr; if (!regmod(d_ptr = D1, _ir >> 6)) { on_stack(D1); return (FALSE); } if (d_ptr->d_flags & A_VALID) d_ptr = D2; arg_s(", "); /* Should be 'sep_txt' */ if (!regmod(d_ptr, _ir)) { on_stack(D1); on_stack(D2); return (FALSE); } return (TRUE); } /************************************************************************* * * * f l t m o d * ----------- * * Handle general floating point register/mode arguments. Note that * there are only six KEF-11/FP-11 registers, AC0 to AC5. If any * other is detected, force a .word. For all floating point instr- * uctions, the only place that Fsrc/Fdst can be found is in the low * six bits. * *************************************************************************/ local fltmod(_ir) register int _ir; { register int temp; if (_ir & 070) return (regmod(D1, _ir)); if ((temp = _ir & 007) == 6 || temp == 7) return (FALSE); arg_s(fltnam[temp]); return (TRUE); } /************************************************************************* * * * r e g m o d * ----------- * * Generate the register/mode argument. Return TRUE if all * is well, false else. * *************************************************************************/ local regmod(d_ptr, bits) register DATA_PTR d_ptr; int bits; { register int reg, flags; SYMBOL_PTR lookup(); char *radmin(); reg = bits & 07; if (bits & 010) arg_c('@'); switch ((bits & 070) >> 3) { case 0: case 1: arg_s(regnam[reg]); break; case 2: case 3: if (reg != 7) { arg_c('('); arg_s(regnam[reg]); arg_s(")+"); break; } if (((flags = get_item(d_ptr)) & A_TYPE) != A_DATA || flags & (A_B | A_LIMIT)) return (FALSE); arg_c('#'); if (flags & A_RELA) arg_s(d_ptr->d_text); else arg_o(d_ptr->d_value); break; case 4: case 5: if (reg == 7) return (FALSE); arg_s("-("); arg_s(regnam[reg]); arg_c(')'); break; case 6: case 7: if (((flags = get_item(d_ptr)) & A_TYPE) != A_DATA || flags & (A_B | A_LIMIT)) return (FALSE); if (reg == 7) { if (flags & A_RELA) arg_s(d_ptr->d_text); else arg_s(radmin((lookup(seg_cur, d_ptr->d_value + seg_ptr->p_dot))->s_name)); break; } if (flags & A_RELA) arg_s(d_ptr->d_text); else arg_o(d_ptr->d_value); arg_c('('); arg_s(regnam[reg]); arg_c(')'); break; } return (TRUE); } /************************************************************************* * * * b o u n d e d * ------------- * * Check that the passed argument lies within the bounds of the * current psect. Return TRUE is it does, FALSE else. * *************************************************************************/ global bounded(offset) register unsigned int offset; { return ( offset <= seg_ptr->p_mlength ? TRUE : FALSE ); } /************************************************************************* * * * d o _ l a b e l s * ----------------- * * Output all the labels for the current dot. * *************************************************************************/ local do_labels() { register SYMBOL_PTR s_ptr; SYMBOL_PTR lookup(); SYMBOL_PTR s_index(); if (!(s_ptr = lookup(seg_cur, seg_ptr->p_dot))) return; set_ptr(F_LABEL, lbuff); label_(s_ptr); while (s_ptr = s_index(s_ptr, 2)) { line_out(F_LNUM | F_PC | F_LABEL); label_(s_ptr); } } /************************************************************************* * * * l a b e l _ * ----------- * * Form a true label from a symbol pointer. * *************************************************************************/ local label_(s_ptr) register SYMBOL_PTR s_ptr; { register char *temp; char *radmin(); temp = cpystr(lbuff, radmin(s_ptr->s_name)); *temp++ = ':'; if (s_ptr->s_flags & S_GLOBAL) *temp++ = ':'; *temp = '\0'; s_ptr->s_flags |= S_USED; } /************************************************************************* * * * b y t e _ * --------- * * Output a .byte directive (always from the IR). * *************************************************************************/ local byte_() { arg_i(); if (IR->d_flags & A_RELA) arg_s(IR->d_text); else arg_o(IR->d_value); set_ptr(F_ARGS, wbuf); set_ptr(F_INST, ".byte"); } /************************************************************************* * * * w o r d _ * --------- * * Output a .word directive (always from the IR). * *************************************************************************/ local word_() { arg_i(); if (IR->d_flags & A_RELA) arg_s(IR->d_text); else arg_o(IR->d_value); set_ptr(F_ARGS, wbuf); set_ptr(F_INST, word); } /************************************************************************* * * * l i m i t _ * ----------- * * Output a .limit directive. * *************************************************************************/ local limit_() { register int flags; arg_i(); if ((flags = get_item(D1)) & (A_RELA | A_B) || (flags & A_TYPE) != A_DATA || D1->d_value) warn(".LIMIT not followed by blank word\n"); set_ptr(F_ARGS, wbuf); set_ptr(F_INST, ".limit"); } /************************************************************************* * * * p s e c t * --------- * * Handle the output of a psect command. Note: its a ... * * ".asect" if * a) Name is ". abs." * b) psect is ABS and nothing else * ".csect" if * a) Name is blank * b) Attributes are REL * ".csect name" if * a) Name is non-blank * b) Attributes are GBL, REL, OVR * ".psect [name]" if * not one of the above. * *************************************************************************/ local psect_desc(p_ptr) register PSECT_PTR p_ptr; { register int flags, temp; char *radnam(), *radmin(); arg_i(); set_ptr(F_ARGS, wbuf); p_ptr->p_flags = (flags = p_ptr->p_flags) | P_USED; if (p_ptr->p_name[0] == 0127401 && /* .rad50 -. a- */ p_ptr->p_name[1] == 0007624 && /* .rad50 -bs.- */ !(flags & P_REL)) { /* ..and its ABS */ set_ptr(F_INST, ".asect"); return; } temp = p_ptr->p_name[0] | p_ptr->p_name[1]; arg_s(radmin(p_ptr->p_name)); if (((flags & P_ALL) == (P_GBL | P_REL | P_OVR) && temp) || ((flags & P_ALL) == P_REL && !temp)) { set_ptr(F_INST, ".csect"); return; } set_ptr(F_INST, ".psect"); if (!(flags & P_USED)) p_type(flags); } /************************************************************************* * * * p _ t y p e * ----------- * * Output the flags for a psect. * *************************************************************************/ local p_type(flags) register int flags; { arg_s(flags & P_RO ? ", ro" : ", rw" ); arg_s(flags & P_D ? ", d" : ", i" ); arg_s(flags & P_GBL ? ", gbl" : ", lcl"); arg_s(flags & P_REL ? ", rel" : ", abs"); arg_s(flags & P_OVR ? ", ovr" : ", con"); arg_s(flags & P_HIGH ? ", high" : ", low"); } /************************************************************************* * * * p _ r e s e t * ------------- * * Reset all psect attributes for a new pass. * *************************************************************************/ local p_reset() { register PSECT_PTR p_ptr; register int i; for (i = 0; i <= seg_max; i++) { p_ptr = &segtbl[i]; p_ptr->p_dot = 0; p_ptr->p_flags &= ~P_USED; } } /************************************************************************* * * * d o _ e n d * ----------- * * Handle the .end directive. We try to be flash here. If the trans- * fer address was not 000001 in ". abs." and code is being output, * then get the name of the label at that location. * *************************************************************************/ local do_end() { SYMBOL_PTR s_ptr; SYMBOL_PTR lookup(); char *radmin(); putb(); line_new(); IR->d_value = 1; IR->d_flags = A_VALID; set_ptr(F_INST, ".end"); if (t_addr.s_flags & S_VALID && code) { s_ptr = lookup(t_addr.s_name[0], IR->d_value = t_addr.s_offset); set_ptr(F_ARGS, radmin(s_ptr->s_name)); if (t_addr.s_flags & P_REL) IR->d_flags |= A_R; } line_out(F_LNUM | F_IR | F_INST | F_ARGS); }