Skip to content

Machine code monitor debugger#705

Draft
chrisgleissner wants to merge 4 commits into
GideonZ:test-mergefrom
chrisgleissner:feature/machine-code-monitor-debug
Draft

Machine code monitor debugger#705
chrisgleissner wants to merge 4 commits into
GideonZ:test-mergefrom
chrisgleissner:feature/machine-code-monitor-debug

Conversation

@chrisgleissner

@chrisgleissner chrisgleissner commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Overview

This PR adds debugger support to the Machine Code Monitor:

  • Available from the monitor's Assembly view.
  • Supports interactive stepping, breakpoints, live CPU status, and branch / target prediction.
  • Works in Telnet, UI Overlay, and UI Freeze modes.

Demo

The demo shows a small program cycling the background colour, followed by stepping through KERNAL and BASIC:

https://youtu.be/ECsqq5HKPlE

Background

This PR depends on prior Machine Code Monitor work (see PRs #358 and #704) which has already been merged by @GideonZ into the test-merge branch. The test-merge branch in turn will eventually be merged to master.

The target branch of this PR is therefore test-merge, not master.

Dependency hierarchy between branches:

master
└── test-merge
   └── PR #358: Machine Code Monitor (MERGED to test-merge)
       └── PR #704: Machine Code Monitor fixes (MERGED to test-merge)
          └── PR #705: Machine Code Monitor debugger (THIS PR)

Features

In the following overview, keyboard shortcuts are bolded.

  • Stepping and execution

    • Supports RAM, RAM under ROM, and visible ROM (BASIC/KERNAL/CHAR) on the U64. All three memory views are validated across the Telnet, UI Overlay, and UI Freeze modes (see Testing).
    • Step Over, also known as Debug.
    • Step InTo, also known as Trace.
    • Step Out.
    • Continue, also known as Go.
    • Run to Kursor.
  • Live CPU status

    • The debug footer shows PC, AC, XR, YR, SP, processor flags, and the IRQ/NMI vectors at $0314/$0315 and $0318/$0319.
    • Important values and active processor flags (NV-BDIZC) are highlighted.
  • Target prediction

    • Shows the target of JMP, JSR, RTS, and branch opcodes.
    • Branch targets are highlighted when the branch is about to be taken.
    • This makes it clear where the next debug action will go before stepping.
  • Breakpoints

    • Supports RAM, RAM under ROM, and visible ROM on the U64 (ROM breakpoints use the volatile FPGA ROM-image buffers; nothing is written to persistent ROM storage).
    • Supports up to 10 non-persistent breakpoints, aligned with the existing bookmark UX.
    • R toggles a breakpoint on the current cursor line.
    • C=+R opens the breakpoint list.
    • The breakpoint list supports:
      • 0..9: Jump to a breakpoint slot.
      • L: Change the breakpoint Label.
      • S: Set a breakpoint at the current cursor line.
      • E: Enable or disable a breakpoint.
      • DEL: Delete a breakpoint.
  • Free exploration while debugging

    • Debug mode works together with the existing monitor views, including Memory, ASCII, Screen Code, Binary, and Assembly.
    • Edit mode can be active at the same time as Debug mode.
    • Memory can be inspected or edited without losing debug context.
  • Multi-mode debugging

    • Supports Telnet, UI Overlay, and UI Freeze modes.
  • Convenient reset

    • C=+X resets the C64 from inside Debug mode and returns to a clean monitor state.

Known Limitations

  • There are no conditional breakpoints, watchpoints, or CPU history. It stops only at instruction boundaries and uses a bounded per-step trap timeout.
  • U2 ROM breakpoints are not supported since it accesses the C64 ROM directly. We thus cannot place a temporary BRK opcode in the volatile ROM shadow as this feature only exists on a U64.

Remaining Work

  • 1. Incorporate changes from Deterministic U64 debugger stepping across RAM, RAM-under-ROM and ROM chrisgleissner/1541ultimate#11 upon successful testing. The mcm-debug-wip branch was merged, and all seven review comments on that PR were re-reviewed against this branch: three (heap-overflow clamp in screen.cc, run_window_unfroze freeze gating, get_live_cpu_port view contract) were already correctly resolved; one further real defect it flagged (the contextless visible-ROM relaunch settled on the wrong signal) was fixed here (see Testing).
  • 2. UX Overlay Mode: Fix reentry into the debug mode after having reset a prior debug session. This works already in UX Freeze and Telnet modes.
  • 3. Soak test for repeated execution of the sequence step over -> step into -> step out -> continue to breakpoint -> continue -> reset) for all combinations of:
    • View: UI Freeze, UI Overlay, Telnet
    • Banking: RAM, RAM under ROM, ROM
  • 4. Test on U2

