Skip to content

Machine code monitor debugger - #705

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

Machine code monitor debugger#705
chrisgleissner wants to merge 1 commit 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 debugging to the Machine Code Monitor's Assembly view.

It provides:

  • Step Over, Step Into, Step Out, Continue, and Run to Cursor.
  • Up to 10 breakpoints.
  • Live CPU state and prediction of the next execution target.
  • Debugging of RAM, RAM under ROM, and visible BASIC/KERNAL ROM on the U64.
  • Support for Telnet, UI Overlay, and UI Freeze modes.

Current status

The main debugger functionality is implemented and has been exercised across Telnet, UI Overlay, and UI Freeze modes.

One limitation is still open: a first-time jump into a BASIC or KERNAL breakpoint can miss the temporary BRK. It is now detected, bounded and reported, but not removed, and it needs an FPGA-side change. RAM-under-ROM stepping is not affected by it. See "First-fetch handling for ROM breakpoints" and "Remaining work".

Demo

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

https://youtu.be/ECsqq5HKPlE

Target branch

This PR builds on the Machine Code Monitor work already merged into test-merge through PRs #358 and #704. Its target branch is therefore test-merge, not master.

master
└── test-merge
    ├── PR #358: Machine Code Monitor
    ├── PR #704: Machine Code Monitor fixes
    └── PR #705: Machine Code Monitor debugger (this PR)

Features

Execution control

Action Shortcut Behaviour
Step Over D Executes the current instruction without entering a JSR.
Step Into T Executes the current instruction and enters a JSR.
Step Out O Continues to the return address of the current subroutine.
Continue G Runs until an enabled breakpoint is reached.
Run to Cursor K Runs until the selected instruction is reached.

Debugging supports:

  • RAM.
  • RAM under ROM.
  • Visible BASIC and KERNAL ROM on the U64.
  • Telnet, UI Overlay, and UI Freeze modes.

Live CPU state

The debug footer displays:

  • Program counter.
  • Accumulator, X register, Y register, and stack pointer.
  • Processor flags.
  • IRQ and NMI vectors at $0314/$0315 and $0318/$0319.
  • Predicted jump, branch, call, and return targets.

Active flags and important values are highlighted. A branch target is highlighted only when the branch will be taken.

The next instruction is marked in the Assembly view with >...<. This allows the cursor to move elsewhere while the current execution position remains visible.

Breakpoints

The debugger supports up to 10 non-persistent breakpoints.

Action Shortcut
Toggle a breakpoint on the cursor line R
Open the breakpoint list C=+R
Jump to a breakpoint slot 0 to 9
Change its label L
Set it to the cursor address S
Enable or disable it E
Delete it DEL

Breakpoints appear as [BRKx], where x is the slot number. A custom label replaces this with [LABL].

RAM breakpoints work on both U64 and U2. On the U64, visible-ROM breakpoints temporarily modify the FPGA's writable copies of the BASIC and KERNAL ROM images. Persistent ROM storage is never changed.

Monitor integration

Debug mode extends the existing Assembly view.

  • Memory, ASCII, Screen Code, Binary, and Assembly views remain available.
  • Memory can be inspected or edited without ending the debug session.
  • Edit mode and Debug mode can be active at the same time.
  • C=+X resets the C64 and returns the monitor to a clean state.
  • Debug mode can be left with C=+D or RUN/STOP.

Screenshots

Debugger

The debugger is paused in the KERNAL SCNKEY routine:

  • Dbg indicates that Debug mode is active.
  • [KEY] marks a labelled breakpoint at $EA87.
  • The CPU is stopped at $EA98.
  • The highlighted target $EAFB shows that the current branch will be taken.
  • The footer shows the current CPU state.
Debugger paused in the KERNAL SCNKEY routine

Breakpoint list

The breakpoint popup follows the existing bookmark-list controls.

Debugger breakpoint list

Debug help

Shortcuts that change meaning while Debug mode is active are shown at the top of the help screen.

Machine Code Monitor debug help

Design

BRK-based debugging

The FPGA core does not provide hardware breakpoints or direct access to the 6510 registers for the application-hosted monitor. The debugger therefore stops execution by temporarily writing BRK instructions.

For each temporary breakpoint, the debugger:

  1. Saves the original byte.
  2. Writes $00, the 6510 BRK opcode.
  3. Resumes the CPU.
  4. Captures the register state when the BRK is reached.
  5. Restores the original byte.

