64kramsystem / 64kramsystem/ghidra-vice-connector
Memory access heatmap: runtime code-vs-data classification
- 主要语言
- Python
- 星标
- 1
- 派生
- 0
- PR 合并指标
- 30 天内没有已合并 PR
描述
## Summary
The fundamental challenge in C64 game disassembly is distinguishing code from data in a flat 64KB address space (sprite frames, music data, lookup tables, level maps all live alongside executable code). Using VICE tracepoints (non-stopping checkpoints) to record which addresses are executed vs. read as data vs. written, then annotating the Ghidra listing accordingly, can solve this automatically at runtime.
Inspired by RetroDebugger's memory access layers and IceBroLite's memory visualization.
---
## How VICE supports this
Checkpoints with `stop_on_hit=False` fire without pausing execution. VICE still sends `RESP_CHECKPOINT_INFO` (0x11) events when these tracepoints are hit — with `currently_hit=True` and the `cpu_op` flag (EXEC/LOAD/STORE) identifying the access type.
`checkpoint_set()` in `util.py:480` already accepts `stop_on_hit` — it just needs to be `False`. The event handler in `hooks.py` currently only handles `RESP_STOPPED` (0x62) and `RESP_RESUMED` (0x63); a handler for `RESP_CHECKPOINT_INFO` (0x11) needs to be registered.
---
## Implementation plan
### Phase 1 — Collect access data
1. **`util.py`**: Register a `RESP_CHECKPOINT_INFO` (0x11) event handler in `ViceBmpClient` that accumulates `(address, cpu_op)` hits into a dict without blocking. The address is already in the checkpoint info struct at `start` field (`util.py:98-116`).
2. **`methods.py`**: Add three new remote methods:
- `start_heatmap(process: C64, start: Address, end: Address)` — calls `checkpoint_set()` three times (EXEC, LOAD, STORE) with `stop_on_hit=False` over the given range.
- `stop_heatmap(process: C64)` — deletes the heatmap checkpoints.
- `apply_heatmap(process: C64)` — applies collected data to the program (Phase 2).
### Phase 2 — Annotate the Ghidra listing
`apply_heatmap()` iterates the accumulated access dict and for each address:
- EXEC hits → set Ghidra bookmark type `"Code"` (or call `trace.disassemble()` at those addresses)
- LOAD-only (never EXEC) → set bookmark type `"Data"`, add plate comment `"Read as data (heatmap)"`
- STORE hits → add plate comment `"Written at runtime"`
Bookmarks are written via the trace: `trace.proxy_object_path(...).set_value(...)` or via a post-analysis Ghidra script that reads from a persisted set.
### Alternative: CPU History bulk approach
If tracepoints prove too noisy (too many RESP_CHECKPOINT_INFO events flooding the socket), use `CMD_CPU_HISTORY` (0x86) on each stop instead: retrieve the last N instructions, extract their PCs, and accumulate them. This gives executed addresses only, not data reads.
---
## BMP commands involved
| Command | Hex | Already in util.py? |
|---------|-----|-------------------|
| `CMD_CHECKPOINT_SET` | 0x12 | Yes (`util.py:480`) |
| `CMD_CHECKPOINT_DELETE` | 0x13 | Yes (`util.py:512`) |
| `RESP_CHECKPOINT_INFO` event | 0x11 | Not handled as event |
---
## Files to change
- `src/main/py/src/vice/util.py` — add `RESP_CHECKPOINT_INFO` event handling
- `src/main/py/src/vice/methods.py` — add `start_heatmap`, `stop_heatmap`, `apply_heatmap` methods
- `src/main/py/src/vice/commands.py` — add accumulated access state and annotation logic
贡献指南
这个仓库没有索引到贡献指南
评估
这个 Issue 还没有评估数据。