Screenshots

Debugger

The following screenshot shows the debugger paused in the Kernal SCNKEY subroutine, which checks for pressed keys:

  • The Dbg flag in the top right shows that debug mode is active. At $EA87, the start of the subroutine, [KEY] marks a breakpoint.
  • The CPU is currently stopped at $EA98. Pressing D would take the highlighted branch to $EAFB, shown by the white target address. Without highlighting, the branch would not be taken.
  • The reversed cursor row can be moved to inspect other memory while staying in debug mode. Because of that, the next instruction to execute is also marked with >....<. Here, the cursor row and next instruction happen to be the same.
  • At the bottom, above the CPU/VIC bank footer, the two-line debug footer shows the current CPU state and updates after each debug step.
debug

Entering and leaving Debug mode

  • Press D in Assembly view to enter Debug mode.
  • Leave Debug mode with either C=+D or RUN/STOP.
  • Edit mode and Debug mode are independent, so both can be active at the same time.

Debug footer

The debug footer shows:

  • The next opcode address.
  • AC, XR, YR, and SP.
  • Processor flags.
  • IRQ and NMI vectors.
  • Predicted jump, branch, and return targets where applicable.

The next opcode to be executed is marked with >.....< and is also reflected by the PC value in the footer. This lets the developer move freely around memory while still keeping track of the active debug state.

Breakpoints

breakpoints

Breakpoints are shown on the right side of assembly opcode lines as [BRKx], where x is the breakpoint index from 0 to 9. Custom labels are shown as [LABL]. Lines with breakpoints are also highlighted.

The breakpoint list popup is intentionally similar to the existing bookmarks popup. It supports jumping to breakpoints, deleting breakpoints, enabling or disabling breakpoints, and assigning custom labels.

Debug help page

help

Some rarely used monitor shortcuts have debug-specific meanings while Debug mode is active. These mappings are shown at the top of the help screen.

Design

The following chapter discusses key design considerations.

Breakpoints

  • The machine-code monitor implements debugging by planting temporary 6510 BRK instructions because the FPGA core does not expose hardware breakpoints or direct 6510 register access usable by the application-hosted monitor.
  • MemoryBackend::create_debug_session() is the only seam between the monitor and platform-specific debug backends. Host tests use fake backends; firmware builds use the real U64 and U2 implementations.
  • The monitor UI talks only to a DebugSession abstraction (over, trace, step_out, go). The shared BRK engine owns trap installation, sentinel polling, register capture, resume, cleanup, breakpoint orchestration and patch tracking.
  • Breakpoints save the original byte, write $00 (i.e. the BRK opcode), let the CPU trap, read the register snapshot from the BRK catcher stub, then restore the saved byte. RAM breakpoints work on both U64 and U2.
  • BRK patches are recorded with their address, original byte and CPU port, then restored inside a stopped-session window. Reserved addresses, including trampoline memory and IRQ/NMI/BRK vectors, reject patches.
  • The debugger borrows the cassette buffer for its catcher, resume trampoline, one-shot NMI trampoline and scratch state, and temporarily repoints the RAM BRK vector at $0316/$0317. Any changes are temporary and cleaned up on leaving debug mode.