Each change records the address, original byte, and CPU-port state required to restore it correctly. Debugger working memory and interrupt-vector locations cannot be used as breakpoint addresses.

The debugger temporarily uses the cassette buffer for its handler, resume code, NMI code, and working state. It also temporarily changes the RAM BRK vector at $0316/$0317. All changes are restored when Debug mode ends.

Platform interface

MemoryBackend::create_debug_session() separates the monitor UI from the U64- and U2-specific implementations.

  • Host tests use test implementations.
  • Firmware builds use the U64 or U2 implementation.
  • The monitor UI uses only the shared DebugSession operations.

Stepping

The debugger does not have hardware single-step support. Instead, it decodes the current instruction and calculates the addresses that may execute next.

It then places temporary BRK instructions at those addresses, resumes the CPU, and captures the breakpoint that is reached.

Step Out uses return addresses recorded when Step Into enters a JSR. It does not depend only on reading the current stack.

When Continue starts on an existing breakpoint, the debugger first executes past it to avoid immediately stopping at the same address again.

ROM support

On the U64, BASIC and KERNAL breakpoints temporarily modify writable copies of the ROM images held by the FPGA.

The U2 reads the C64 ROM directly and has no equivalent writable copy. Visible-ROM breakpoints are therefore unavailable on the U2. RAM breakpoints and register capture use the same shared debugger implementation on both devices.

Cleanup and mode handling

Temporary instructions, vectors, and working memory are restored on every exit path, including:

  • Normal Debug-mode exit.
  • Timeout or cancellation.
  • Reset.
  • Monitor close.
  • RUN/STOP.
  • C=+O.
  • C=+X.

In Overlay mode, the debugger prepares the resume code before restoring modified program bytes. This prevents the running CPU from encountering partially restored code.

Freeze mode temporarily resumes the C64 while an instruction is executed, then freezes it again. Telnet and Overlay modes do not require this process.


First-fetch handling for ROM breakpoints

Problem

A contextless visible-ROM entry means jumping from a small RAM program directly to a BASIC or KERNAL breakpoint at an address that has not already executed during the current debug session.

In this situation, the U64 can occasionally execute the original ROM byte instead of the newly written BRK on the first fetch.

The relevant C64 FPGA core is supplied to this repository as a prebuilt file, and its source is maintained elsewhere. The first-fetch behaviour therefore cannot be corrected directly in the FPGA code within this PR.

Previous test behaviour

Earlier versions of the tests reset the C64 and tried again when this happened.

A reset did not make the first fetch reliable. It only repeated the operation from a clean start until it happened to succeed.

The tests also waited for less time than the full firmware operation could require. Some slow but successful entries were therefore incorrectly counted as misses.

The reset-and-retry behaviour has been removed.

Current handling

The firmware now:

  • Makes one bounded second attempt without resetting the C64 or replaying the debug command.
  • Performs that attempt only when a visible-ROM breakpoint is installed.
  • Returns DBG_ROM_ENTRY_UNCOHERENT if the second attempt also misses.
  • Displays ROM BP ENTRY MISSED - RUN CODE FIRST.
  • Restores all modified bytes and leaves the debugger usable.
  • Does not reset or silently repeat the complete debug session.

The small RAM launcher now writes $2F/$37 to $00/$01 before entering ROM. This restores the expected C64 memory configuration before both the first attempt and any second attempt.

The tests now wait for the complete go() operation:

  • The broad E2E suite reports a genuine first-fetch miss as a known limitation and skips that check.
  • The matrix suite reports ROM_ENTRY_UNCOHERENT.
  • Neither suite turns a retry into a passing result.

A host-side test also fails if reset-based retries, command replay, or session replay are added back to the main debugger tests.

Defects found in that handling

Testing the handling above on hardware showed it did not actually take effect. Three defects were found and fixed.

The miss was never reported. wait_for_sentinel() counted its own delay_ms(5) steps instead of measuring elapsed time. The same loop polls the cancel keyboard, and on a Telnet session that poll blocks for the socket's 200 ms receive timeout, so each nominal 5 ms iteration really took about 205 ms. A 900 ms budget therefore ran for about 37 seconds and a 5000 ms budget for over three minutes. A forced miss produced no output at all for 45 seconds. The loop now measures elapsed time with getMsTimer(), and the same forced miss reports ROM BP ENTRY MISSED after 3.1 seconds. This path is only reached when the breakpoint is not hit, so successful operations are unaffected.

