Atari Lynx Game Debugging with Gearlynx
Overview
Debug Atari Lynx games using the Gearlynx emulator as an MCP server. Control execution (pause, step, breakpoints), inspect the 6502 CPU and hardware (Mikey, Suzy), read/write memory, disassemble code, trace instructions, and capture screenshots — all through MCP tool calls. Hardware documentation is available in the references/ directory.
MCP Server Prerequisite
IMPORTANT — Check before installing: Before attempting any installation or configuration, you MUST first verify if the Gearlynx MCP server is already connected in your current session. In the default mode, call debug_get_status directly. If Gearlynx was intentionally started with --mcp-router, call get_tool_info with {"name":"debug_get_status"}, then call execute_tool with {"name":"debug_get_status","arguments":{}}. A valid response from either workflow means the server is active and ready.
Only if neither workflow is available or the call fails, you need to help install and configure the Gearlynx MCP server:
Installing Gearlynx
Run the bundled install script (macOS/Linux):
bash scripts/install.sh
This installs Gearlynx via Homebrew on macOS or downloads the latest release on Linux. It prints the binary path on completion. You can also set INSTALL_DIR to control where the binary goes (default: ~/.local/bin).
Alternatively, download from GitHub Releases or install with brew install --cask drhelius/geardome/gearlynx on macOS.
Connecting as MCP Server
Configure your AI client to run Gearlynx as an MCP server via STDIO transport. Example for Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"gearlynx": {
"command": "/path/to/gearlynx",
"args": ["--mcp-stdio"]
}
}
}
Replace /path/to/gearlynx with the actual binary path from the install script. Add --headless before --mcp-stdio on headless machines.
Hardware Documentation (References)
Atari Lynx hardware documentation is available in the references/ directory. Load them into your context when investigating specific hardware.
| Reference |
File |
Load when... |
| General Overview |
references/lynx1_general_overview.md |
System specs, feature set, overall architecture |
| Hardware Overview |
references/lynx2_hardware_overview.md |
System block diagram: CPU, Mikey, Suzy, RAM, cart |
| Hardware Quirks |
references/lynx3_software_hardware_perniciousness.md |
Unsafe register operations, hardware gotchas |
| CPU/ROM |
references/lynx4_cpu_rom.md |
65C02 cycle timing, CPU sleep, ROM mapping |
| Display |
references/lynx5_display.md |
Frame rate, pen/palette colors, display buffer addresses |
| Sprite/Collision |
references/lynx6_sprite_collision.md |
SCB format, sprite types, collision depository |
| Audio/ROM Cart |
references/lynx7_audio_tape_romcart.md |
4 audio channels, feedback taps, stereo, ROM cart banking |
| Timers/Interrupts |
references/lynx8_timers_interrupts.md |
8 timer channels, linking, reload, IRQ sources |
| UART |
references/lynx8a_uart.md |
ComLynx serial port, UART registers, baud rate, flow control |
| Other Hardware |
references/lynx9_other_hardware.md |
Hardware multiply/divide, parallel port, upward compatibility |
| System Reset |
references/lynx10_system_reset.md |
Reset/power-up sequence, Suzy/Mikey init, boot ROM |
| System Bus Interplay |
references/lynx11_system_bus_interplay.md |
Bus masters, page mode, DMA timing, bus contention |
| Hardware Addresses |
references/hardware_addresses.md |
64K memory map, Mikey/Suzy register addresses ($FC00-$FCFF, $FD00-$FDFF) |
| Interrupts & CPU Sleep |
references/irq_interrupts_cpu_sleep.md |
IRQ vector, interrupt enable/status bits, CPU sleep (JAM) |
| Sprite Engine |
references/sprite_engine.md |
Display init, double-buffering, VIDBAS/COLLBAS macros |
Debugging Workflow
1. Load and Orient
load_media → get_media_info → get_6502_status → get_screenshot
Start every session by loading the ROM, confirming it loaded correctly, then checking CPU state and taking a screenshot to understand the current game state. If a .sym, .elf, .lbl, or .noi file exists alongside the ROM, symbols are loaded automatically.
Load additional symbols with load_symbols or add individual labels with add_symbol.
2. Pause and Inspect
Always call debug_pause before inspecting state. While paused:
- CPU state:
get_6502_status — registers A, X, Y, S, P (flags), PC, interrupt status, MAPCTL visibility
- Disassembly:
get_disassembly with a start/end address range — only shows executed code paths
- Call stack:
get_call_stack — current subroutine hierarchy
- Memory:
read_memory with area name (RAM, Zero Page, Stack, Bank 0, Bank 0A, Bank 1, Bank 1A, BIOS, EEPROM) and address/length
3. Set Breakpoints
Use breakpoints to stop execution at points of interest:
| Breakpoint Type |
Tool |
Use Case |
| Execution |
set_breakpoint (type: exec) |
Stop when PC reaches address |
| Read |
set_breakpoint (type: read) |
Stop when memory address is read |
| Write |
set_breakpoint (type: write) |
Stop when memory address is written |
| Range |
set_breakpoint_range |
Cover an address range (exec/read/write) |
| IRQ |
set_breakpoint_on_irq |
Stop on specific timer IRQ (Timer0-7: HBLANK, VBLANK, UART, etc.) |
Important: Read/write breakpoints stop with PC at the instruction after the memory access.
Manage breakpoints with list_breakpoints, remove_breakpoint, list_breakpoints_on_irq, clear_breakpoint_on_irq.
4. Step Through Code
After hitting a breakpoint or pausing:
| Action |
Tool |
Behavior |
| Step Into |
debug_step_into |
Execute one instruction, enter subroutines |
| Step Over |
debug_step_over |
Execute one instruction, skip JSR calls |
| Step Out |
debug_step_out |
Run until RTS/RTI returns from current subroutine |
| Step Frame |
debug_step_frame |
Execute until next VBLANK; use mode: "sync" before dependent calls |
| Run To |
debug_run_to_cursor |
Continue until PC reaches target address |
| Continue |
debug_continue |
Resume normal execution |
After each step, call get_6502_status and get_disassembly to see where you are.
5. Trace Execution
The trace logger records CPU instructions interleaved with hardware events (Suzy math/sprites, Mikey timers/audio/UART, cartridge access).
set_trace_log with enabled: true to start recording (optionally filter event types)
- Let the game run or step through code
set_trace_log with enabled: false to stop (entries are preserved)
get_trace_log to read recorded entries
Tracing is essential for understanding timing-sensitive code, interrupt handlers, and hardware interaction sequences.
Hardware Inspection
Mikey (Display, Timers, Audio)
get_mikey_registers — all registers at $FD00-$FDFF, or filter by address
get_mikey_timers — Timer 0-7 status (HBLANK, VBLANK, UART, sample rate, etc.)
get_mikey_audio — audio channel 0-3 status (volume, feedback, output, etc.)
get_lcd_status — LCD pixel/DMA info (only on visible lines)
write_mikey_register — modify a Mikey register live
Suzy (Sprites, Math)
get_suzy_registers — all registers at $FC00-$FCFF, or filter by address
write_suzy_register — modify a Suzy register live
Other Hardware
get_uart_status — ComLynx serial port state
get_cart_status — cartridge address generation, bank info, AUDIN
get_eeprom_status — EEPROM type, size, mode, state, IO pins
Frame Buffers and Screenshots
get_screenshot — current rendered frame as PNG
get_frame_buffer — raw debug frame buffer (VIDBAS from Suzy or DISPADR from Mikey)
Use screenshots after stepping or continuing to see the visual impact of changes.
Common Debugging Scenarios
Finding an Interrupt Handler
set_breakpoint_on_irq for the target IRQ (e.g., Timer0 for HBLANK, Timer2 for VBLANK)
debug_continue to run until the IRQ fires
get_6502_status + get_disassembly to see the handler code
get_call_stack to see how deep you are
add_symbol to label the handler address and any subroutines it calls
Diagnosing Graphics Corruption
debug_pause → get_suzy_registers — check VIDBAS, COLLBAS, sprite control
get_frame_buffer — capture both Suzy and Mikey buffers
get_mikey_registers — check DISPADR, display DMA settings
get_lcd_status — verify line-by-line rendering state
- Set read/write breakpoints on display buffer addresses to catch corruption source
Analyzing a Subroutine
set_breakpoint at the subroutine entry point
debug_continue → when hit, get_6502_status
- Step through with
debug_step_into / debug_step_over
- After each step: check registers, read relevant memory
add_symbol for the routine and any called subroutines
add_disassembler_bookmark to mark interesting locations
Tracking a Variable
add_memory_watch on the variable's address — watches are visible in the emulator GUI
- Set a write breakpoint with
set_breakpoint (type: write) on that address
- When hit,
get_disassembly reveals what code is modifying it
get_call_stack shows the call chain leading to the write
Timing Analysis
set_breakpoint_on_irq for Timer interrupts
set_trace_log with enabled: true to start recording
get_trace_log to see the interleaved CPU + hardware events
- Analyze timer reload values via
get_mikey_timers
- Correlate timer fires with code execution in the trace
Debug Output for Homebrew Development
Homebrew games can send debug text to the Trace Logger via unused Mikey registers $FDC0–$FDC4 — a printf-style mechanism without breakpoints.
set_trace_log with enabled: true and debug_output: true
- Run your game — text written through the registers appears in
get_trace_log
| Register |
Write |
$FDC0 |
Flush buffer to Trace Logger (any non-zero value) |
$FDC1 |
Append byte as ASCII character |
$FDC2 |
Append byte as two hex digits |
$FDC3 |
Set string pointer low byte |
$FDC4 |
Set string pointer high byte (triggers copy) |
ca65 example:
; Print string + hex value in a single trace entry
; A/X = string pointer, Y = hex value
.proc debug_print
sta $FDC3 ; pointer low
stx $FDC4 ; pointer high (triggers copy)
sty $FDC2 ; append Y as hex
lda #1
sta $FDC0 ; flush
rts
.endproc
lda #<message
ldx #>message
ldy player_hp
jsr debug_print
message: .asciiz "Player HP: "
When disabled (the default), these registers are no-ops — safe to leave in shipping code.
Organizing Your Debug Session
- Symbols: Use
add_symbol liberally to label addresses you've identified — makes disassembly readable
- Bookmarks: Use
add_disassembler_bookmark for code locations and add_memory_bookmark for data regions
- Watches: Use
add_memory_watch for variables you're tracking across steps
- Save states: Use
save_state / load_state to snapshot and restore emulator state at interesting points
- Rewind: Use
get_rewind_status + rewind_seek to scrub back through recent execution history without manual save states
- Screenshots: Capture visual state with
get_screenshot after significant changes
Rewind (Time Travel Debugging)
The emulator continuously records snapshots into a ring buffer during gameplay. You can seek to any recorded snapshot to restore full emulator state at that point in time — like time travel debugging.
Workflow
- Check availability:
get_rewind_status — returns snapshot count, capacity, buffered seconds
- Pause:
debug_pause — the emulator must be paused before seeking
- Seek:
rewind_seek with a snapshot number (1 = oldest, snapshot_count = newest)
- Inspect:
get_6502_status, get_disassembly, get_screenshot, read_memory, etc.
- Iterate: Seek to different snapshots to narrow down when a bug first appeared
- Resume or continue debugging:
debug_continue to resume from the seeked state
Tools
| Tool |
Description |
get_rewind_status |
Snapshot count, capacity, buffered seconds, configuration |
rewind_seek |
Jump to snapshot N (1=oldest, count=newest). Non-destructive — can seek repeatedly |
Key Details
- Non-destructive seeking:
rewind_seek loads a snapshot without removing it. You can seek to the same snapshot multiple times, or jump between different snapshots freely.
- Snapshot numbering: Snapshot 1 is the oldest available, snapshot_count is the newest (most recent).
- Buffer size: Configured by the user (default: 10 seconds). When full, oldest snapshots are overwritten.
- Granularity: Snapshots are taken every N frames (configurable). Default is every frame for maximum precision.
Bug Reproduction with Rewind
- Let the game run past the bug occurrence
debug_pause → get_rewind_status to see how far back you can go
- Binary search with
rewind_seek: try the midpoint, check if the bug is visible (get_screenshot), then narrow the range
- Once you find the exact snapshot where the bug appears, inspect CPU/memory state
- Set breakpoints at the relevant code, then
rewind_seek to a snapshot just before the bug and debug_continue
1---2name: gearlynx-debugging3description: Debug and trace Atari Lynx games using the Gearlynx emulator MCP server. Provides workflows for 6502 CPU debugging, breakpoint management, hardware inspection, disassembly analysis, and execution tracing. Use when the user wants to debug a Lynx game, trace code execution, inspect CPU registers or hardware state, set breakpoints, analyze interrupts, step through 6502 instructions, reverse engineer game code, examine Mikey/Suzy registers, view the call stack, or diagnose rendering, audio, or timing issues. Also use when the user mentions Atari Lynx development, Lynx homebrew testing, or 6502 debugging with Gearlynx.4---56# Atari Lynx Game Debugging with Gearlynx78## Overview910Debug Atari Lynx games using the Gearlynx emulator as an MCP server. Control execution (pause, step, breakpoints), inspect the 6502 CPU and hardware (Mikey, Suzy), read/write memory, disassemble code, trace instructions, and capture screenshots — all through MCP tool calls. Hardware documentation is available in the [references/](references/) directory.1112## MCP Server Prerequisite1314**IMPORTANT — Check before installing:** Before attempting any installation or configuration, you MUST first verify if the Gearlynx MCP server is already connected in your current session. In the default mode, call `debug_get_status` directly. If Gearlynx was intentionally started with `--mcp-router`, call `get_tool_info` with `{"name":"debug_get_status"}`, then call `execute_tool` with `{"name":"debug_get_status","arguments":{}}`. A valid response from either workflow means the server is active and ready.1516Only if neither workflow is available or the call fails, you need to help install and configure the Gearlynx MCP server:1718### Installing Gearlynx1920Run the bundled install script (macOS/Linux):2122```bash23bash scripts/install.sh24```2526This installs Gearlynx via Homebrew on macOS or downloads the latest release on Linux. It prints the binary path on completion. You can also set `INSTALL_DIR` to control where the binary goes (default: `~/.local/bin`).2728Alternatively, download from [GitHub Releases](https://github.com/drhelius/Gearlynx/releases/latest) or install with `brew install --cask drhelius/geardome/gearlynx` on macOS.2930### Connecting as MCP Server3132Configure your AI client to run Gearlynx as an MCP server via STDIO transport. Example for Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`):33```json34{35 "mcpServers": {36 "gearlynx": {37 "command": "/path/to/gearlynx",38 "args": ["--mcp-stdio"]39 }40 }41}42```43Replace `/path/to/gearlynx` with the actual binary path from the install script. Add `--headless` before `--mcp-stdio` on headless machines.4445### Hardware Documentation (References)4647Atari Lynx hardware documentation is available in the [references/](references/) directory. Load them into your context when investigating specific hardware.4849| Reference | File | Load when... |50|---|---|---|51| General Overview | [references/lynx1_general_overview.md](references/lynx1_general_overview.md) | System specs, feature set, overall architecture |52| Hardware Overview | [references/lynx2_hardware_overview.md](references/lynx2_hardware_overview.md) | System block diagram: CPU, Mikey, Suzy, RAM, cart |53| Hardware Quirks | [references/lynx3_software_hardware_perniciousness.md](references/lynx3_software_hardware_perniciousness.md) | Unsafe register operations, hardware gotchas |54| CPU/ROM | [references/lynx4_cpu_rom.md](references/lynx4_cpu_rom.md) | 65C02 cycle timing, CPU sleep, ROM mapping |55| Display | [references/lynx5_display.md](references/lynx5_display.md) | Frame rate, pen/palette colors, display buffer addresses |56| Sprite/Collision | [references/lynx6_sprite_collision.md](references/lynx6_sprite_collision.md) | SCB format, sprite types, collision depository |57| Audio/ROM Cart | [references/lynx7_audio_tape_romcart.md](references/lynx7_audio_tape_romcart.md) | 4 audio channels, feedback taps, stereo, ROM cart banking |58| Timers/Interrupts | [references/lynx8_timers_interrupts.md](references/lynx8_timers_interrupts.md) | 8 timer channels, linking, reload, IRQ sources |59| UART | [references/lynx8a_uart.md](references/lynx8a_uart.md) | ComLynx serial port, UART registers, baud rate, flow control |60| Other Hardware | [references/lynx9_other_hardware.md](references/lynx9_other_hardware.md) | Hardware multiply/divide, parallel port, upward compatibility |61| System Reset | [references/lynx10_system_reset.md](references/lynx10_system_reset.md) | Reset/power-up sequence, Suzy/Mikey init, boot ROM |62| System Bus Interplay | [references/lynx11_system_bus_interplay.md](references/lynx11_system_bus_interplay.md) | Bus masters, page mode, DMA timing, bus contention |63| Hardware Addresses | [references/hardware_addresses.md](references/hardware_addresses.md) | 64K memory map, Mikey/Suzy register addresses ($FC00-$FCFF, $FD00-$FDFF) |64| Interrupts & CPU Sleep | [references/irq_interrupts_cpu_sleep.md](references/irq_interrupts_cpu_sleep.md) | IRQ vector, interrupt enable/status bits, CPU sleep (JAM) |65| Sprite Engine | [references/sprite_engine.md](references/sprite_engine.md) | Display init, double-buffering, VIDBAS/COLLBAS macros |6667---6869## Debugging Workflow7071### 1. Load and Orient7273```74load_media → get_media_info → get_6502_status → get_screenshot75```7677Start every session by loading the ROM, confirming it loaded correctly, then checking CPU state and taking a screenshot to understand the current game state. If a `.sym`, `.elf`, `.lbl`, or `.noi` file exists alongside the ROM, symbols are loaded automatically.7879Load additional symbols with `load_symbols` or add individual labels with `add_symbol`.8081### 2. Pause and Inspect8283Always call `debug_pause` before inspecting state. While paused:8485- **CPU state**: `get_6502_status` — registers A, X, Y, S, P (flags), PC, interrupt status, MAPCTL visibility86- **Disassembly**: `get_disassembly` with a start/end address range — only shows executed code paths87- **Call stack**: `get_call_stack` — current subroutine hierarchy88- **Memory**: `read_memory` with area name (RAM, Zero Page, Stack, Bank 0, Bank 0A, Bank 1, Bank 1A, BIOS, EEPROM) and address/length8990### 3. Set Breakpoints9192Use breakpoints to stop execution at points of interest:9394| Breakpoint Type | Tool | Use Case |95|---|---|---|96| Execution | `set_breakpoint` (type: exec) | Stop when PC reaches address |97| Read | `set_breakpoint` (type: read) | Stop when memory address is read |98| Write | `set_breakpoint` (type: write) | Stop when memory address is written |99| Range | `set_breakpoint_range` | Cover an address range (exec/read/write) |100| IRQ | `set_breakpoint_on_irq` | Stop on specific timer IRQ (Timer0-7: HBLANK, VBLANK, UART, etc.) |101102**Important**: Read/write breakpoints stop with PC at the instruction *after* the memory access.103104Manage breakpoints with `list_breakpoints`, `remove_breakpoint`, `list_breakpoints_on_irq`, `clear_breakpoint_on_irq`.105106### 4. Step Through Code107108After hitting a breakpoint or pausing:109110| Action | Tool | Behavior |111|---|---|---|112| Step Into | `debug_step_into` | Execute one instruction, enter subroutines |113| Step Over | `debug_step_over` | Execute one instruction, skip JSR calls |114| Step Out | `debug_step_out` | Run until RTS/RTI returns from current subroutine |115| Step Frame | `debug_step_frame` | Execute until next VBLANK; use `mode: "sync"` before dependent calls |116| Run To | `debug_run_to_cursor` | Continue until PC reaches target address |117| Continue | `debug_continue` | Resume normal execution |118119After each step, call `get_6502_status` and `get_disassembly` to see where you are.120121### 5. Trace Execution122123The trace logger records CPU instructions interleaved with hardware events (Suzy math/sprites, Mikey timers/audio/UART, cartridge access).1241251. `set_trace_log` with `enabled: true` to start recording (optionally filter event types)1262. Let the game run or step through code1273. `set_trace_log` with `enabled: false` to stop (entries are preserved)1284. `get_trace_log` to read recorded entries129130Tracing is essential for understanding timing-sensitive code, interrupt handlers, and hardware interaction sequences.131132---133134## Hardware Inspection135136### Mikey (Display, Timers, Audio)137138- `get_mikey_registers` — all registers at $FD00-$FDFF, or filter by address139- `get_mikey_timers` — Timer 0-7 status (HBLANK, VBLANK, UART, sample rate, etc.)140- `get_mikey_audio` — audio channel 0-3 status (volume, feedback, output, etc.)141- `get_lcd_status` — LCD pixel/DMA info (only on visible lines)142- `write_mikey_register` — modify a Mikey register live143144### Suzy (Sprites, Math)145146- `get_suzy_registers` — all registers at $FC00-$FCFF, or filter by address147- `write_suzy_register` — modify a Suzy register live148149### Other Hardware150151- `get_uart_status` — ComLynx serial port state152- `get_cart_status` — cartridge address generation, bank info, AUDIN153- `get_eeprom_status` — EEPROM type, size, mode, state, IO pins154155### Frame Buffers and Screenshots156157- `get_screenshot` — current rendered frame as PNG158- `get_frame_buffer` — raw debug frame buffer (VIDBAS from Suzy or DISPADR from Mikey)159160Use screenshots after stepping or continuing to see the visual impact of changes.161162---163164## Common Debugging Scenarios165166### Finding an Interrupt Handler1671681. `set_breakpoint_on_irq` for the target IRQ (e.g., Timer0 for HBLANK, Timer2 for VBLANK)1692. `debug_continue` to run until the IRQ fires1703. `get_6502_status` + `get_disassembly` to see the handler code1714. `get_call_stack` to see how deep you are1725. `add_symbol` to label the handler address and any subroutines it calls173174### Diagnosing Graphics Corruption1751761. `debug_pause` → `get_suzy_registers` — check VIDBAS, COLLBAS, sprite control1772. `get_frame_buffer` — capture both Suzy and Mikey buffers1783. `get_mikey_registers` — check DISPADR, display DMA settings1794. `get_lcd_status` — verify line-by-line rendering state1805. Set read/write breakpoints on display buffer addresses to catch corruption source181182### Analyzing a Subroutine1831841. `set_breakpoint` at the subroutine entry point1852. `debug_continue` → when hit, `get_6502_status`1863. Step through with `debug_step_into` / `debug_step_over`1874. After each step: check registers, read relevant memory1885. `add_symbol` for the routine and any called subroutines1896. `add_disassembler_bookmark` to mark interesting locations190191### Tracking a Variable1921931. `add_memory_watch` on the variable's address — watches are visible in the emulator GUI1942. Set a write breakpoint with `set_breakpoint` (type: write) on that address1953. When hit, `get_disassembly` reveals what code is modifying it1964. `get_call_stack` shows the call chain leading to the write197198### Timing Analysis1992001. `set_breakpoint_on_irq` for Timer interrupts2012. `set_trace_log` with `enabled: true` to start recording2023. `get_trace_log` to see the interleaved CPU + hardware events2034. Analyze timer reload values via `get_mikey_timers`2045. Correlate timer fires with code execution in the trace205206### Debug Output for Homebrew Development207208Homebrew games can send debug text to the Trace Logger via unused Mikey registers `$FDC0`–`$FDC4` — a `printf`-style mechanism without breakpoints.2092101. `set_trace_log` with `enabled: true` and `debug_output: true`2112. Run your game — text written through the registers appears in `get_trace_log`212213| Register | Write |214|----------|-------|215| `$FDC0` | Flush buffer to Trace Logger (any non-zero value) |216| `$FDC1` | Append byte as ASCII character |217| `$FDC2` | Append byte as two hex digits |218| `$FDC3` | Set string pointer low byte |219| `$FDC4` | Set string pointer high byte (triggers copy) |220221**ca65 example:**222223```asm224; Print string + hex value in a single trace entry225; A/X = string pointer, Y = hex value226.proc debug_print227 sta $FDC3 ; pointer low228 stx $FDC4 ; pointer high (triggers copy)229 sty $FDC2 ; append Y as hex230 lda #1231 sta $FDC0 ; flush232 rts233.endproc234235 lda #<message236 ldx #>message237 ldy player_hp238 jsr debug_print239240message: .asciiz "Player HP: "241```242243When disabled (the default), these registers are no-ops — safe to leave in shipping code.244245---246247## Organizing Your Debug Session248249- **Symbols**: Use `add_symbol` liberally to label addresses you've identified — makes disassembly readable250- **Bookmarks**: Use `add_disassembler_bookmark` for code locations and `add_memory_bookmark` for data regions251- **Watches**: Use `add_memory_watch` for variables you're tracking across steps252- **Save states**: Use `save_state` / `load_state` to snapshot and restore emulator state at interesting points253- **Rewind**: Use `get_rewind_status` + `rewind_seek` to scrub back through recent execution history without manual save states254- **Screenshots**: Capture visual state with `get_screenshot` after significant changes255256---257258## Rewind (Time Travel Debugging)259260The emulator continuously records snapshots into a ring buffer during gameplay. You can seek to any recorded snapshot to restore full emulator state at that point in time — like time travel debugging.261262### Workflow2632641. **Check availability**: `get_rewind_status` — returns snapshot count, capacity, buffered seconds2652. **Pause**: `debug_pause` — the emulator must be paused before seeking2663. **Seek**: `rewind_seek` with a snapshot number (1 = oldest, snapshot_count = newest)2674. **Inspect**: `get_6502_status`, `get_disassembly`, `get_screenshot`, `read_memory`, etc.2685. **Iterate**: Seek to different snapshots to narrow down when a bug first appeared2696. **Resume or continue debugging**: `debug_continue` to resume from the seeked state270271### Tools272273| Tool | Description |274|---|---|275| `get_rewind_status` | Snapshot count, capacity, buffered seconds, configuration |276| `rewind_seek` | Jump to snapshot N (1=oldest, count=newest). Non-destructive — can seek repeatedly |277278### Key Details279280- **Non-destructive seeking**: `rewind_seek` loads a snapshot without removing it. You can seek to the same snapshot multiple times, or jump between different snapshots freely.281- **Snapshot numbering**: Snapshot 1 is the oldest available, snapshot_count is the newest (most recent).282- **Buffer size**: Configured by the user (default: 10 seconds). When full, oldest snapshots are overwritten.283- **Granularity**: Snapshots are taken every N frames (configurable). Default is every frame for maximum precision.284285### Bug Reproduction with Rewind2862871. Let the game run past the bug occurrence2882. `debug_pause` → `get_rewind_status` to see how far back you can go2893. Binary search with `rewind_seek`: try the midpoint, check if the bug is visible (`get_screenshot`), then narrow the range2904. Once you find the exact snapshot where the bug appears, inspect CPU/memory state2915. Set breakpoints at the relevant code, then `rewind_seek` to a snapshot just before the bug and `debug_continue`