Stepping

  • Stepping is implemented by prediction rather than hardware single-step: the pure, host-testable 6502 predictor decodes the current instruction, plants BRKs at possible next instruction addresses, resumes the CPU, then captures the trap.
  • D steps over JSR, T traces into it, O runs to the caller-side return, and G installs enabled breakpoints and free-runs. If G starts on a breakpoint, it first steps past it to avoid immediately re-trapping.
  • Step Out uses a return-target stack populated when Trace enters a JSR, rather than relying only on live stack inspection.

ROM Support

  • On U64, BASIC and KERNAL ROM breakpoints are supported by patching the volatile FPGA ROM image buffers, not persistent ROM storage. The same engine path is used; the U64 leaf decides whether an address maps to RAM or a visible ROM image.
  • U2 does not support visible ROM image patching, so ROM breakpoints are refused cleanly. RAM breakpoints and register capture still use the same shared engine.

Cleanup and Modes

  • Cleanup restores every patched byte, borrowed stub byte and vector on every Debug exit path, including normal exit, timeout, cancel, reset, monitor close, RUN/STOP, C=+O and C=+X.
  • Overlay cleanup stages the resume path before restoring live code so the running CPU never executes a half-restored state.
  • Freeze mode temporarily unfreezes during a step and refreezes afterwards. Overlay and Telnet modes do not use that path.
  • C=+X clears transient debug state, resets through the backend, and either reopens the monitor or exits cleanly depending on the mode.

Implementation

Debug mode is implemented as a modal layer on top of the Assembly view. Normal monitor behaviour is unchanged.

The main source code files are:

  • machine_monitor.cc

    • Handles UI, key routing, and Debug/Edit composition.
  • monitor_debug.{h,cc}

    • Contains the debug data model, footer formatting, and help formatting.
  • monitor_breakpoints.{h,cc}

    • Contains the 10-slot non-persistent breakpoint table.
  • monitor_debug_session.h

    • Defines the backend abstraction.
  • monitor_debug_brk_session.cc

    • Implements the shared BRK trampoline, sentinel handling, patch management, return-target tracking, run-window depth, and cleanup.
  • monitor_debug_u64.cc and monitor_debug_u2.cc

    • Provide the U64 and U2 hardware hooks.

Testing

Hardware testing

  • Tested on an Ultimate 64 Elite (V1.4B, firmware 3.15), deployed over JTAG.
  • The underlying Machine Code Monitor branch was also tested on an Ultimate II.
  • Debug stepping through ROM is not supported on an Ultimate II because it requires patching ROM code.

Release matrix gate

tools/developer/machine-code-monitor/monitor_debug_matrix_gate.py is the debugger release gate. It drives the full {Telnet, UI Overlay, UI Freeze} x {RAM, RAM-under-ROM, visible ROM} matrix at two repetitions (18 cells) and validates every step against two independent oracles.

  • Each cell runs Step Over -> Step Into (depth 32) -> Step Out -> Run to cursor -> Continue-to-breakpoint -> Continue -> Reset.
  • Every step is checked for PC, registers, stack, and memory writes against a from-scratch 6510 interpreter (mcm6502.py) and against VICE, plus a per-cell 100-opcode dual-oracle trace and a separate 1000-opcode live run.
  • Latest U64 run: 18/18 cells PASS, 1000-opcode live run PASS (per-memory RAM 6/6, RAM-under-ROM 6/6, ROM 6/6; per-interface Telnet 6/6, Overlay 6/6, Freeze 6/6). Note: this run's ROM cells used the 5x reset-retry that the follow-up commit 3e01af5b removes (see "ROM-breakpoint entry coherency hardening" below); ROM cold-entry is now reported honestly rather than masked, and the deterministic RAM / RAM-under-ROM cells remain the release gate.
  • Preflight probes (oracle self-test, quick telnet-debug, freeze-thrash, local-UI soak) all pass.

Fix validated by this run