The settle was skipped after the first debug session. The fetch-path settle and the DBG_ROM_ENTRY_UNCOHERENT classification were gated on launch_ctx == 0. In that branch launch_ctx is only relaunch metadata, and cleanup_to_context() leaves a resume_context seed behind after every parked Debug exit, so launch_ctx was non-null for every ROM entry after the first Debug session of a monitor lifetime. Both the settle and the honest reporting were therefore lost exactly where they were needed. The gate is now the installed patch set. It requires all installed breakpoints to be in visible ROM, so a contextless step, which also installs its predicted next program counter in RAM and can legitimately stop there, is not misreported as a ROM first-fetch miss.

The settle was too short. Measured on a U64 Elite as the hit rate over repeated first-time ROM entries: 33/40 at the original 50 ms, 39/40 at 150 ms, and 18/20 at 250 ms. The refresh saturates around 150 ms, which is now the value used. Pairing the visible-ROM launch with the raster-synced stop used for RAM-under-ROM was also measured and made the hit rate much worse (1/20), so that pairing was left as it was.

One test-side defect was fixed alongside them: a check skipped for a first-fetch miss left its entry breakpoint armed in ROM, and a later check then trapped on that leftover BRK instead of its own target. The skip path now clears the breakpoint it armed.


Implementation

The main files are:

File Responsibility
machine_monitor.cc UI integration, keyboard handling, and Debug/Edit interaction.
monitor_debug.{h,cc} Debug state, footer formatting, and help text.
monitor_breakpoints.{h,cc} Ten-slot non-persistent breakpoint table.
monitor_debug_session.h Shared interface for the U64, U2, and host tests.
monitor_debug_brk_session.cc BRK handling, stepping, byte restoration, return addresses, and cleanup.
monitor_debug_u64.cc U64 hardware access and visible-ROM support.
monitor_debug_u2.cc U2 hardware access.

Testing

Test suites

The monitor E2E tests are under tests/e2e/monitor/ and registered in the repository-root run-e2e-tests runner.

Selector Script Mode
monitor-harness monitor_harness_test.py Automatic, host only
machine-code-monitor monitor_test.py Manual
machine-code-monitor-debug monitor_debug_test.py Manual
machine-code-monitor-matrix monitor_debug_matrix_test.py Manual

Other files in this directory are shared test code rather than standalone suites:

  • mcm6502.py
  • mcm_rest.py
  • mcm_localui.py
  • mcm_split_rest.py
  • overlay_lifecycle.py
  • monitor_debug_stress.py
  • freeze_reentry_guard.py
  • gen_interpreter_vectors.py

Unit tests

All three host-side monitor suites under target/pc/linux/machinemonitortest pass:

  • Core monitor tests.
  • Bookmark tests.
  • Debugger tests.

software/test/monitor/machine_monitor_debug_test.cc contains 174 cases covering:

  • Instruction decoding and next-address prediction.
  • Breakpoint management.
  • Footer and help formatting.
  • Debug keyboard controls.
  • Step Over, Step Into, Step Out, Continue, and Debug exit.
  • Reset and monitor re-entry.
  • Popup handling.
  • Debug/Edit interaction.
  • Cleanup on all exit paths.
  • Freeze and Overlay behaviour.
  • Timeout recovery.
  • Step Out return-address checks.
  • U64 BASIC and KERNAL stepping.
  • First-fetch handling for visible-ROM breakpoints.
  • Correct distinction between RAM-under-ROM timeouts and ROM first-fetch misses.

Hardware environment

Tested on:

  • Ultimate 64 Elite.
  • Hardware revision V1.4B.
  • Firmware 3.15.
  • FPGA 122.
  • Deployment over JTAG.

The underlying Machine Code Monitor was also tested on an Ultimate II. Debugger-specific testing on U2 hardware remains outstanding.

Main debugger matrix

tests/e2e/monitor/monitor_debug_matrix_test.py, invoked through the machine-code-monitor-matrix selector, is the main debugger release test.

It covers:

{Telnet, UI Overlay, UI Freeze}
x
{RAM, RAM under ROM, visible ROM,
 RAM->ROM->RAM, RAM->RAM-under-ROM->ROM->RAM-under-ROM->RAM}

