/**************************************************************************** * dcc project disassembler * (C) Cristina Cifuentes, Mike van Emmerik, Jeff Ledermann ****************************************************************************/ #include "disassem.h" #include "dcc.h" #include "msvc_fixes.h" #include "symtab.h" #include "project.h" #include #include #include #include #include #include #include #include #include // Note: for the time being, there is no interactive disassembler // for unix using namespace std; #define POS_LAB 15 /* Position of label */ #define POS_OPC 20 /* Position of opcode */ #define POS_OPR 25 /* Position of operand */ #define WID_PTR 10 /* Width of the "xword ptr" lingo */ #define POS_OPR2 POS_OPR+WID_PTR /* Position of operand after "xword ptr" */ #define POS_CMT 54 /* Position of comment */ static const char *szFlops0C[] = { "FCHS", "FABS", "???", "???", "FTST", "FXAM", "???", "???" }; static const char *szFlops0D[] = { "FLD1", "FLDL2T","FLDL2E","FLDP1", "FLDLG2","FLDLN2","FLDZ", "???" }; static const char *szFlops0E[] = { "F2XM1", "FYL2X", "FPTAN", "FPATAN","FXTRACT","FPREM1","FDECSTP","FINCSTP" }; static const char *szFlops0F[] = { "FPREM", "FYLXP1","FSQRT", "FSINCOS","FRNDINT","FSCALE","FSIN","FCOS" }; static const char *szFlops15[] = { "???", "FUCOMPP", "???", "???", "???", "???", "???", "???" }; static const char *szFlops1C[] = { "???", "???", "FCLEX", "FINIT", "FTST", "FXAM", "???", "???" }; static const char *szFlops33[] = { "???", "FCOMPP", "???", "???", "???", "???", "???", "???" }; static const char *szFlops3C[] = { "FSTSWAX","???", "???", "???", "???", "???", "???", "???" }; static const char *szPtr[2] = { "word ptr ", "byte ptr " }; static void formatRM(QTextStream & p, const LLOperand &pm); static QTextStream & strDst(QTextStream & os, uint32_t flg, const LLOperand &pm); static char *strHex(uint32_t d); //static int checkScanned(uint32_t pcCur); //static void setProc(Function * proc); //static void dispData(uint16_t dataSeg); bool callArg(uint16_t off, char *temp); /* Check for procedure name */ //static FILE *dis_g_fp; static CIcodeRec pc; static int cb, j, numIcode, allocIcode; static map pl; static uint32_t nextInst; static bool fImpure; //static int g_lab; static Function * pProc; /* Points to current proc struct */ struct POSSTACK_ENTRY { int ic; /* An icode offset */ Function * pProc; /* A pointer to a PROCEDURE structure */ } ; static vector posStack; /* position stack */ //static uint8_t iPS; /* Index into the stack */ // These are "curses equivalent" functions. (Used to use curses for all this, // but it was too much of a distribution hassle #define printfd(x) printf(x) #define dis_newline() printf("\n") #define dis_show() // Nothing to do unless using Curses void LLInst::findJumpTargets(CIcodeRec &_pc) { if (testFlags(I) and not testFlags(JMP_ICODE) and isJmpInst()) { /* Replace the immediate operand with an icode index */ iICODE labTgt=_pc.labelSrch(src().getImm2()); if (labTgt!=_pc.end()) { m_src.SetImmediateOp(labTgt->loc_ip); /* This icode is the target of a jump */ labTgt->ll()->setFlags(TARGET); setFlags(JMP_ICODE); /* So its not done twice */ } else { /* This jump cannot be linked to a label */ setFlags(NO_LABEL); } } } /***************************************************************************** * disassem - Prints a disassembled listing of a procedure. * pass == 1 generates output on file .a1 * pass == 2 generates output on file .a2 * pass == 3 generates output on file .b ****************************************************************************/ void Disassembler::disassem(Function * ppProc) { pProc = ppProc; /* Save the passes pProc */ createSymTables(); allocIcode = numIcode = pProc->Icode.size(); cb = allocIcode * sizeof(ICODE); if (numIcode == 0) { return; /* No Icode */ } /* Open the output file (.a1 or .a2 only) */ if (pass != 3) { auto p = (pass == 1)? asm1_name: asm2_name; m_disassembly_target = new QFile(p); if(!m_disassembly_target->open(QFile::WriteOnly|QFile::Text|QFile::Append)) { fatalError(CANNOT_OPEN, p.toStdString().c_str()); } m_fp.setDevice(m_disassembly_target); } /* Create temporary code array */ // Mike: needs objectising! pc=pProc->Icode; if (pass == 1) { /* Bind jump offsets to labels */ //for (i = 0; i < numIcode; i++) for( ICODE &icode : pc) { LLInst *ll=icode.ll(); ll->findJumpTargets(pc); } } /* Create label array to keep track of location => label name */ pl.clear(); /* Write procedure header */ if (pass != 3) { const char * near_far=(pProc->flg & PROC_FAR)? "FAR": "NEAR"; m_fp << "\t\t"<name<<" PROC "<< near_far<<"\n"; } /* Loop over array printing each record */ nextInst = 0; for( ICODE &icode : pc) { this->dis1Line(*icode.ll(),icode.loc_ip,pass); } /* Write procedure epilogue */ if (pass != 3) { m_fp << "\n\t\t"<name<<" ENDP\n\n"; m_fp.setDevice(nullptr); m_disassembly_target->close(); delete m_disassembly_target; } pc.clear(); destroySymTables(); } /**************************************************************************** * dis1Line() - disassemble one line to stream fp * * i is index into Icode for this proc * * It is assumed that icode i is already scanned * ****************************************************************************/ void Disassembler::dis1Line(LLInst &inst,int loc_ip, int pass) { PROG &prog(Project::get()->prog); QString oper_contents; QTextStream oper_stream(&oper_contents); QString hex_bytes; QString result_contents; QTextStream result_stream(&result_contents); QString opcode_with_mods; QString operands_contents; QTextStream operands_s(&operands_contents); oper_stream.setNumberFlags(QTextStream::UppercaseBase|QTextStream::UppercaseDigits); /* Disassembly stage 1 -- * Do not try to display NO_CODE entries or synthetic instructions, * other than JMPs, that have been introduced for def/use analysis. */ if ((option.asm1) and ( inst.testFlags(NO_CODE) or (inst.testFlags(SYNTHETIC) and (inst.getOpcode() != iJMP)))) { return; } else if (inst.testFlags(NO_CODE)) { return; } if (inst.testFlags(TARGET | CASE)) { if (pass == 3) cCode.appendCode("\n"); /* Print to c code buffer */ else m_fp<< "\n"; /* No, print to the stream */ } /* Find next instruction label and print hex bytes */ if (inst.testFlags(SYNTHETIC)) nextInst = inst.label; else { cb = (uint32_t) inst.numBytes; nextInst = inst.label + cb; /* Output hex code in program image */ if (pass != 3) { for (j = 0; j < cb; j++) { hex_bytes += QString("%1").arg(uint16_t(prog.image()[inst.label + j]),2,16,QChar('0')).toUpper(); } hex_bytes += ' '; } } oper_stream.setFieldWidth(POS_LAB); oper_stream.setFieldAlignment(QTextStream::AlignLeft); oper_stream << hex_bytes; /* Check if there is a symbol here */ selectTable(Label); oper_stream.setFieldWidth(5); // align for the labels { QString lab_contents; QTextStream lab_stream(&lab_contents); if (readVal(lab_stream, inst.label, nullptr)) { lab_stream << ':'; /* Also removes the null */ } else if (inst.testFlags(TARGET)) /* Symbols override Lnn labels */ { /* Print label */ if (pl.count(loc_ip)==0) { pl[loc_ip] = ++g_lab; } lab_stream<< "L"<ll()->label, nullptr)) { break; /* Symbolic label. Done */ } } if (inst.testFlags(NO_LABEL)) { //strcpy(p + WID_PTR, strHex(pIcode->ll()->immed.op)); operands_s<name); operands_s<< qPrintable(oper); } else if (inst.getOpcode() == iCALLF) { operands_s<<"dword ptr "; inst.strSrc(operands_s,true); } else strDst(operands_s,I, inst.src()); break; case iENTER: operands_s< 0 and j < (int)nextInst; j++) { fImpure |= BITMAP(j, BM_DATA); } } result_stream.setFieldWidth(54); result_stream.setFieldAlignment(QTextStream::AlignLeft); oper_stream.flush(); result_stream << oper_contents; result_stream.setFieldWidth(0); /* Check for user supplied comment */ selectTable(Comment); QString cbuf_contents; QTextStream cbuf(&cbuf_contents); if (readVal(cbuf, inst.label, nullptr)) { cbuf.flush(); result_stream <<"; "<<*cbuf.string(); } else if (fImpure or (inst.testFlags(SWITCH | CASE | SEG_IMMED | IMPURE | SYNTHETIC | TERMINATES))) { if (inst.testFlags(CASE)) { result_stream << ";Case l"<< inst.caseEntry; } if (inst.testFlags(SWITCH)) { result_stream << ";Switch "; } if (fImpure) { result_stream << ";Accessed as data "; } if (inst.testFlags(IMPURE)) { result_stream << ";Impure operand "; } if (inst.testFlags(SEG_IMMED)) { result_stream << ";Segment constant"; } if (inst.testFlags(TERMINATES)) { result_stream << ";Exit to DOS"; } } /* Comment on iINT icodes */ if (inst.getOpcode() == iINT) inst.writeIntComment(result_stream); /* Display output line */ if(pass==3) { /* output to .b code buffer */ if (inst.testFlags(SYNTHETIC)) result_stream<<";Synthetic inst"; if (pass == 3) { /* output to .b code buffer */ cCode.appendCode("%s\n", qPrintable(result_contents)); } } else { char buf[12]; /* output to .a1 or .a2 file */ if (not inst.testFlags(SYNTHETIC) ) { sprintf(buf,"%03d %06X",loc_ip, inst.label); } else /* SYNTHETIC instruction */ { sprintf(buf,"%03d ",loc_ip); result_stream<<";Synthetic inst"; } result_stream.flush(); m_fp< 9)? "h": ""); return (buf + (buf[1] <= '9')); } /**************************************************************************** * interactDis - interactive disassembler * ****************************************************************************/ void interactDis(Function * initProc, int initIC) { QString procname = "UNKNOWN"; if(initProc) procname = initProc->name; qDebug() << "Wanted to start interactive disasassembler for "<= 0x20) and (op <= 0x27)) { /* This is the ST(i), ST form. */ out << "ST("<