Topics

UTTORI ASM · IN DEVELOPMENT

Live debugging

Connect VS Code to a matching emulator runtime for stepping and breakpoints.

The extension ships the MIT ares protocol sidecar, but never bundles an emulator executable, its runtime resources, or a test ROM. Live launch is allowed only in a trusted workspace and requires explicit paths to a versioned runtime manifest, the manifest-matched emulator executable, and its portable runtime root. The exact program bytes must still match the debug map before the emulator starts.

For the selected ares backend:

{
  "type": "uttori-asm",
  "request": "launch",
  "name": "Debug Uttori ASM (ares)",
  "backend": "ares",
  "program": "${workspaceFolder}/build/rebuilt-content.sfc",
  "debugMap": "${workspaceFolder}/build/rebuilt-content.sfc.debug.json",
  "sourceRoot": "${workspaceFolder}",
  "runtimeManifest": "${workspaceFolder}/.debug/ares-runtime-manifest.json",
  "emulator": "${workspaceFolder}/.debug/ares/ares.app/Contents/MacOS/ares",
  "runtimeDirectory": "${workspaceFolder}/.debug/ares"
}

The extension starts the ares sidecar without a shell, passes a fresh 256-bit session token through the child environment rather than command-line arguments, and rejects any capability profile that differs from the built editor profile. An optional sidecar path is a development-only override for the bundled MIT module. It does not bypass protocol, token, or capability validation.

Watches evaluate only bounded integer expressions against the selected stopped frame's registers. Native $hex, %binary, low/high/bank selectors, arithmetic, comparisons, logical operations, bitwise operations, shifts, and parentheses are supported. Expressions cannot call JavaScript, read files, access emulator memory, or mutate emulator state.

The ares manifest selects "frontend": "desktop" for a graphical game window, audio, and controller input. "headless" (the default when omitted) remains available for automation. Both use the same debugger/trace bridge. A desktop runtime must contain the separately built and hashed application resources. A headless executable cannot become graphical by changing the manifest alone. Launch pauses at entry: Continue (F5) starts gameplay. Configure controls in ares Settings → Input. The desktop stays responsive while the CPU is paused, and runs while unfocused for capture dialogs.

Set a source breakpoint by clicking the editor gutter or pressing F9 on a generated assembly line, then use Continue (F5). A label, comment, or directive breakpoint moves to the next mapped executable line and VS Code shows the resolved location. Breakpoints may also be added, removed, or changed while the emulator is running: the adapter performs a hidden pause/update/resume transaction because the native emulator protocols only mutate execute breakpoints at a stopped boundary. The next matching instruction produces a normal breakpoint stop, where stepping and inspection are available. Use Debug in Emulator, not Run in Emulator, when breakpoints are required.

Run Assembly: Configure Emulator Runtime once per workspace and select the ares runtime manifest, emulator executable, and runtime directory. The absolute binding is kept in machine-local extension storage rather than project settings. Run in Emulator and Debug in Emulator build first and launch only the returned ROM/debug-map pair. If no picker binding exists, these commands reuse a single unambiguous live runtime explicitly configured in the folder's launch.json. Conflicting runtime configurations require the picker. Unresolved variables other than ${workspaceFolder} are not guessed. Startup status, the chosen frontend, native stderr, and failures appear in Output → Uttori Emulator. Launch errors also produce a notification. Optional analysis annotations load asynchronously after debugger connection rather than delaying initialization. Run and Watch rebuilds on a debounced save and performs a deterministic full emulator restart only when a successful build changes the ROM or map. A compilation error leaves the current session running, and VS Code reapplies source breakpoints against the replacement map.

Generated configs contain entryPoints: ["main.asm"]. To open a parent folder instead of the generated directory, set asm.configFile to disassembly/uttori-asm.config.json and asm.analysisProject to disassembly/analysis in the parent's .vscode/settings.json. Paths read from the config resolve beside that config. Explicit asm.* path settings resolve from the workspace root. Run Build Binary before using a direct F5 launch after switching workspace roots so portable debug-map source paths match the launch's sourceRoot. At launch the debugger also checks the map's portable source paths against the configured root and adjacent artifact parents, choosing and logging the best on-disk match. This keeps source navigation valid for an initial generated map when the project is opened from its parent folder.

Every generated SNES project also includes defines.asm. main.asm includes it before the bank sources, and emitted absolute MMIO operands use conventional names such as INIDISP, NMITIMEN, and DMAP0 instead of anonymous addresses. The file also supplies controller, interrupt/status, and DMA-channel masks. Every generated register and mask definition has its own trailing description, so hovering a symbolic operand shows that register's purpose. Generated banks continue to express processor width with .a8/.a16 and .i8/.i16 directives.

While stopped, the Variables view groups registers by processor and native group, decodes the 65816 N V M X D I Z C E flags while retaining raw P/E, lists only named memory spaces whose backend reads are guaranteed side-effect-free, and shows 32 bytes from the 65816 or SPC700 stack pointer. Native bridges currently expose an explicit “current frame only” row instead of inventing a call stack. Virtual disassembly is discarded and regenerated at every stopped boundary.

Replay scripts can model instruction, step-in, step-over, and step-out transitions. The live ares bridge negotiates exact instruction and step-in support only. Step-over and step-out are labeled unavailable and rejected instead of being approximated.