summaryrefslogtreecommitdiff
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
parent6769fca1dd90f4e34e1fd6b2256c3795bbcaf658 (diff)
WIP
-rw-r--r--src/disasm.cpp157
-rw-r--r--src/disasm.h14
-rw-r--r--src/m68k.h2
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();
};
diff --git a/src/m68k.h b/src/m68k.h
index 8fe10ee..079d26b 100644
--- a/src/m68k.h
+++ b/src/m68k.h
@@ -20,7 +20,7 @@ enum class OpSize: int {
};
enum class OpCode: uint8_t {
- kNone,
+ kNone = 0,
kRaw, ///< Emits ".short"
kRaw8, ///< Emits ".byte"
kORI,