#include "disassembler.h" #include #include #include Disassembler::Disassembler(QByteArray memimage) { m_memimage = memimage, makeOpcodeTable(); m_memusagemap.clearData(); } QList Disassembler::disassemble(quint16 from, quint16 to, QList entryPoints, bool processRecursively) { QList retval; qDebug() << "Disassemble: From"< 0xffff || (idx < from)) { qDebug() << "Breaking."; break; } } #else MemoryUsageMap memuse; bool stopping = false; quint16 next = from; if (entryPoints.count()) { next = entryPoints.takeFirst(); m_jumps.append(entryPoints); } while (!stopping) { // quint8 opcode = m_memimage[next]; // qDebug() << "Opcode: " << uint8ToHex(opcode); DisassembledItem item; bool ok = false; if (next >= from && next <= to) //TODO: Remove this to not limit disassembly to program range ok = disassembleOp(next,item,&memuse); if (ok) { retval.append(item); quint16 flow = item.nextFlowAddress(); qDebug() << uint16ToHex(next) << uint16ToHex(flow); if (item.isBranch()) { qDebug() << "Is Branch"; if (!m_jumps.contains(item.targetAddress())) { m_jumps.append(item.targetAddress()); } } if (item.isJsr() && !item.canNotFollow()) { if (item.targetAddress() <= to) //TODO: Remove this to not limit disassembly to program range if (!m_jumps.contains(item.targetAddress())) { m_jumps.append(item.targetAddress()); } } if (next <= to) //TODO: Remove this to not limit disassembly to program range { next = flow; stopping = item.stopsProcessing(); } else stopping = true; } else { stopping = true; // already processed this address } // if (found.contains(next)) stopping = true; if (next >= to) stopping = true; // if (stopping) { // qDebug() << "Stopping. Stops processing: " // << item.stopsProcessing() // << ", next>=to: " << (next >= to) // << ", alreadyFound: " << ((!ok)?"true":"false"); // } } m_memusagemap.merge(memuse); if (processRecursively) while (m_jumps.size()) { quint16 num = m_jumps.takeFirst(); if (!m_memusagemap[num].testFlag(Operation)) { if (num >= from && num <= to) // TODO: remove this to not limit disassembly to program range { qDebug() << "Calling recursively to"<() << num,false)); qDebug() << "Return from recursive call."; } } } #endif qSort(retval); // QStringList hexdump; // foreach (quint16 adr,m_memusagemap.addressesWhichContain(Operation)) // { // hexdump.append(uint16ToHex(adr)); // } // qDebug() << "Operations:" << hexdump; // hexdump.clear(); // foreach (quint16 adr,m_memusagemap.addressesWhichContain(OperationArg)) // { // hexdump.append(uint16ToHex(adr)); // } // qDebug() << "Operations Args:" << hexdump; return retval; } bool Disassembler::disassembleOp(quint16 address, DisassembledItem &retval, MemoryUsageMap *memuse) { if (memuse) { if ((*memuse)[address].testFlag(Operation)) return false; } quint8 opcode = m_memimage[address]; AssyInstruction op = m_opcodeinfo[opcode]; retval.setInstruction(op); if (opcode == 0x6C || opcode == 0x7c) // Indirect jumps retval.setCanNotFollow(true); QString disassemblyLine; QString hexValueString; QByteArray hexValues; hexValues.append(opcode); // Prepare Op Code arguments for (int idx = 1; idx < op.numArgs()+1; idx++) { quint8 val = m_memimage[address+idx]; hexValues.append(val); } if (memuse) { (*memuse)[address].setFlag(Operation); for (int idx = 1; idx < op.numArgs()+1; idx++) { (*memuse)[address+idx].setFlag(OperationArg); } } quint16 argval = 0; if (op.numArgs() == 1) argval = (quint8) hexValues[1]; else if (op.numArgs() == 2) argval = (quint8) hexValues[1] + ((quint8) hexValues[2] * 256); for (int idx = 0; idx < hexValues.length(); idx++) { hexValueString.append(QString("%1 ").arg((quint8) hexValues[idx],2,16,QChar('0'))); } retval.setNextContiguousAddress(address+1+op.numArgs()); retval.setNextFlowAddress(address+1+op.numArgs()); // Disassemble instruction switch (op.addressMode()) { case AM_InvalidOp: { disassemblyLine = op.mnemonic(); retval.setIsInvalidOp(true); break; } case AM_Absolute:{ disassemblyLine = QString("%1 _ARG16_").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_AbsoluteIndexedIndirect:{ disassemblyLine = QString("%1 (_ARG16_,x)").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_AbsoluteIndexedWithX:{ disassemblyLine = QString("%1 _ARG16_,x").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_AbsoluteIndexedWithY:{ disassemblyLine = QString("%1 _ARG16_,y").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_AbsoluteIndirect:{ disassemblyLine = QString("%1 (_ARG16_)").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_Immediate:{ disassemblyLine = QString("%1 #%2").arg(op.mnemonic()).arg((quint8) hexValues[1],2,16,QChar('0')).toUpper(); retval.setRawArgument(argval); break; } case AM_Implied:{ disassemblyLine = op.mnemonic(); break; } case AM_Accumulator:{ disassemblyLine = op.mnemonic(); break; } case AM_ProgramCounterRelative:{ qint8 offset = (qint8) hexValues[1]; quint16 offsetAddress = address+2+offset; retval.setTargetAddress(offsetAddress); disassemblyLine = QString("%1 _ARG16_ {%2%3}").arg(op.mnemonic()) .arg((offset<0)?"-":"+") .arg(abs(offset)); retval.setRawArgument(offsetAddress); break; } case AM_ZeroPage:{ disassemblyLine = QString("%1 _ARG8_").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_ZeroPageIndirectIndexedWithY:{ disassemblyLine = QString("%1 (_ARG8_),Y").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_ZeroPageIndexedIndirect:{ disassemblyLine = QString("%1 (_ARG8_,x)").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_ZeroPageIndexedWithX:{ disassemblyLine = QString("%1 _ARG8_,x").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_ZeroPageIndexedWithY:{ disassemblyLine = QString("%1 _ARG8_,y").arg(op.mnemonic()); retval.setRawArgument(argval); break; } case AM_ZeroPageIndirect:{ disassemblyLine = QString("%1 (_ARG8_)").arg(op.mnemonic()); retval.setRawArgument(argval); break; } default:{ disassemblyLine = op.mnemonic(); retval.setIsInvalidOp(true); qDebug() << "Unhandled Address Mode: " << op.addressMode(); break; } } if (opcode == 0x20 || opcode == 0x4c) { retval.setTargetAddress(hexValues[2]*256 + hexValues[1]); } if (opcode == 0x4c) { qDebug() << "JMP: Setting next flow address to"<Cy", AM_Immediate); m_opcodeinfo[0x1b] = AssyInstruction(0x1b, "asl/ora", AM_AbsoluteIndexedWithY); m_opcodeinfo[0x2b] = AssyInstruction(0x2b, "and/mov bit7->Cy", AM_Immediate); m_opcodeinfo[0x3b] = AssyInstruction(0x3b, "asl/ora", AM_AbsoluteIndexedWithY); m_opcodeinfo[0x4b] = AssyInstruction(0x4b, "and/lsr A", AM_Immediate); m_opcodeinfo[0x5b] = AssyInstruction(0x5b, "lsr/eor", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x6b] = AssyInstruction(0x6b, "and/ror A", AM_Immediate); m_opcodeinfo[0x7b] = AssyInstruction(0x7b, "ror/adc", AM_AbsoluteIndexedWithY); m_opcodeinfo[0x8b] = AssyInstruction(0x8b, "txa/and", AM_Immediate); m_opcodeinfo[0x9b] = AssyInstruction(0x9b, "sta/stx", AM_AbsoluteIndexedWithY); m_opcodeinfo[0xab] = AssyInstruction(0xab, "lda/ldx", AM_Implied); m_opcodeinfo[0xbb] = AssyInstruction(0xbb, "lda/ldx", AM_Implied); m_opcodeinfo[0xcb] = AssyInstruction(0xcb, "sbx", AM_Immediate); m_opcodeinfo[0xdb] = AssyInstruction(0xdb, "dec/cmp", AM_Absolute); m_opcodeinfo[0xeb] = AssyInstruction(0xeb, "sbc", AM_Immediate); m_opcodeinfo[0xfb] = AssyInstruction(0xfb, "inc/sbc", AM_Absolute); m_opcodeinfo[0x0c] = AssyInstruction(0x0c, "nop", AM_Absolute); m_opcodeinfo[0x1c] = AssyInstruction(0x1c, "nop", AM_Absolute); m_opcodeinfo[0x2c] = AssyInstruction(0x2c, "BIT", AM_Absolute); m_opcodeinfo[0x3c] = AssyInstruction(0x3c, "nop", AM_Absolute); m_opcodeinfo[0x4c] = AssyInstruction(0x4c, "JMP", AM_Absolute); m_opcodeinfo[0x5c] = AssyInstruction(0x5c, "nop", AM_Absolute); m_opcodeinfo[0x6c] = AssyInstruction(0x6c, "JMP", AM_AbsoluteIndirect); m_opcodeinfo[0x7c] = AssyInstruction(0x7c, "nop", AM_Absolute); m_opcodeinfo[0x8c] = AssyInstruction(0x8c, "STY", AM_Absolute); m_opcodeinfo[0x9c] = AssyInstruction(0x9c, "sta/stx", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xac] = AssyInstruction(0xac, "LDY", AM_Absolute); m_opcodeinfo[0xbc] = AssyInstruction(0xbc, "LDY", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xcc] = AssyInstruction(0xcc, "CPY", AM_Absolute); m_opcodeinfo[0xdc] = AssyInstruction(0xdc, "???", AM_InvalidOp); m_opcodeinfo[0xec] = AssyInstruction(0xec, "CPX", AM_Absolute); m_opcodeinfo[0xfc] = AssyInstruction(0xfc, "???", AM_InvalidOp); m_opcodeinfo[0x0d] = AssyInstruction(0x0d, "ORA", AM_Absolute); m_opcodeinfo[0x1d] = AssyInstruction(0x1d, "ORA", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x2d] = AssyInstruction(0x2d, "AND", AM_Absolute); m_opcodeinfo[0x3d] = AssyInstruction(0x3d, "AND", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x4d] = AssyInstruction(0x4d, "EOR", AM_Absolute); m_opcodeinfo[0x5d] = AssyInstruction(0x5d, "EOR", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x6d] = AssyInstruction(0x6d, "ADC", AM_Absolute); m_opcodeinfo[0x7d] = AssyInstruction(0x7d, "ADC", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x8d] = AssyInstruction(0x8d, "STA", AM_Absolute); m_opcodeinfo[0x9d] = AssyInstruction(0x9d, "STA", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xad] = AssyInstruction(0xad, "LDA", AM_Absolute); m_opcodeinfo[0xbd] = AssyInstruction(0xbd, "LDA", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xcd] = AssyInstruction(0xcd, "CMP", AM_Absolute); m_opcodeinfo[0xdd] = AssyInstruction(0xdd, "CMP", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xed] = AssyInstruction(0xed, "SBC", AM_Absolute); m_opcodeinfo[0xfd] = AssyInstruction(0xfd, "SBC", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x0e] = AssyInstruction(0x0e, "ASL", AM_Absolute); m_opcodeinfo[0x1e] = AssyInstruction(0x1e, "ASL", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x2e] = AssyInstruction(0x2e, "ROL", AM_Absolute); m_opcodeinfo[0x3e] = AssyInstruction(0x3e, "ROL", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x4e] = AssyInstruction(0x4e, "LSR", AM_Absolute); m_opcodeinfo[0x5e] = AssyInstruction(0x5e, "LSR", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x6e] = AssyInstruction(0x6e, "ROR", AM_Absolute); m_opcodeinfo[0x7e] = AssyInstruction(0x7e, "ROR", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x8e] = AssyInstruction(0x8e, "STX", AM_Absolute); m_opcodeinfo[0x9e] = AssyInstruction(0x9e, "sta/stx", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xae] = AssyInstruction(0xae, "LDX", AM_Absolute); m_opcodeinfo[0xbe] = AssyInstruction(0xbe, "LDX", AM_AbsoluteIndexedWithY); m_opcodeinfo[0xce] = AssyInstruction(0xce, "DEC", AM_Absolute); m_opcodeinfo[0xde] = AssyInstruction(0xde, "DEC", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xee] = AssyInstruction(0xee, "INC", AM_Absolute); m_opcodeinfo[0xfe] = AssyInstruction(0xfe, "INC", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x0f] = AssyInstruction(0x0f, "asl/ora", AM_Absolute); m_opcodeinfo[0x1f] = AssyInstruction(0x1f, "asl/ora", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x2f] = AssyInstruction(0x2f, "rol/and", AM_Absolute); m_opcodeinfo[0x3f] = AssyInstruction(0x3f, "rol/and", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x4f] = AssyInstruction(0x4f, "lsr/eor", AM_Absolute); m_opcodeinfo[0x5f] = AssyInstruction(0x5f, "lsr/eor", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x6f] = AssyInstruction(0x6f, "ror/adc", AM_Absolute); m_opcodeinfo[0x7f] = AssyInstruction(0x7f, "ror/adc", AM_AbsoluteIndexedWithX); m_opcodeinfo[0x8f] = AssyInstruction(0x8f, "sta/stx", AM_Absolute); m_opcodeinfo[0x9f] = AssyInstruction(0x9f, "sta/stx", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xaf] = AssyInstruction(0xaf, "lda/ldx", AM_Absolute); m_opcodeinfo[0xbf] = AssyInstruction(0xbf, "ldx/ldx", AM_AbsoluteIndexedWithY); m_opcodeinfo[0xcf] = AssyInstruction(0xcf, "dec/cmp", AM_Absolute); m_opcodeinfo[0xdf] = AssyInstruction(0xdf, "dec/cmp", AM_AbsoluteIndexedWithX); m_opcodeinfo[0xef] = AssyInstruction(0xef, "inc/sbc", AM_Absolute); m_opcodeinfo[0xff] = AssyInstruction(0xff, "inc/sbc", AM_AbsoluteIndexedWithX); #endif } void Disassembler::setUnknownToData(quint16 from, quint16 to) { for (int idx = from; idx <= to; idx++) { if (m_memusagemap[idx].testFlag(Unknown)) { m_memusagemap[idx].setFlag(Data); } } } AssyInstruction::AssyInstruction(quint8 opcode, QString mnemonic, AddressMode am) { m_opcode = opcode; m_mnemonic = mnemonic; m_addressMode = am; } quint8 AssyInstruction::numArgs() { switch (m_addressMode) { case AM_Absolute: case AM_AbsoluteIndexedIndirect: case AM_AbsoluteIndexedWithX: case AM_AbsoluteIndexedWithY: case AM_AbsoluteIndirect: return 2; case AM_ProgramCounterRelative: case AM_ZeroPage: case AM_ZeroPageIndirectIndexedWithY: case AM_ZeroPageIndexedIndirect: case AM_ZeroPageIndexedWithX: case AM_ZeroPageIndexedWithY: case AM_ZeroPageIndirect: case AM_Immediate: return 1; case AM_InvalidOp: case AM_Implied: case AM_Accumulator: default: return 0; } } DisassembledItem::DisassembledItem(AssyInstruction instr) { setInstruction(instr); } void DisassembledItem::setInstruction(AssyInstruction instr) { m_instruction = instr; // qDebug() << "Set instruction: " << uint8ToHex(instr.opcode()); // qDebug() << " Copied instr:" << m_instruction.debugStr(); if (instr.opcode() == 0x20) { m_is_jsr = true; } if (instr.opcode() == 0x10) { m_is_branch = true; } // BPL if (instr.opcode() == 0x30) { m_is_branch = true; } // BMI if (instr.opcode() == 0x50) { m_is_branch = true; } // BVC if (instr.opcode() == 0x70) { m_is_branch = true; } // BVS if (instr.opcode() == 0x90) { m_is_branch = true; } // BCC if (instr.opcode() == 0xB0) { m_is_branch = true; } // BCS if (instr.opcode() == 0xD0) { m_is_branch = true; } // BNE if (instr.opcode() == 0xF0) { m_is_branch = true; } // BEQ if (instr.opcode() == 0x80) { m_is_jump = true; } // BRA if (instr.opcode() == 0x4C) { m_is_jump = true; } // JMP a if (instr.opcode() == 0x6C) { m_is_jump = true; } // JMP (a) if (instr.opcode() == 0x7C) { m_is_jump = true; } // JMP (a,x) } QString DisassembledItem::disassembledString() { QString retval = rawDisassembledString(); if (hasArg()) { if (retval.contains("_ARG16_")) { retval.replace("_ARG16_","$"+arg16Str()); } else if (retval.contains("_ARG8_")) { retval.replace("_ARG8_","$"+arg8Str()); } } return retval; } void DisassembledItem::init() { m_address = m_target_address = 0; m_nextContiguousAddress = 0; m_nextFlowAddress = 0; m_is_jump = m_is_branch = m_is_jsr = false; m_unknown_ta = true; m_raw_arg = 0; m_has_arg = false; m_canNotFollow = false; m_isInvalidOp = false; }