The last two are boundary-traversal modes, added because the first three each
enter their region cold from a bootstrap and then stay inside it. That is not
how the debugger is normally used. A developer debugs their own program in RAM
and reaches ROM by stepping into it, so these modes start with a live context in
a RAM fixture and cross a region boundary mid-trace:

Memory mode What the cell exercises
ram-rom-ram A RAM program calls BASIC $BC0F. Step Into crosses RAM to ROM, Step Out crosses ROM back to RAM.
ram-rur-rom-ram RAM to RAM-under-ROM to RAM to ROM to RAM to RAM-under-ROM to RAM, switching $01 between legs.

A bank switch cannot execute from the window it is switching, so a direct
RAM-under-ROM to visible-ROM step is not expressible on a 6510: the STA $01
that maps KERNAL back in would change the bytes the CPU is fetching. The
traversal therefore returns to RAM between the two banked regions, which is what
real code does as well.

Each traversal step is checked against the same oracles as the rest of the
matrix, and the per-cell boundary-walk-evidence.json records the program
counter and stack pointer at every crossing.

monitor_harness_test.py also executes both fixtures through mcm6502.py on
the host, asserting the region sequence, that every step of the walk is really
reached in order, and that the liveness loop keeps running. A broken fixture
therefore fails in under a second instead of part-way through an hour-long
hardware run.

These modes take the matrix from 9 to 15 combinations at one repetition. They
are implemented and verified on the host; they have not yet been run on
hardware.

Each combination executes:

  1. Step Over.
  2. Step Into to depth 32.
  3. Step Out.
  4. Run to Cursor.
  5. Continue to Breakpoint.
  6. Continue.
  7. Reset.

The checks include:

  • Program counter.
  • Registers.
  • Stack state.
  • Memory writes.
  • A 100-instruction comparison against both an independent 6510 interpreter and VICE.
  • A separate 1000-instruction live run.

An earlier run passed all 18 combinations, but its visible-ROM checks still used the now-removed reset retry. It remains useful evidence for the deterministic stepping behaviour, but not for first-time ROM entry.

Current testing without reset retries includes:

  • RAM across Telnet, Overlay, and Freeze: 3/3 combinations passed.
  • The 1000-instruction live run passed.
  • The interpreter self-test, short Telnet test, Freeze re-entry test, local-UI repeated test, and VICE comparison passed.
  • The focused visible-ROM E2E group passed 7/7 checks on a fresh deployment, with no retries and no skipped checks.
  • First-time visible-ROM entry completed 14/14 times while the device was otherwise idle.
  • The RAM launcher memory-configuration fix completed 4/4 tests, compared with 2/3 without the fix.

After the first-fetch defects were fixed, the full matrix was rerun without reset retries:

  • All 9 combinations passed at one repetition, including all three visible-ROM combinations.
  • No combination reported ROM_ENTRY_UNCOHERENT, so the visible-ROM entries genuinely completed rather than being tolerated.
  • The 1000-instruction live run passed.
  • Verdict: PRODUCTION READY.
  • The three visible-ROM combinations were then rerun on their own and passed again, again with no ROM_ENTRY_UNCOHERENT.

Read that result with the same caution as the broad suite: the matrix verdict treats ROM_ENTRY_UNCOHERENT as a non-failing terminal status, so a green matrix does not by itself prove that first-time ROM entry succeeded. It has to be read together with the ROM_ENTRY_UNCOHERENT count, which was zero here. One repetition per combination is also a single sample against a per-entry miss rate of roughly one in twenty.

Core monitor regression suite

./run-e2e-tests -H <u64-ip> -s machine-code-monitor

This runs tests/e2e/monitor/monitor_test.py.

Latest result:

32 passed
0 skipped

It covers:

  • KERNAL visibility.
  • Disassembly and ASCII/hex formatting.
  • Inline editing.
  • CPU and VIC bank selection.
  • G execution.
  • Bookmarks.
  • Binary-width selection.
  • Save/load round trips.
  • Assembly navigation.

Broad debugger suite

./run-e2e-tests -H <u64-ip> -s machine-code-monitor-debug

This runs tests/e2e/monitor/monitor_debug_test.py.

The suite contains 86 checks covering:

  • Stepping.
  • Breakpoints.
  • RAM, RAM-under-ROM, and visible-ROM entry.
  • Continue behaviour.
  • Cleanup.
  • Leaving Debug mode without affecting the monitor or C64.

