summaryrefslogtreecommitdiff
path: root/src/disasm.cpp
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2025-02-01 16:56:01 +0300
committerOxore <oxore@protonmail.com>2025-02-01 18:26:18 +0300
commitc76658ccce753b26bd8dc751f7b7cdeef2ab5b43 (patch)
tree73dc241fc9da3f2f9db5f0543e7ee578040bebb7 /src/disasm.cpp
parent6769fca1dd90f4e34e1fd6b2256c3795bbcaf658 (diff)
WIP
Diffstat (limited to 'src/disasm.cpp')
-rw-r--r--src/disasm.cpp157
1 files changed, 127 insertions, 30 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);