Source-level debugging of Atari 8-bit programs from inside VS Code, driving the Fujisan emulator over its TCP debug API. Supports both toolchains:
- llvm-mos — C/C++ with ELF + DWARF debug info
- cc65 / ca65 — C and assembly, using ld65's
.dbgdebug-info file
Features
- Source-line breakpoints (C/C++), driven by the program's DWARF line table.
- Registers + processor flags — A/X/Y/PC/SP/P with a decoded
NV-BDIZCview. - Variables — inspect and edit globals and locals/parameters:
- globals & file/function statics (typed; arrays and structs expand),
- locals in zero-page imaginary registers (
RCn/RSn), CPU registers (A/X/Y), and frame-relative (DW_OP_fbreg) storage, <optimized out>reported honestly when a value's live range has ended.
- Watches & hovers (
evaluate) over in-scope locals and globals. - Call stack — multi-frame heuristic unwind of the 6502 hardware stack.
- Stepping — source-level step in / over / out, plus instruction stepping.
- Memory — read/write via the memory view.
Requirements
-
Fujisan with its TCP server enabled (Tools → TCP Server, default
localhost:6502). You do not need to start it yourself — if nothing is listening, the extension launches it and shuts it down again when the session ends. An emulator you already have open is reused and left running.The executable is found via the
fujisanPathlaunch attribute, then thexexDebug.fujisanPathsetting, then well-known locations, thenPATH.Your Fujisan must include the per-instruction PC-breakpoint fix ([PR #41](https://github.com/pedgarcia/fujisan/pull/41), merged 2026-07-13). Without it, breakpoints on addresses that execute only once — a program's entry point, or any ordinary line of code — are silently missed; only breakpoints inside hot loops appear to work. The fix is on
mainbut is not yet in a tagged release (latest is v2.0.4), so build Fujisan frommainfor now. The extension warns in the debug console when the build it started appears to lack the fix.Tested on Linux. macOS and Windows emulator discovery is implemented but untested — reports welcome.
-
llvm-mos or cc65 on your
PATH. The DWARF path shells out tollvm-dwarfdumpandllvm-nm, both of which ship with llvm-mos, so make sure itsbin/directory is onPATH. -
A program built with debug info, so the companion file sits next to the
.xex:llvm-mos — emits
<name>.xexalongside<name>.xex.elf(symbols + DWARF). Compile with-gand without aggressive inlining if you want readable locals:mos-atari8-clang -g -Og -o build/main.xex main.ccc65 / ca65 — build with
-gand ask ld65 for a.dbgfile (the.lblis used to locate the C-stack pointer, so pass-Lntoo):cl65 -t atari -g -Ln prog.lbl -Wl --dbgfile,prog.dbg -o prog.xex prog.cThe extension picks the provider automatically: a
.dbgnext to the program selects the cc65 path, otherwise it looks for the ELF. Both can be overridden with thedbg/elflaunch attributes.
Usage
Add a launch configuration of type xex:
{
"type": "xex",
"request": "launch",
"name": "Debug Atari XEX (Fujisan)",
"program": "${workspaceFolder}/build/main.xex",
"stopOnEntry": true
}
Press F5. Fujisan starts automatically if it isn't already running, and the
.xex.elf (or .dbg) beside program is discovered automatically for symbols
and source mapping.
Launch attributes
| attribute | default | description |
|---|---|---|
program |
(required) | path to the .xex to debug |
elf |
<program>.elf |
override the ELF used for symbols/DWARF (llvm-mos) |
dbg |
<program>.dbg |
override the cc65 debug-info file (ld65 --dbgfile) |
stopOnEntry |
true |
break at the program entry point |
tcpHost |
127.0.0.1 |
Fujisan TCP host |
tcpPort |
6502 |
Fujisan TCP debug port |
fujisanPath |
(auto) | path to the Fujisan launcher/executable |
autoLaunch |
true |
start Fujisan when nothing is listening on tcpPort |
sourceRoot |
(auto) | root for resolving source files |
Architecture
The debug adapter runs in-process. Emulator backends sit behind a single
ITransport interface (src/transport/), so additional backends (mos-sim,
Altirra) can be added without touching the DAP core. Source/symbol mapping is
owned by the extension (DWARF via llvm-dwarfdump + llvm-nm); the emulator is
driven at the address level.
Debug info sits behind a DebugInfo provider interface (src/debuginfo/), with
two implementations: DWARF via llvm-dwarfdump/llvm-nm for llvm-mos, and an
ld65 .dbg parser for cc65 (src/cc65/). cc65 auto-locals resolve through the
C-stack pointer (c_sp); note cc65 records no C type for locals, so they are
shown as single bytes, and module-level globals come from mangled symbol labels
with widths inferred from address gaps.