Before the defects described under "First-fetch handling for ROM breakpoints" were fixed, long runs stopped at different first-time BASIC or KERNAL entry checks. The same behaviour occurred on the branch before the rebase, so the rebase did not introduce it: building and deploying the pre-rebase tip and running the same harness against it failed in the same way.

All 86 checks now complete. Three consecutive runs, each on a fresh JTAG deployment:

run 1:  84 passed, 2 skipped, 0 failed
run 2:  82 passed, 4 skipped, 0 failed
run 3:  82 passed, 4 skipped, 0 failed

No run was free of skips, and a skipped check verifies nothing. Every skip is a first-time visible-ROM entry that missed the first fetch. The suite exits zero because the miss is the documented limitation rather than a debugger defect, so between 2 and 4 of the 86 checks did not exercise the behaviour they exist to cover. The affected checks vary between runs.

Across all completed runs the skips landed on these checks, most frequent first:

Check Runs skipped
[52] BASIC ROM Step Over on visible JSR 5
[51] KERNAL ROM Step Into on visible JSR 4
[50] KERNAL ROM Step Over on visible JSR 3
[55] KERNAL $E002 G continues to BASIC $BC0F 3
[49] KERNAL ROM Step Into from $E000 2
[03], [54], [56] 1 each

Every one is a visible-ROM entry. No RAM-under-ROM check was ever skipped or failed in any run. RAM-under-ROM breakpoints are written to RAM, which is coherent, so they do not take the settle path and a timeout there stays a plain DBG_TIMEOUT; a host test enforces that distinction.

Full automatic test run

./run-e2e-tests -H u64

All 11 automatic suites passed in 748 seconds.


Remaining work

  • The first-fetch miss itself is still open. This PR makes it detected, bounded and correctly reported; it does not remove it. Roughly one in twenty first-time visible-ROM entries still misses, which is why 2 to 4 of the 86 broad-suite checks are skipped on any given run. Removing it needs the FPGA-side change described under "VHDL Change".
  • Validate the debugger on U2 hardware.

Known limitations

  • Conditional breakpoints, watchpoints, and CPU execution history are not supported.
  • Breakpoints stop only between instructions, and each debugging operation has a fixed maximum wait time.
  • Visible-ROM breakpoints are supported only on the U64 because the U2 has no writable copy of the C64 ROM that the debugger can temporarily modify.
  • A first-time jump from RAM to a BASIC or KERNAL breakpoint can miss the temporary BRK. Here, "first-time" means that the target ROM instruction has not already executed during the current debug session. Measured at roughly one entry in twenty on an otherwise idle device, and more often when the C64 is running code that rarely fetches from ROM, because the fetch-path settle depends on the CPU actually executing ROM while it runs. The debugger reports the miss and stays usable, but the operation the user asked for does not happen; in the E2E suites the affected checks are skipped, so they verify nothing on that run. RAM and RAM-under-ROM breakpoints are unaffected.

VHDL Change

@GideonZ The final item under "Known Limitations" is the one outstanding issue that may require a VHDL-side fix.

The firmware writes the temporary BRK into the U64's writable ROM image before resuming the 6510. In rare cases, the first instruction fetch still receives the previous ROM byte instead of the newly written BRK.

The firmware now detects this case, makes one bounded second attempt without resetting the C64, and reports DBG_ROM_ENTRY_UNCOHERENT if the breakpoint is still missed.

The application-side firmware has no direct way to force the 6510 instruction-fetch path to observe the updated ROM byte. It also has no hardware program-counter breakpoint facility to use instead. A complete fix may therefore require a change in the FPGA C64 core, most likely where ROM-image updates become visible to the 6510 fetch path.

The chapter "First-fetch handling for ROM breakpoints" in this PR description contains more details.

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
@chrisgleissner
chrisgleissner force-pushed the feature/machine-code-monitor-debug branch from 84e7892 to 5ebf0df Compare July 22, 2026 00:57
@chrisgleissner
chrisgleissner force-pushed the feature/machine-code-monitor-debug branch 2 times, most recently from 3b23c5a to 3105a60 Compare July 31, 2026 00:44
@chrisgleissner
chrisgleissner force-pushed the feature/machine-code-monitor-debug branch from 3105a60 to 1df9591 Compare July 31, 2026 06:09
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