The contextless visible-ROM breakpoint entry has a closed-core first-fetch edge: a freshly DMA-committed ROM-image BRK can lag the live 6510 instruction-fetch path, so a bp+Go toward a KERNAL/BASIC target can run past the breakpoint. The follow-up commit 3e01af5b supersedes the reset-retry handling described here; see "ROM-breakpoint entry coherency hardening" below.

  • The initial launch settles the fetch path, gated on the installed breakpoint bank (has_visible_rom_patch()).
  • The self-heal relaunch is a bounded, in-place, no-reset relaunch, gated on the same installed-breakpoint-bank signal as the initial launch.
  • On a genuine residual miss the firmware now returns the precise DBG_ROM_ENTRY_UNCOHERENT result instead of a bare timeout, so the honest cause is reported rather than masked.

Unit tests

All three host suites under target/pc/linux/machinemonitortest pass: the core monitor suite, the bookmarks suite, and the debugger suite (software/test/monitor/machine_monitor_debug_test.cc, 173 cases including the new test_contextless_visible_rom_relaunch_resettles_fetch_path regression).

machine_monitor_debug_test.cc coverage includes:

  • Predictor classification.
  • Breakpoint table behaviour.
  • Footer layout.
  • Debug key handling.
  • Continue, Trace, Step Over, Step Out, and Stop Debugging.
  • C=+X reset and monitor re-entry.
  • Modal and popup gating.
  • Debug/Edit composition.
  • Cleanup on all exit paths.
  • Freeze and overlay behaviour.
  • Timeout recovery.
  • Step Out target validation.
  • BASIC/KERNAL visible-ROM stepping on U64.

End-to-end tests

Two Telnet E2E suites drive the deployed firmware and assert screen contents after each step, using the U64 REST API for memory setup. As of 3e01af5b they no longer reset-retry the contextless visible-ROM entry: each entry is attempted once, waiting the full firmware go() budget, and a genuine miss is surfaced as a documented-limitation SKIP (never a masked pass). See "ROM-breakpoint entry coherency hardening" below.

./tools/developer/machine-code-monitor/monitor_test.py --host <u64-ip> covers the core (non-debug) monitor as a regression guard: KERNAL visibility, disassembly and ASCII/hex formatting, inline edit, CPU/VIC bank cycling, G execution, bookmarks, binary-width cycling, save/load round-trips, and ASM navigation. Latest run: 32/32 passed, 0 skipped.

./tools/developer/machine-code-monitor/monitor_debug_test.py --host <u64-ip> is the broad debugger e2e (86 checks across stepping, breakpoints, RAM / RAM-under-ROM / visible-ROM entry, continue, cleanup, and exit-liveness). Latest run: 86/86 passed, 0 skipped (an excerpt is shown below).

Output (excerpt):

./monitor_debug_test.py
[01] Exit-liveness: ordinary RAM step then natural exit stays live ... OK
[03] Exit-liveness: KERNAL ROM step then natural exit stays live ... OK
[05] Exit-liveness: RAM-under-KERNAL debug then natural exit keeps loop live ... OK
[32] Debug: D over without captured context executes from cursor ... OK
[48] Debug: Step Out breaks after active JSR, not nearby RTS ... OK
[49] Debug: KERNAL ROM Step Into from $E000 ... OK
[52] Debug: BASIC ROM Step Over on visible JSR ... OK
[54] Debug: KERNAL ROM breakpoint set/hit/remove/step ... OK
[55] Debug: KERNAL $E002 G continues safely to BASIC $BC0F ... OK
[58] Debug: deep trace uses D/T/G/O across RAM, KERNAL, and BASIC ... OK
[61] Debug: RAM-under-KERNAL breakpoint hits with KERNAL banked out ... OK
[66] Debug: G with no breakpoints keeps $01=$00 loop running ... OK
[82] Debug: step-mode JSR/RTS keep the stack balanced and return address correct ... OK
[85] Debug: repeated G from the current breakpoint skips once and re-arms cleanly ... OK
[86] Debug: post-suite banking/display hygiene leaves $0001 safe and C64 readable ... OK
...
==== debug_e2e_test summary ====
  target  : u64
  passed  : 86
  skipped : 0
  failed  : 0
