#include "opcodes.h" #include "disassembler.h" #include "role_asm_opcode.h" #include "role_asm_operand.h" #include "util.h" #include #include #include #include Disassembler::Disassembler(AttributedMemory &mem) { m_mem = &mem; m_memusagemap.clearData(); } QList Disassembler::disassemble(quint16 from, quint16 to, QList entryPoints, bool processRecursively) { m_from = from; m_to = to; QList retval; MemoryUsageMap memUse; bool stopping = false; quint16 next = from; while (entryPoints.count()) { next = entryPoints.takeFirst(); m_stack.push(next); } while (!stopping) { 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"; m_stack.push(item.targetAddress()); } if (item.isJsr() && !item.canNotFollow()) { if (item.targetAddress() <= to && item.targetAddress() >= from) //TODO: Remove this to not limit disassembly to program range { if (m_stack.push(item.targetAddress())) { qDebug() << "Appending" << uint16ToHex(item.targetAddress()) << "to jump table"; } else { qDebug() << "Not adding" << uint16ToHex(item.targetAddress()) << "to jump table"; } } } 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 (next >= to) stopping = true; } m_memusagemap.merge(memUse); if (processRecursively) while (!m_stack.isEmpty()) { quint16 num = m_stack.pop(); 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."; } } } std::sort(retval.begin(),retval.end()); if (processRecursively) { m_jumplines = m_jlm.buildJumpLines(); qDebug() << "Num Channels: " << m_jlm.getNumJumpLineChannels(); } return retval; } bool Disassembler::disassembleOp(quint16 address, DisassembledItem &retval, MemoryUsageMap *memuse) { if (memuse) { if ((*memuse)[address].testFlag(Operation)) return false; } quint8 opcode = m_mem->at(address); retval.setOpcode(opcode); if (!m_mem->hasRoleAt(address,RoleAsmOpcode::RoleID)) { auto opRole = new RoleAsmOpcode(); if (!m_mem->setRoleAt(address,opRole)) { qWarning("Cannot set role %s at address %s for opcode %s", qPrintable(opRole->name()), qPrintable(uint16ToHex(address)), qPrintable(uint8ToHex(opcode))); delete opRole; } } if (OpCodes::isIndirectJump(opcode)) // Indirect jumps { m_jlm.addJump(address,address,IsUnknownJump,m_from,m_to); retval.setCanNotFollow(true); } QString disassemblyLine; QString hexValueString; QByteArray hexValues; hexValues.append(opcode); // Prepare Op Code arguments auto numArgs = OpCodes::numArgs(opcode); for (int idx = 1; idx < numArgs+1; idx++) { qDebug() << "Opcode at " << uint16ToHex(idx + address); auto oprndRole = new RoleAsmOperand(); if (numArgs == 1) { oprndRole->setOperandType(RoleAsmOperand::Type::Byte); } else { if (idx == 1) { oprndRole->setOperandType(RoleAsmOperand::Type::WordLo); } else // idx == 2 { oprndRole->setOperandType(RoleAsmOperand::Type::WordHi); } } if (!m_mem->setRoleAt(address+idx,oprndRole)) { qWarning(">> Cannot set role %s at address %s", qPrintable(oprndRole->name()), qPrintable(uint16ToHex(address))); delete oprndRole; } quint8 val = m_mem->at(address+idx); hexValues.append(val); } if (memuse) { (*memuse)[address].setFlag(Operation); for (int idx = 1; idx < OpCodes::numArgs(opcode)+1; idx++) { (*memuse)[address+idx].setFlag(OperationArg); } } quint16 argval = 0; if (numArgs == 1) { argval = (quint8) hexValues[1]; } else if (numArgs == 2) { argval = makeWord(hexValues[1],hexValues[2]); } for (int idx = 0; idx < hexValues.length(); idx++) { hexValueString.append(QString("%1 ").arg(uint8ToHex(hexValues[idx]))); } retval.setNextContiguousAddress(address+1+numArgs); retval.setNextFlowAddress(address+1+numArgs); QString mnemonic = OpCodes::mnemonic(opcode); // Disassemble instruction switch (OpCodes::addressMode(opcode)) { case AM_InvalidOp: { disassemblyLine = OpCodes::mnemonic(opcode); retval.setIsInvalidOp(true); break; } case AM_Absolute:{ disassemblyLine = QString("%1 _ARG16_").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_AbsoluteIndexedIndirect:{ disassemblyLine = QString("%1 (_ARG16_,x)").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_AbsoluteIndexedWithX:{ disassemblyLine = QString("%1 _ARG16_,x").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_AbsoluteIndexedWithY:{ disassemblyLine = QString("%1 _ARG16_,y").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_AbsoluteIndirect:{ disassemblyLine = QString("%1 (_ARG16_)").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_Immediate:{ disassemblyLine = QString("%1 #%2").arg(mnemonic).arg((quint8) hexValues[1],2,16,QChar('0')).toUpper(); retval.setRawArgument(argval); break; } case AM_Implied:{ disassemblyLine = mnemonic; break; } case AM_Accumulator:{ disassemblyLine = mnemonic; break; } case AM_ProgramCounterRelative:{ qint8 offset = (qint8) hexValues[1]; quint16 offsetAddress = address+2+offset; retval.setTargetAddress(offsetAddress); if (opcode == 0x80) // BRA m_jlm.addJump(address,offsetAddress,IsBRA,m_from,m_to); else m_jlm.addJump(address,offsetAddress,IsBranch,m_from,m_to); disassemblyLine = QString("%1 _ARG16_ {%2%3}").arg(mnemonic) .arg((offset<0)?"-":"+") .arg(abs(offset)); retval.setRawArgument(offsetAddress); break; } case AM_ZeroPage:{ disassemblyLine = QString("%1 _ARG8_").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_ZeroPageIndirectIndexedWithY:{ disassemblyLine = QString("%1 (_ARG8_),Y").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_ZeroPageIndexedIndirect:{ disassemblyLine = QString("%1 (_ARG8_,x)").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_ZeroPageIndexedWithX:{ disassemblyLine = QString("%1 _ARG8_,x").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_ZeroPageIndexedWithY:{ disassemblyLine = QString("%1 _ARG8_,y").arg(mnemonic); retval.setRawArgument(argval); break; } case AM_ZeroPageIndirect:{ disassemblyLine = QString("%1 (_ARG8_)").arg(mnemonic); retval.setRawArgument(argval); break; } default:{ disassemblyLine = mnemonic; retval.setIsInvalidOp(true); qDebug() << "Unhandled Address Mode: " << mnemonic; break; } } if (opcode == 0x20 || opcode == 0x4c) // JSR / JMP { retval.setTargetAddress(makeWord(hexValues[1],hexValues[2])); if (opcode == 0x4c) // JMP { qDebug() << "JMP: Adding flow address " << uint16ToHex(makeWord(hexValues[1],hexValues[2])) << "to jump table"; m_jlm.addJump(address,argval,IsJMP,m_from,m_to); m_stack.push(argval); retval.setCanNotFollow(true); } else // JSR { m_jlm.addJump(address,argval,IsJSR,m_from,m_to); } } retval.setAddress(address); retval.setDisassembledString(disassemblyLine.toUpper()); retval.setHexString(hexValueString.trimmed().toUpper().leftJustified(12,' ')); retval.setHexValues(hexValues); return true; } 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); } } } //DisassembledItem::DisassembledItem(AssyInstruction instr) { // m_canNotFollow = false; // setInstruction(instr); //} DisassembledItem::DisassembledItem(quint8 opcode) { m_canNotFollow = false; m_opcode = opcode; } 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_unknown_ta = true; m_raw_arg = 0; m_has_arg = false; m_canNotFollow = false; m_isInvalidOp = false; }