diff options
-rw-r--r-- | common.h | 18 | ||||
-rw-r--r-- | disasm.cpp | 61 | ||||
-rw-r--r-- | disasm.h | 22 | ||||
-rw-r--r-- | main.cpp | 61 |
4 files changed, 108 insertions, 54 deletions
@@ -12,8 +12,22 @@ struct Settings { bool raw_data_comment{}; }; -constexpr unsigned kRefRelMask = 1; -constexpr unsigned kRefAbsMask = 2; +using RefKindMask = unsigned; + +constexpr RefKindMask kRef1RelMask = (1 << 0); // For first argument +constexpr RefKindMask kRef1AbsMask = (1 << 1); // For first argument +constexpr RefKindMask kRef2RelMask = (1 << 2); // For second argument +constexpr RefKindMask kRef2AbsMask = (1 << 3); // For second argument +/// Indicates whether instruction is a call or just a branch, for any argument. +/// Calls are BSR and JSR, branches are DBcc, Bcc and JMP. +constexpr RefKindMask kRefCallMask = (1 << 4); +constexpr RefKindMask kRefReadMask = (1 << 5); // For any argument +constexpr RefKindMask kRefWriteMask = (1 << 6); // For any argument +constexpr RefKindMask kRefRelMask = kRef1RelMask | kRef2RelMask; +constexpr RefKindMask kRefAbsMask = kRef1AbsMask | kRef2AbsMask; +constexpr RefKindMask kRef1Mask = kRef1RelMask | kRef1AbsMask; // For first argument +constexpr RefKindMask kRef2Mask = kRef2RelMask | kRef2AbsMask; // For second argument +constexpr RefKindMask kRefDataMask = kRefReadMask | kRefWriteMask; constexpr size_t kInstructionSizeStepBytes = 2; constexpr size_t kRomSizeBytes = 4 * 1024 * 1024; constexpr size_t kDisasmMapSizeElements = kRomSizeBytes / kInstructionSizeStepBytes; @@ -178,23 +178,23 @@ static size_t disasm_jsr_jmp( case AddrMode::kWord: // 4eb8 / 4ef8 { const uint32_t ref_addr = static_cast<uint32_t>(a.lword); - node.ref_addr = ref_addr; - node.has_ref = true; + node.ref1_addr = ref_addr; + node.ref_kinds = kRef1AbsMask; } break; case AddrMode::kLong: // 4eb9 / 4ef9 { const uint32_t ref_addr = static_cast<uint32_t>(a.lword); - node.ref_addr = ref_addr; - node.has_ref = true; + node.ref1_addr = ref_addr; + node.ref_kinds = kRef1AbsMask; } break; case AddrMode::kD16PCAddr: // 4eba / 4efa { const uint32_t ref_addr = node.offset + kInstructionSizeStepBytes + static_cast<uint32_t>(a.d16_pc.d16); - node.ref_addr = ref_addr; - node.has_ref = true; + node.ref1_addr = ref_addr; + node.ref_kinds = kRef1RelMask; } break; case AddrMode::kD8PCXiAddr: // 4ebb / 4efb @@ -205,7 +205,7 @@ static size_t disasm_jsr_jmp( return disasm_verbatim(node, instr); } const bool is_jmp = instr & 0x40; - node.is_call = !is_jmp; + node.ref_kinds |= is_jmp ? 0 : kRefCallMask; node.op = Op::Typical(is_jmp ? OpCode::kJMP : OpCode::kJSR, OpSize::kNone, a); return node.size = kInstructionSizeStepBytes + a.Size(opsize); } @@ -299,9 +299,17 @@ static size_t disasm_lea( return disasm_verbatim(node, instr); case AddrMode::kD16AnAddr: case AddrMode::kD8AnXiAddr: + break; case AddrMode::kWord: case AddrMode::kLong: + node.ref1_addr = static_cast<uint32_t>(addr.lword); + node.ref_kinds = kRef1AbsMask | kRefReadMask; + break; case AddrMode::kD16PCAddr: + node.ref1_addr = node.offset + kInstructionSizeStepBytes + + static_cast<uint32_t>(addr.d16_pc.d16); + node.ref_kinds = kRef1RelMask | kRefReadMask; + break; case AddrMode::kD8PCXiAddr: break; case AddrMode::kImmediate: @@ -369,9 +377,8 @@ static size_t disasm_bra_bsr_bcc( const uint32_t ref_addr = static_cast<uint32_t>(node.offset + dispmt); Condition condition = static_cast<Condition>((instr >> 8) & 0xf); // False condition Indicates BSR - node.is_call = (condition == Condition::kF); - node.ref_addr = ref_addr; - node.has_ref = true; + node.ref1_addr = ref_addr; + node.ref_kinds = kRef1RelMask | ((condition == Condition::kF) ? kRefCallMask : 0); node.op = Op{OpCode::kBcc, opsize, condition, Arg::Displacement(dispmt)}; return node.size; } @@ -992,8 +999,8 @@ static size_t disasm_dbcc(DisasmNode &node, const uint16_t instr, const DataBuff } const int16_t dispmt_raw = GetI16BE(code.buffer + node.offset + kInstructionSizeStepBytes); const int32_t dispmt = dispmt_raw + kInstructionSizeStepBytes; - node.ref_addr = static_cast<uint32_t>(node.offset + dispmt); - node.has_ref = true; + node.ref2_addr = static_cast<uint32_t>(node.offset + dispmt); + node.ref_kinds = kRef2RelMask; node.op = Op{ OpCode::kDBcc, OpSize::kNone, @@ -1499,11 +1506,16 @@ size_t DisasmNode::Disasm(const DataBuffer &code) return this->size; } size = kInstructionSizeStepBytes; - has_ref = 0; - ref_addr = 0; - is_call = false; + ref_kinds = 0; + ref1_addr = 0; + ref2_addr = 0; const uint16_t instr = GetU16BE(code.buffer + this->offset); - return m68k_disasm(*this, instr, code); + if (this->type == TracedNodeType::kInstruction) { + return m68k_disasm(*this, instr, code); + } else { + // Data should not be disassembled + return disasm_verbatim(*this, instr); + } } size_t DisasmNode::DisasmAsRaw(const DataBuffer &code) @@ -1511,9 +1523,9 @@ size_t DisasmNode::DisasmAsRaw(const DataBuffer &code) // We assume that machine have no MMU and ROM data always starts with 0 assert(this->offset < code.occupied_size); size = kInstructionSizeStepBytes; - has_ref = 0; - ref_addr = 0; - is_call = false; + ref_kinds = 0; + ref1_addr = 0; + ref2_addr = 0; const uint16_t instr = GetU16BE(code.buffer + this->offset); return disasm_verbatim(*this, instr); } @@ -1735,7 +1747,7 @@ static size_t snprint_reg_mask( int Arg::SNPrint( char *const buf, const size_t bufsz, - const unsigned ref_kinds, + const RefKindMask ref_kinds, const uint32_t self_addr, const uint32_t ref_addr) const { @@ -1828,19 +1840,20 @@ int Arg::SNPrint( int Op::FPrint( FILE *const stream, - const unsigned ref_kinds, + const RefKindMask ref_kinds, const uint32_t self_addr, - const uint32_t ref_addr) const + const uint32_t ref1_addr, + const uint32_t ref2_addr) const { assert(opcode != OpCode::kNone); char mnemonic_str[kMnemonicBufferSize]{}; OpcodeSNPrintf(mnemonic_str, kMnemonicBufferSize, opcode, condition, size_spec); if (arg1.type != ArgType::kNone) { char arg1_str[kArgsBufferSize]{}; - arg1.SNPrint(arg1_str, kArgsBufferSize, ref_kinds, self_addr, ref_addr); + arg1.SNPrint(arg1_str, kArgsBufferSize, ref_kinds & kRef1Mask, self_addr, ref1_addr); if (arg2.type != ArgType::kNone) { char arg2_str[kArgsBufferSize]{}; - arg2.SNPrint(arg2_str, kArgsBufferSize, ref_kinds, self_addr, ref_addr); + arg2.SNPrint(arg2_str, kArgsBufferSize, ref_kinds & kRef2Mask, self_addr, ref2_addr); return fprintf(stream, " %s %s,%s", mnemonic_str, arg1_str, arg2_str); } else { return fprintf(stream, " %s %s", mnemonic_str, arg1_str); @@ -297,7 +297,7 @@ struct Arg { int SNPrint( char *buf, size_t bufsz, - unsigned ref_kinds = 0, + RefKindMask ref_kinds = 0, uint32_t self_addr = 0, uint32_t ref_addr = 0) const; }; @@ -314,8 +314,10 @@ constexpr size_t kArgsBufferSize = 80; enum class ReferenceType { kUnknown = 0, - kBranch, kCall, + kBranch, + kRead, + kWrite, }; struct ReferenceRecord { @@ -350,9 +352,10 @@ struct Op { } int FPrint( FILE *, - unsigned ref_kinds = 0, + RefKindMask ref_kinds = 0, uint32_t self_addr = 0, - uint32_t ref_addr = 0) const; + uint32_t ref1_addr = 0, + uint32_t ref2_addr = 0) const; }; struct DisasmNode { @@ -362,12 +365,11 @@ struct DisasmNode { /// Instruction size in bytes size_t size{kInstructionSizeStepBytes}; /// Indicates whether `ref_addr` should be interpreted and how - bool has_ref{}; - /// Absolute address of where to branch to - uint32_t ref_addr{}; - /// Indicates whether instruction is a call (BSR, JSR) or just a branch - /// (Bcc, JMP) if `has_branch_addr` is set - bool is_call{}; + RefKindMask ref_kinds{}; + /// Absolute address of reference + uint32_t ref1_addr{}; + /// Absolute address of reference + uint32_t ref2_addr{}; ReferenceNode *ref_by{}; ReferenceNode *last_ref_by{}; Op op{}; @@ -58,10 +58,16 @@ static uint32_t AlignInstructionAddress(const uint32_t offset) return offset & ~1UL; } -DisasmNode *DisasmMap::insertTracedNode(uint32_t offset, TracedNodeType type) +DisasmNode *DisasmMap::insertTracedNode(const uint32_t offset, const TracedNodeType type) { auto *node = findNodeByOffset(offset); if (node) { + // Instruction nodes take precedence over data nodes. If a node that + // was previously accessed only as data now turns out to be an + // instruction, then it must become an instruction node. + if (type == TracedNodeType::kInstruction && node->type != TracedNodeType::kInstruction) { + *const_cast<TracedNodeType*>(&node->type) = type; + } return node; } node = new DisasmNode(DisasmNode{type, AlignInstructionAddress(offset)}); @@ -107,9 +113,14 @@ void DisasmMap::Disasm(const DataBuffer &code, const Settings &) node->DisasmAsRaw(code); } // FIXME implement deep graph walk for DisasmMapType::kTraced case - if (node->has_ref && node->ref_addr < code.occupied_size) { - auto *const ref_node = insertTracedNode( - node->ref_addr, TracedNodeType::kInstruction); + const bool has_code_ref = + ((node->ref_kinds & kRef1Mask) && node->ref1_addr < code.occupied_size) || + ((node->ref_kinds & kRef2Mask) && node->ref2_addr < code.occupied_size); + if (has_code_ref) { + const uint32_t ref_addr = (node->ref_kinds & kRef1Mask) ? node->ref1_addr : node->ref2_addr; + const TracedNodeType type = (node->ref_kinds & (kRefReadMask | kRefWriteMask)) + ? TracedNodeType::kData : TracedNodeType::kInstruction; + auto *const ref_node = insertTracedNode(ref_addr, type); const auto size = ref_node->Disasm(code); assert(size >= kInstructionSizeStepBytes); if (canBeAllocated(*ref_node)) { @@ -120,8 +131,14 @@ void DisasmMap::Disasm(const DataBuffer &code, const Settings &) } else { ref_node->DisasmAsRaw(code); } - ref_node->AddReferencedBy( - node->offset, node->is_call ? ReferenceType::kCall : ReferenceType::kBranch); + const auto ref_type = (node->ref_kinds & kRefCallMask) + ? ReferenceType::kCall + : (node->ref_kinds & kRefReadMask) + ? ReferenceType::kRead + : (node->ref_kinds & kRefWriteMask) + ? ReferenceType::kWrite + : ReferenceType::kBranch; + ref_node->AddReferencedBy(node->offset, ref_type); } i += node->size; } @@ -167,9 +184,11 @@ static size_t RenderRawDataComment( static const char *ReferenceTypeToString(ReferenceType type) { switch (type) { - case ReferenceType::kUnknown: return "UNKN"; - case ReferenceType::kBranch: return "BRANCH"; + case ReferenceType::kUnknown: return "UNKNOWN"; case ReferenceType::kCall: return "CALL"; + case ReferenceType::kBranch: return "BRANCH"; + case ReferenceType::kRead: return "READ"; + case ReferenceType::kWrite: return "WRITE"; } return "UNKN"; } @@ -225,21 +244,27 @@ static void RenderDisassembly( } fprintf(output, "\n"); } else { - const bool with_reference = node->has_ref && s.marks && - (s.abs_marks || s.rel_marks); - const auto *referenced = disasm_map.FindNodeByOffset(node->ref_addr); - if (with_reference && referenced) { - const uint32_t ref_addr = referenced->offset; - const unsigned ref_kinds = ((s.abs_marks ? kRefAbsMask : 0) | - (s.rel_marks ? kRefRelMask : 0)); - node->op.FPrint(output, ref_kinds, node->offset, ref_addr); + const bool with_ref = node->ref_kinds && s.marks && (s.abs_marks || s.rel_marks); + const auto *ref1 = (node->ref_kinds & kRef1Mask) + ? disasm_map.FindNodeByOffset(node->ref1_addr) : nullptr; + const auto *ref2 = (node->ref_kinds & kRef2Mask) + ? disasm_map.FindNodeByOffset(node->ref2_addr) : nullptr; + const uint32_t ref1_addr = (with_ref && ref1) ? ref1->offset : 0; + const uint32_t ref2_addr = (with_ref && ref2) ? ref2->offset : 0; + if (with_ref && (ref1 || ref2)) { + const RefKindMask ref_kinds = + ((s.abs_marks ? (node->ref_kinds & kRefAbsMask) : 0) | + (s.rel_marks ? (node->ref_kinds & kRefRelMask) : 0)); + node->op.FPrint(output, ref_kinds, node->offset, ref1_addr, ref2_addr); } else { node->op.FPrint(output); } } - if (node->has_ref && s.xrefs_to) { + if (node->ref_kinds && s.xrefs_to) { char ref_addr_str[12]{}; - snprintf(ref_addr_str, sizeof(ref_addr_str), " .L%08x", node->ref_addr); + const uint32_t ref_addr = + (node->ref_kinds & kRef1Mask) ? node->ref1_addr : node->ref2_addr; + snprintf(ref_addr_str, sizeof(ref_addr_str), " .L%08x", ref_addr); fprintf(output, " |%s", ref_addr_str); } if (s.raw_data_comment) { |