debug_e2e_test: OK (86 checks, 0 skipped)

Soak test

./tools/developer/machine-code-monitor/monitor_debug_soak.py --host <u64-ip>

This long-running test loops high-risk scenarios such as freeze/refreeze handling, parking trampoline cleanup, and breakpoint re-entry.

ROM-breakpoint entry coherency hardening (follow-up 3e01af5b)

Defect. A contextless visible-ROM breakpoint entry (arm a breakpoint in BASIC/KERNAL, then Go into it from a short RAM bootstrap, with no prior execution of that ROM line) can miss the 6510's first fetch: the closed U64 C64 core (delivered as the prebuilt external/u64.sof, sourced from a separate private repo per target/u64/nios2/updater/getbuild.sh) can serve a stale pre-patch byte to the live instruction fetch for a ROM line not re-fetched since the DMA BRK write. It is rare and load-conditioned; on a healthy device it did not occur at idle (0 coherency misses in 20 first-fetch attempts, all clean at ~2s).

Why the previous reset-retry was invalid. The harnesses masked this with a 5x reset-and-retry (ROM_ENTRY_MAX_ATTEMPTS). A reset does not create fetch coherency; it only buys another independent probabilistic draw, so the "pass" was probabilistic retry, not correctness. Two problems were also conflated: the firmware go() for this launch blocks its full breakpoint wait plus bounded relaunch budget (~15s) before returning, but the tests waited only ~6s and then reset, counting slow-but-succeeding entries as misses (the origin of the inflated "~37%" figure).

No FPGA fix is possible in this repository. The C64 core (6510, VIC-II, ROM serving) is a prebuilt binary with no source or FPGA project here, and there is no hardware PC-compare/breakpoint register (the debugger is BRK-only). The fix is firmware- and test-side.

Fix.

  • Firmware go()/continue return the precise DBG_ROM_ENTRY_UNCOHERENT (monitor shows ROM BP ENTRY MISSED - RUN CODE FIRST) on a genuine contextless visible-ROM miss, after its bounded in-place, no-reset relaunch. Patches are restored (no stale BRK), the handler is uninstalled, and the debugger stays usable. No reset, no command replay.
  • Test harnesses wait the full go() budget and drop the reset-retry entirely. A genuine miss is surfaced honestly as a documented-limitation SKIP (E2E) or a terminal ROM_ENTRY_UNCOHERENT status (matrix gate), never a masked pass. The matrix gate prints reset/retry counters and asserts the prohibited categories (recovery_reset, command_retry, session_replay, transparent_reset) are zero.
  • Added test_no_reset_retry_masking.py (a structural anti-masking guard that fails the build if a reset-retry loop reappears in a gate harness), host tests test_contextless_visible_rom_entry_reports_uncoherent_on_miss and test_ram_under_rom_timeout_is_not_reported_as_uncoherent, and doc/machine-code-monitor-rom-fetch-coherency.md.

Validation. Host suites green including the two new tests; anti-masking guard and matrix unit green; ./build-tool u64 builds cleanly. U64 hardware (Elite, fw 3.15, FPGA 122, core 1.4B): the de-masked ROM E2E group passes 7/7 checks with zero reset-retries and zero skips on a fresh deploy, and idle contextless first-fetch entry is 14/14 clean hits. Note: a separate, pre-existing network-stack degradation appears under sustained rapid reset+telnet+REST cycling ("back-to-back degrades telnet") and is recovered by a fresh JTAG redeploy; it is not the fetch race and is not masked as one.

