gearlynx-debugging — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited gearlynx-debugging (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
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. Call debug_get_status — if it returns a valid response, the server is active and ready.
Only if the tool is not available or the call fails, you need to help install and configure the Gearlynx MCP server:
Run the bundled install script (macOS/Linux):
bash scripts/install.shThis 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.
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.
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 |
load_media → get_media_info → get_6502_status → get_screenshotStart 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.
Always call debug_pause before inspecting state. While paused:
get_6502_status — registers A, X, Y, S, P (flags), PC, interrupt status, MAPCTL visibilityget_disassembly with a start/end address range — only shows executed code pathsget_call_stack — current subroutine hierarchyread_memory with area name (RAM, Zero Page, Stack, Bank 0, Bank 0A, Bank 1, Bank 1A, BIOS, EEPROM) and address/lengthUse 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.
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 |
| 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.
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)set_trace_log with enabled: false to stop (entries are preserved)get_trace_log to read recorded entriesTracing is essential for understanding timing-sensitive code, interrupt handlers, and hardware interaction sequences.
get_mikey_registers — all registers at $FD00-$FDFF, or filter by addressget_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 liveget_suzy_registers — all registers at $FC00-$FCFF, or filter by addresswrite_suzy_register — modify a Suzy register liveget_uart_status — ComLynx serial port stateget_cart_status — cartridge address generation, bank info, AUDINget_eeprom_status — EEPROM type, size, mode, state, IO pinsget_screenshot — current rendered frame as PNGget_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.
set_breakpoint_on_irq for the target IRQ (e.g., Timer0 for HBLANK, Timer2 for VBLANK)debug_continue to run until the IRQ firesget_6502_status + get_disassembly to see the handler codeget_call_stack to see how deep you areadd_symbol to label the handler address and any subroutines it callsdebug_pause → get_suzy_registers — check VIDBAS, COLLBAS, sprite controlget_frame_buffer — capture both Suzy and Mikey buffersget_mikey_registers — check DISPADR, display DMA settingsget_lcd_status — verify line-by-line rendering stateset_breakpoint at the subroutine entry pointdebug_continue → when hit, get_6502_statusdebug_step_into / debug_step_overadd_symbol for the routine and any called subroutinesadd_disassembler_bookmark to mark interesting locationsadd_memory_watch on the variable's address — watches are visible in the emulator GUIset_breakpoint (type: write) on that addressget_disassembly reveals what code is modifying itget_call_stack shows the call chain leading to the writeset_breakpoint_on_irq for Timer interruptsset_trace_log with enabled: true to start recordingget_trace_log to see the interleaved CPU + hardware eventsget_mikey_timersHomebrew 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: trueget_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.
add_symbol liberally to label addresses you've identified — makes disassembly readableadd_disassembler_bookmark for code locations and add_memory_bookmark for data regionsadd_memory_watch for variables you're tracking across stepssave_state / load_state to snapshot and restore emulator state at interesting pointsget_rewind_status + rewind_seek to scrub back through recent execution history without manual save statesget_screenshot after significant changesThe 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.
get_rewind_status — returns snapshot count, capacity, buffered secondsdebug_pause — the emulator must be paused before seekingrewind_seek with a snapshot number (1 = oldest, snapshot_count = newest)get_6502_status, get_disassembly, get_screenshot, read_memory, etc.debug_continue to resume from the seeked state| 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 |
rewind_seek loads a snapshot without removing it. You can seek to the same snapshot multiple times, or jump between different snapshots freely.debug_pause → get_rewind_status to see how far back you can gorewind_seek: try the midpoint, check if the bug is visible (get_screenshot), then narrow the rangerewind_seek to a snapshot just before the bug and debug_continue~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.