diff options
author | Oxore <oxore@protonmail.com> | 2025-02-01 16:56:01 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2025-02-01 18:26:18 +0300 |
commit | c76658ccce753b26bd8dc751f7b7cdeef2ab5b43 (patch) | |
tree | 73dc241fc9da3f2f9db5f0543e7ee578040bebb7 | |
parent | 6769fca1dd90f4e34e1fd6b2256c3795bbcaf658 (diff) |
WIP
-rw-r--r-- | src/disasm.cpp | 157 | ||||
-rw-r--r-- | src/disasm.h | 14 | ||||
-rw-r--r-- | src/m68k.h | 2 |
3 files changed, 140 insertions, 33 deletions
diff --git a/src/disasm.cpp b/src/disasm.cpp index 3d1ac4a..d88a27e 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -37,7 +37,6 @@ DisasmNode::~DisasmNode() DisasmNode &DisasmMap::insertNode(uint32_t address, NodeType type) { - ASSERT(address < _code_size); if (IsInstruction(type)) { address = AlignInstructionAddress(address); } @@ -73,6 +72,65 @@ DisasmNode &DisasmMap::insertNode(uint32_t address, NodeType type) return *node; } +DisasmNode &DisasmMap::insertNodeQuickPeek(uint32_t address, NodeType type) +{ + ASSERT(IsInstruction(type)); + address = AlignInstructionAddress(address); + auto *node = findNodeByAddress(address); + 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. + // XXX: Traced data must not be classified as instruction. But the + // traced data support is yet to come. + if (IsInstruction(type) && !IsInstruction(node->type)) { + if (0 == (node->size & 1) && 0 == (node->address & 1)) { + *const_cast<NodeType*>(&node->type) = type; + // Make sure it is OpCode::kNone so it will be properly disassembled + node->op = Op{}; + } + } + return *node; + } + node = new DisasmNode(DisasmNode::Simple(type, address)); + ASSERT(node); + // Spread across the size + for (size_t o = 0; o < node->size; o++) { + ASSERT(_map[address + o] == nullptr || _map[address + o] == node); + _map[address + o] = node; + } + return *node; +} + +DisasmNode *DisasmMap::mergeNodes(DisasmNode *primary, DisasmNode *secondary) +{ + ASSERT(primary->address < secondary->address); + ASSERT(primary->address + primary->size >= secondary->address); + ASSERT(primary->address + primary->size >= secondary->address + secondary->size); + ReferenceNode *rnode{secondary->ref_by}; + while (rnode) { + for (size_t i = 0; i < rnode->refs_count; i--) { + primary->AddReferencedBy(rnode->refs[i].address, rnode->refs[i].type); + } + ReferenceNode *prev = rnode; + rnode = rnode->next; + delete prev; + } + if (secondary.ref_kinds & kRef1Mask) { + DisasmNode *node = _map[secondary.ref1_addr]; + ASSERT(node); + node->RemoveReferencedBy(secondary.ref1_addr); + } + if (secondary.ref_kinds & kRef2Mask) { + DisasmNode *node = _map[secondary.ref2_addr]; + ASSERT(node); + node->RemoveReferencedBy(secondary.ref2_addr); + } + secondary->ref_by = secondary->last_ref_by = nullptr; + delete secondary; + return primary; +} + DisasmNode &DisasmMap::insertReferencedBy( const uint32_t by_addr, const uint32_t ref_addr, @@ -195,49 +253,40 @@ static constexpr bool IsNextLikelyAnInstruction(const Op &op) op.opcode != OpCode::kSTOP); } -void DisasmMap::Disasm( +void DisasmMap::disasmProper( const DataView &code, const Settings &s, size_t at, bool nested) { - at = AlignInstructionAddress(at); - _code_size = code.size; - ASSERT(_code_size <= kRomSizeBytes); // Some of logic of this function is covered by integration tests in // `test_walk_and_follow_jumps.bash`. bool inside_code_span = nested; - while (at < code.size) { - DisasmNode *node; - if (_type == DisasmMapType::kTraced) { - node = _map[at]; - if (!node) { - if (inside_code_span) { - node = &insertNode(at, NodeType::kTracedInstruction); - } else { - at += kInstructionSizeStepBytes; - continue; - } + for (at = AlignInstructionAddress(at); at < code.size;) { + DisasmNode *node = _map[at]; + if (!node) { + if (!inside_code_span) { + at += kInstructionSizeStepBytes; + continue; } - ASSERT(node->address == at); - } else { node = &insertNode(at, NodeType::kTracedInstruction); } + ASSERT(node->address == at); const bool perform_disasm = node->IsYetToBeHandled(_type) || inside_code_span; if (perform_disasm) { - const auto size = node->Disasm(code, s); - if (canBeAllocated(*node)) { - // Spread across the size - const size_t address = node->address; - for (size_t o = 0; o < size; o++) { - ASSERT(_map[address + o] == nullptr || _map[address + o] == node); - _map[address + o] = node; - } - } else { + node->Disasm(code, s); + if (!canBeAllocated(*node)) { node->DisasmAsRaw(code); } + // Spread across the size + const size_t size = node->size; + const size_t address = node->address; + for (size_t o = 0; o < size; o++) { + ASSERT(_map[address + o] == nullptr || _map[address + o] == node); + _map[address + o] = node; + } } inside_code_span = s.walk && IsNextLikelyAnInstruction(node->op); at += node->size; // NOTE: There is not much information about a reference passed further, - // so just don't add a reference of immediate if s.imm_labels is false + // so just don't add a reference of immediate if s.imm_labels is not // enabled. const bool has_ref1 = (node->ref_kinds & kRef1ImmMask) ? s.imm_labels @@ -251,7 +300,7 @@ void DisasmMap::Disasm( node->address, node->ref1_addr, type, ref_type); if (ref_node.IsYetToBeHandled(_type)) { if (s.follow_jumps) { - Disasm(code, s, ref_node.address, true); + disasmProper(code, s, ref_node.address, true); } else { ref_node.DisasmAsRaw(code); } @@ -267,7 +316,7 @@ void DisasmMap::Disasm( node->address, node->ref2_addr, type, ref_type); if (ref_node.IsYetToBeHandled(_type)) { if (s.follow_jumps) { - Disasm(code, s, ref_node.address, true); + disasmProper(code, s, ref_node.address, true); } else { ref_node.DisasmAsRaw(code); } @@ -279,6 +328,54 @@ void DisasmMap::Disasm( } } +void DisasmMap::disasmQuickPeek(const DataView &code, const Settings &s) +{ + for (size_t at = 0; at < code.size;) { + // QuickPeek mode goes always aligned by 2. + ASSERT((at & 1u) == 0); + DisasmNode *node = &insertNodeQuickPeek(at, NodeType::kTracedInstruction); + node->Disasm(code, s); + if ((node->size & 1) != 0) { + fprintf(stderr, "at=%zx, size=%zu ", at, node->size); + fprintf(stderr, "type=%u ", static_cast<unsigned>(node->type)); + fprintf(stderr, "opcode=%u\n", static_cast<unsigned>(node->op.opcode)); + ASSERT((node->size & 1) == 0); + } + // Spread across the size and merge if an intersection encountered + const size_t address = node->address; + const auto size = node->size; + for (size_t i = 0; i < size; i++) { + auto *const ptr = _map[node->address + i]; + if (ptr != nullptr && ptr != node) { + node = mergeNodes(node, ptr); + } + _map[address + i] = node; + } + at += node->size; + // NOTE: There is not much information about a reference passed further, + // so just don't add a reference of immediate if s.imm_labels is not + // enabled. + const bool has_ref1 = (node->ref_kinds & kRef1ImmMask) + ? s.imm_labels + : (node->ref_kinds & kRef1Mask); + const bool has_code_ref1 = node->ref1_addr < code.size && has_ref1; + if (has_code_ref1) { + const NodeType type = (node->ref_kinds & (kRef1ReadMask | kRef1WriteMask)) + ? NodeType::kRefData : NodeType::kRefInstruction; + const auto ref_type = ReferenceTypeFromRefKindMask1(node->ref_kinds); + insertReferencedBy(node->address, node->ref1_addr, type, ref_type); + } + const bool has_ref2 = (node->ref_kinds & kRef2Mask); + const bool has_code_ref2 = (has_ref2 && node->ref2_addr < code.size); + if (has_code_ref2) { + const NodeType type = (node->ref_kinds & (kRef2ReadMask | kRef2WriteMask)) + ? NodeType::kRefData : NodeType::kRefInstruction; + const auto ref_type = ReferenceTypeFromRefKindMask2(node->ref_kinds); + insertReferencedBy(node->address, node->ref2_addr, type, ref_type); + } + } +} + DisasmMap::~DisasmMap() { ASSERT(_map != nullptr); diff --git a/src/disasm.h b/src/disasm.h index 13109a5..a3c589b 100644 --- a/src/disasm.h +++ b/src/disasm.h @@ -168,7 +168,6 @@ struct Symbol { class DisasmMap { const DisasmMapType _type; DisasmNode **_map{static_cast<DisasmNode **>(calloc(kRomSizeBytes, sizeof(*_map)))}; - size_t _code_size{}; Symbol *_symtab{}; size_t _symtab_size{}; TraceTable _tt{}; @@ -176,6 +175,9 @@ class DisasmMap { constexpr size_t findFirstSymbolAtAddress( uint32_t address, bool return_last_considered=false) const; DisasmNode &insertNode(uint32_t address, NodeType); + DisasmNode &insertNodeQuickPeek(uint32_t address, NodeType); + /// Returns primary, secondary ceases to exist + DisasmNode *mergeNodes(DisasmNode *primary, DisasmNode *secondary); DisasmNode &insertReferencedBy( const uint32_t by_addr, const uint32_t ref_addr, @@ -183,6 +185,8 @@ class DisasmMap { const ReferenceType ref_type); constexpr bool canBeAllocated(const DisasmNode& node) const; constexpr size_t symbolsCount() const { return _symtab_size / sizeof *_symtab; } + void disasmQuickPeek(const DataView &code, const Settings &); + void disasmProper(const DataView &code, const Settings &, size_t from=0, bool nested=false); public: constexpr const Symbol *Symtab() const { return _symtab; } constexpr size_t SymbolsCount() const { return symbolsCount(); } @@ -195,7 +199,13 @@ public: void InsertNode(uint32_t address, NodeType type); bool ApplySymbolsFromElf(const ELF::Image &); void ConsumeTraceTable(TraceTable &&); - void Disasm(const DataView &code, const Settings &, size_t from=0, bool nested=false); + void Disasm(const DataView &code, const Settings &s) + { + if (_type == DisasmMapType::kTraced) { + return disasmProper(code, s, 0, false); + } + return disasmQuickPeek(code, s); + } DisasmMap(DisasmMapType type): _type(type) {} ~DisasmMap(); }; @@ -20,7 +20,7 @@ enum class OpSize: int { }; enum class OpCode: uint8_t { - kNone, + kNone = 0, kRaw, ///< Emits ".short" kRaw8, ///< Emits ".byte" kORI, |