Follow-up 2913a083 (bootstrap CPU port). Dropping the reset-retry exposed a latent issue: a contextless bp+Go whose first fetch misses can briefly run into ROM and change $01, and the firmware's no-reset in-place relaunch then re-enters with a stale bank, breaking downstream ROM stepping. (The old reset-retry masked this by resetting to a clean boot.) The RAM-spin bootstrap now sets $00/$01 to canonical banking ($2F/$37) before jumping into ROM, so every entry - including a relaunch through the bootstrap - lands with correct banking. Validated on U64 hardware: jsr-runcursor-rts is 4/4 with the fix (fresh deploy each) vs 2/3 without it; the pre-hardening harness was 3/3 only because its reset-retry reset the machine on every slow entry.

Copilot AI review requested due to automatic review settings June 6, 2026 16:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR introduces a debugger-capable Machine Code Monitor with breakpoint support across U64 and U2 targets, plus new automation tooling (repro scripts + soak test) and documentation updates to validate and explain the new debug behaviors.

Changes:

  • Adds a Debug mode execution backend (BRK-based stepping, breakpoints, reset/re-entry orchestration) with target-specific implementations (U64/U2).
  • Extends monitor UI/input handling for debug actions, global reset behavior, and updated status/banking display.
  • Adds new deterministic repro scripts, soak testing, and updates docs/snapshots/build files to cover the new functionality.

Reviewed changes

Copilot reviewed 57 out of 61 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/developer/machine-code-monitor/snapshots/expected_snapshots.json Updates expected CPU/view status line fragments to the new CxOy format.
tools/developer/machine-code-monitor/regression_repro.py Adds deterministic REST-driven repro cases for monitor regressions.
tools/developer/machine-code-monitor/monitor_debug_soak.py Adds a telnet-based debug soak test with a lightweight 6510 model comparison.
tools/developer/machine-code-monitor/issue_repro.py Adds autonomous REST repro cases for current monitor blockers.
tools/developer/machine-code-monitor/README.md Documents debug tests/soak usage and new environment variables.
target/u64ii/riscv/ultimate/Makefile Builds new monitor debug/breakpoint sources for U64II RISC-V.
target/u64/riscv/ultimate/Makefile Builds new monitor debug/breakpoint sources for U64 RISC-V.
target/u64/nios2/ultimate/Makefile Builds new monitor debug/breakpoint sources for U64 Nios2.
target/u2plus_L/riscv/ultimate/Makefile Builds new monitor debug/breakpoint sources for U2+L RISC-V.
target/u2plus/nios/ultimate/Makefile Builds new monitor debug/breakpoint sources for U2+ Nios.
target/u2/riscv/ultimate/Makefile Builds new monitor debug/breakpoint sources for U2 RISC-V.
target/pc/linux/machinemonitortest/Makefile Adds PC-side machinemonitordebugtest suite and required sources.
software/userinterface/userinterface.h Adds active monitor tracking and reset re-entry hook into HostClient.
software/userinterface/userinterface.cc Implements global reset shortcut handling and wires it into keymapper.
software/userinterface/ui_elements.cc Treats keymapper -2 as “global accelerator consumed” to exit popups.
software/u64/u64_machine.h Adds raw/visible poke/peek variants and “preserving freeze restore” write.
software/u64/u64_machine.cc Implements raw/visible memory access helpers and improves serve-control handling.
software/test/monitor/machine_monitor_test_support.h Extends FakeKeyboard to allow pushing a key ahead of scripted input.
software/test/monitor/machine_monitor_test_support.cc Implements FakeKeyboard push-head and updates UI string_edit stub signature.
software/test/monitor/machine_monitor_bookmarks_test.cc Updates expected bookmark popup strings and key sequences for new flows.
software/monitor/u64_memory_backend.h Adds reset/debug-session support and observed live CPU port tracking.
software/monitor/u64_memory_backend.cc Updates U64 backend mapping semantics and creates U64 debug sessions.
software/monitor/u2_memory_backend.h Adds reset/debug-session support for U2 backend.
software/monitor/u2_memory_backend.cc Implements U2 reset and debug-session creation.
software/monitor/run_machine_monitor.cc Reworks monitor lifecycle for reset re-entry and interface swap teardown.
software/monitor/monitor_init.h Adds weak global-reset-cancel hook for monitor/debug cancellation.
software/monitor/monitor_file_io.h Adds debug-context resume/staging APIs to safely hand off to execution.
software/monitor/monitor_file_io.cc Implements U64 NMI trampoline helpers and staged NMI handoff paths.
software/monitor/monitor_debug_u64.h Declares U64 debug session factory and helper for step CPU port.
software/monitor/monitor_debug_u64.cc Implements U64-specific BRK debug session with volatile ROM patching support.
software/monitor/monitor_debug_u2.h Declares U2 debug session factory.
software/monitor/monitor_debug_u2.cc Implements U2-specific BRK debug session (no visible ROM patching).
software/monitor/monitor_debug_session.h Introduces the DebugSession interface and result codes for debugger ops.
software/monitor/monitor_debug_predictor.h Adds instruction classification for stepping prediction.
software/monitor/monitor_debug_predictor.cc Implements predictor using fast opcode cases + disassembler length fallback.
software/monitor/monitor_debug_brk_session.h Declares shared BRK-based debug session implementation and patch tracking.
software/monitor/monitor_debug.h Defines DebugContext and MonitorDebug footer/help formatting API.
software/monitor/monitor_debug.cc Implements debug footer layout + help text formatting.
software/monitor/monitor_breakpoints.h Adds in-memory breakpoint table, labels, and popup formatting.
software/monitor/monitor_breakpoints.cc Implements slot allocation, normalization, and popup row formatting.
software/monitor/memory_backend.h Adds backing-store classification helpers and debug-session/reset hooks.
software/monitor/machine_monitor.h Extends monitor state, disasm lane, debug/breakpoint UI plumbing and APIs.
software/monitor/disassembler_6502.h Exposes operand_spec() for shared operand classification.
software/monitor/disassembler_6502.cc Renames illegal mnemonics and refactors operand parsing to use operand_spec().
software/monitor/assembler_6502.cc Canonicalizes additional illegal mnemonic aliases during assembly lookup.
software/io/usb/tests/usb_keyboard_queue_test.cpp Adds regression for Ctrl+R mapping distinct from cursor-down behavior.
software/io/usb/keyboard_usb.cc Maps Ctrl+R to KEY_CTRL_R in control keymap.
software/io/stream/keyboard_vt100.cc Adds Ctrl+R decoding from stream input (0x12 / ESC+r).
software/io/c64/keyboard_c64.cc Maps matrix Ctrl+R to KEY_CTRL_R instead of PETSCII 0x12 collision.
software/io/c64/keyboard.h Introduces KEY_CTRL_R and documents why 0x12 cannot be used.
software/io/c64/c64_subsys.cc Cancels debug waits on reset and normalizes formatting/whitespace.
software/io/c64/c64.h Adds begin/end stopped-session helpers and a refreeze() convenience.
software/io/c64/c64.cc Adds pristine ROM snapshot/restore on reset + stopped-session helpers + refreeze().
software/infra/host.h Adds host callback to request reset re-entry after C64 reset.
doc/machine_code_monitor.md Updates public documentation for modes, status line, edit/debug/breakpoints.
Comments suppressed due to low confidence (4)

software/monitor/disassembler_6502.cc:1

  • Branch opcode templates were changed to use an operand spec of rel (e.g. \"BCC rel\", \"BNE rel\"), but operand_length()/format_operand() no longer have the branch-special-case and also don’t recognize rel. This will cause branch instructions to disassemble with the wrong operand length and likely render an empty/incorrect operand/target, breaking both UI and any predictor logic that relies on disassembly output. Fix by handling rel explicitly (length=1 and formatting $%04X target), or by reinstating a branch-specific path keyed off spec == \"rel\".
#include "disassembler_6502.h"

software/monitor/disassembler_6502.cc:147

  • Branch opcode templates were changed to use an operand spec of rel (e.g. \"BCC rel\", \"BNE rel\"), but operand_length()/format_operand() no longer have the branch-special-case and also don’t recognize rel. This will cause branch instructions to disassemble with the wrong operand length and likely render an empty/incorrect operand/target, breaking both UI and any predictor logic that relies on disassembly output. Fix by handling rel explicitly (length=1 and formatting $%04X target), or by reinstating a branch-specific path keyed off spec == \"rel\".
        !strncmp(spec, "$nn", 3) || !strncmp(spec, "#", 1)) {
        return 1;
    }
    return 0;
}

tools/developer/machine-code-monitor/issue_repro.py:1

  • This line assigns session.dump_ui_screen(...) into mdt.wait_stable_dump, overwriting the imported function/attribute on the monitor_direct_test module. That is almost certainly unintended and can break subsequent calls that rely on mdt.wait_stable_dump. Change this to only assign the frame (e.g., frame = session.dump_ui_screen(...)) or call the real wait helper if you intended to use it.
    tools/developer/machine-code-monitor/README.md:1
  • monitor_debug_soak.py (as added in this PR) does not define --copy-roms-to-ram or --yes-copy-roms arguments, so this example command is not runnable as documented. Either update the README to match the actual CLI flags, or add the missing argparse options and implement the described behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread doc/machine_code_monitor.md Outdated
Comment thread doc/machine_code_monitor.md
@chrisgleissner
chrisgleissner marked this pull request as draft June 6, 2026 16:08
@chrisgleissner chrisgleissner changed the title Add debugger to machine code monitor Machine code monitor debugger Jun 6, 2026
@Kugelblitz360

Copy link
Copy Markdown

Just: WOW! Thank you!

@chrisgleissner
chrisgleissner force-pushed the feature/machine-code-monitor-debug branch 2 times, most recently from b5797b2 to e75f5b0 Compare June 27, 2026 06:41
@chrisgleissner
chrisgleissner force-pushed the feature/machine-code-monitor-debug branch 2 times, most recently from 8f6b8f2 to 84e7892 Compare July 21, 2026 21:55
BRK park/step engine with Step Into/Over/Out, run-to-cursor and
breakpoints across RAM, RAM-under-ROM and ROM from the telnet, overlay
and freeze UIs. U64 volatile ROM-image breakpoints and U2+L split-session
support.

Validated on U64: full UI x memory matrix + 1000-opcode oracle run.
@chrisgleissner
chrisgleissner force-pushed the feature/machine-code-monitor-debug branch from 84e7892 to 5ebf0df Compare July 22, 2026 00:57
…king

Contextless visible-ROM bp entry can miss the 6510 first fetch (closed U64 core served-ROM coherency). go()/continue now return DBG_ROM_ENTRY_UNCOHERENT ("ROM BP ENTRY MISSED") after the bounded no-reset relaunch instead of a bare timeout. Test harnesses wait the full go() budget and drop the 5x reset-retry; a genuine miss is surfaced as a documented-limitation SKIP/status. Adds a static anti-masking guard, reset/retry counters, host tests, and doc/machine-code-monitor-rom-fetch-coherency.md.
A contextless bp+Go whose first fetch misses can run into ROM and change $01; the firmware's no-reset in-place relaunch then re-enters with a stale bank, breaking downstream ROM stepping (exposed by dropping the reset-retry masking). The RAM-spin bootstrap now sets $00/$01 to canonical banking ($2F/$37) before jumping, so every entry - including a relaunch through it - lands with correct banking. jsr-runcursor-rts: 4/4 with fix (was 2/3).
Telnet wait_pc confirms a step via the debugger's STORE_* context ($03F0-$03F6, read by DMA) when a screen render is dropped under congestion (firmware aborts a telnet send it cannot deliver within SO_SNDTIMEO), instead of failing on the stale footer - not a retry, re-issues no command. Add a per-cell SIGALRM watchdog and post-cell C64 hard-wedge detection/logging so a stalled transport cannot hang the run for an hour or silently brick the device.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants