romdevtools 0.70.0 → 0.71.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/examples/dreamcast/platformer/main.c +31 -0
  3. package/examples/dreamcast/puzzle/main.c +44 -0
  4. package/examples/dreamcast/racing/main.c +39 -0
  5. package/examples/dreamcast/shmup/main.c +50 -0
  6. package/examples/dreamcast/sports/main.c +39 -0
  7. package/package.json +3 -3
  8. package/src/cores/capabilities.js +13 -9
  9. package/src/host/LibretroHost.js +242 -23
  10. package/src/host/callbacks.js +68 -1
  11. package/src/host/cpu-state.js +32 -0
  12. package/src/host/dc-aica-state.js +67 -0
  13. package/src/mcp/tools/audio.js +1 -1
  14. package/src/mcp/tools/disasm.js +1 -1
  15. package/src/mcp/tools/index.js +1 -1
  16. package/src/mcp/tools/platform-docs.js +1 -1
  17. package/src/mcp/tools/platform-tools.js +9 -1
  18. package/src/mcp/tools/platforms.js +46 -10
  19. package/src/mcp/tools/toolchain.js +140 -10
  20. package/src/platforms/dreamcast/MENTAL_MODEL.md +87 -0
  21. package/src/platforms/dreamcast/TROUBLESHOOTING.md +55 -0
  22. package/src/platforms/dreamcast/UPSTREAM_SOURCES.md +57 -0
  23. package/src/platforms/gba/MENTAL_MODEL.md +22 -1
  24. package/src/platforms/n64/MENTAL_MODEL.md +84 -0
  25. package/src/platforms/n64/TROUBLESHOOTING.md +60 -0
  26. package/src/platforms/n64/UPSTREAM_SOURCES.md +52 -0
  27. package/src/platforms/n64/lib/c/n64.c +181 -80
  28. package/src/platforms/ps1/MENTAL_MODEL.md +85 -0
  29. package/src/platforms/ps1/TROUBLESHOOTING.md +55 -0
  30. package/src/platforms/ps1/UPSTREAM_SOURCES.md +54 -0
  31. package/src/platforms/snes/TROUBLESHOOTING.md +10 -0
  32. package/src/toolchains/asar/asar.js +84 -14
  33. package/src/toolchains/mips-c/mips-c.js +35 -1
  34. package/src/toolchains/sh-c/lib/dc.h +65 -15
  35. package/src/toolchains/sh-c/sh-c.js +6 -3
package/CHANGELOG.md CHANGED
@@ -4,6 +4,86 @@ All notable changes to `romdevtools`. Dates are release dates.
4
4
  (Published as `romdev-mcp` through 0.11.0; renamed to `romdevtools` in 0.13.0 —
5
5
  the `romdev-mcp` bin is kept as an alias.)
6
6
 
7
+ ## 0.71.1 — 2026-06-29
8
+
9
+ Feedback from the first run against the `pret/pokeruby` GBA decompilation.
10
+
11
+ - **Project-mode `entry` now resolves a NESTED path** (e.g. `entry:'src/main.c'`). The
12
+ recursive asset staging already walked subdirs, but entry resolution stayed top-level
13
+ only — so any decomp/SDK-layout project whose entry isn't at the repo root failed with
14
+ "entry not found." Now it resolves against the recursive file set, reads the nested file
15
+ as the entry **source**, accepts a bare filename when it uniquely matches one nested
16
+ file, and errors with a "did you mean `src/…`?" hint.
17
+ - **`platform({op:'list'})` honors the `platform` filter** — returns just that one
18
+ platform's row instead of the whole 17-platform matrix (the biggest token sink reported:
19
+ ~17 KB → ~0.8 KB for one platform). New **`slim:true`** drops the verbose per-language
20
+ `note` + `quirks` prose (~60× smaller); detail stays behind `op:'doc'`/`op:'capabilities'`.
21
+ - **`platform({op:'resolve'})` surfaces the toolchain(s)** + a note that the build
22
+ toolchain is WASM/harness-only (was core-paths-only, forcing `node_modules` spelunking).
23
+ - **GBA `mental_model` "What's NOT bundled"** now calls out **agbcc's absence** (the real
24
+ blocker for byte-exact Gen-III decomps like pokeruby/pokeemerald) and adds a "romdev's
25
+ build model" section: `build` is single-shot compile→ROM, the toolchain can't back an
26
+ external Makefile, with the confirmed host agbcc recipe for matching builds.
27
+
28
+ ## 0.71.0 — 2026-06-28
29
+
30
+ ### N64 / PS1 / Dreamcast reach full parity: cpuState + audioDebug + GPU-rendering helper libs + 5 examples each
31
+
32
+ The three next-gen 3D platforms are now first-class alongside the original 14.
33
+
34
+ **Live debugging (cpuState + audioDebug).** PS1 (beetle_psx_hw) and Dreamcast (flycast)
35
+ gained the romdev debug-reader exports, so `cpu({op:'read'})` and
36
+ `audioDebug({op:'inspect'})` light up (N64 already had them):
37
+ - **PS1** — `cpu({op:'read'})` returns the live R3000A register file; `audioDebug({chip:'spu'})`
38
+ decodes the SPU's 24 voices + main volume (read from the raw register mirror, not the
39
+ sweep-quantized `SPU_Read`).
40
+ - **Dreamcast** — `cpu({op:'read'})` returns the SH-4 registers (r0–r15, pc, pr, gbr, vbr,
41
+ sr + decoded flags, mac, fpul); `audioDebug({chip:'aica'})` decodes the AICA's 64
42
+ PCM/ADPCM channels + master volume. Full host plumbing + decoders added.
43
+ - The capability manifest now reports cpuState/audioDebug `true` for all three (verified by
44
+ probing real core exports; `breakpoint`/`watch` remain pending on PS1/DC).
45
+
46
+ **GPU-rendering helper libs (no software framebuffers).** Every 3D platform's helper now
47
+ draws through the GPU — a software rasterizer would be black on the GL cores and <1fps:
48
+ - **N64** — the `n64.h` helper was rewritten to emit a **GBI (F3DEX2) display list** that
49
+ glide64 HLEs onto the GPU (clear/rect as fill-rectangles, triangles scan-converted into
50
+ GPU fill-rect spans), instead of poking pixels into RDRAM (which rendered black on
51
+ glide64). No Nintendo microcode shipped — the OSTask CRC-bait trick selects F3DEX2.
52
+ - **PS1** — already correct (GP0 GPU primitives on beetle_psx_hw).
53
+ - **Dreamcast** — `dc.h` now sets **480i interlace** in `dc_video_init` (240p only showed
54
+ the top 240 of 480 lines), and added a Maple-DMA `dc_pad()` / `dc_pressed()`.
55
+ - `#include "n64.h"` / `#include "psx.h"` now auto-bundle (parity with `dc.h`).
56
+
57
+ **5 genre examples per platform.** N64, PS1, and Dreamcast each ship
58
+ shmup / platformer / puzzle / racing / sports examples — all verified building + rendering
59
+ on the GPU.
60
+
61
+ **Docs.** MENTAL_MODEL + TROUBLESHOOTING for N64, PS1, and Dreamcast (the render model,
62
+ boot story, debug surface, and what's N/A and why).
63
+
64
+ ### asar (SNES) build-tool fixes
65
+
66
+ From real commercial-disassembly feedback:
67
+ - **`build({output:'project'|'run'})` honors `options` + a new `defines` map** (e.g. asar
68
+ `--define`) — they were silently dropped.
69
+ - **New `entry` param** so a project whose top file isn't `main.*` (e.g. an existing
70
+ `smw.asm` disassembly) builds without injecting glue.
71
+ - **Subdirectory assets are staged recursively** (a flat `readdir` missed nested
72
+ `incbin`/`incsrc` targets).
73
+ - **A clean asar error is no longer mislabeled** `[worker] Abort in WASM` (it exits errors
74
+ via a C++-exception path; the misleading heap-pointer line is stripped when real
75
+ diagnostics are present).
76
+ - **The LoROM-header bankcross preflight no longer false-positives** on banks $01+ (the
77
+ header is only in bank $00) and honors `check bankcross off`.
78
+ - **A readfile/filesize advisory** warns when a source reads many distinct files that way
79
+ (the asar-WASM resource limit), pointing at the pre-convert-to-`.bin` + `incbin`
80
+ workaround.
81
+
82
+ ### Toolchain
83
+
84
+ - **sh-c (Dreamcast) defaults to `-O1`** — the sh-elf `cc1.wasm` has an `-O2`-only pass
85
+ that aborts on common control flow; `-O1` dodges it. A user-supplied `-O<level>` wins.
86
+
7
87
  ## 0.70.0 — 2026-06-26
8
88
 
9
89
  ### Dreamcast is ship-ready: verified build → run → screenshot, with a renderable example
@@ -0,0 +1,31 @@
1
+ /* Dreamcast PLATFORMER demo — a side-scrolling scene: a hero hops across platforms
2
+ * while the camera scrolls. Self-animating (no input). Renders on the PowerVR2 via
3
+ * the framebuffer path. */
4
+ #include "dc.h"
5
+
6
+ void main(void){
7
+ int frame = 0;
8
+ dc_video_init();
9
+ for (;;){
10
+ int scroll = (frame * 2) % (DC_W);
11
+ int t = frame % 90;
12
+ int jump = t < 45 ? t : (90 - t); /* 0..45..0 hop */
13
+ int hy = DC_H - 140 - jump * 2;
14
+ int hx = DC_W/2 - 16;
15
+
16
+ dc_clear(dc_rgb(80, 150, 230)); /* sky */
17
+ /* parallax hills */
18
+ { int i; for (i = -1; i < 6; i++){ int x = i*140 - (scroll/2); dc_rect(x, DC_H-200, 120, 200, dc_rgb(60, 170, 90)); } }
19
+ /* ground */
20
+ dc_rect(0, DC_H-60, DC_W, 60, dc_rgb(120, 80, 40));
21
+ /* floating platforms (scrolling) */
22
+ { int i; for (i = -1; i < 6; i++){ int x = i*180 - (scroll % 180); dc_rect(x, DC_H-160, 90, 18, dc_rgb(150, 110, 60)); } }
23
+ /* hero (head + body) */
24
+ dc_rect(hx, hy, 32, 32, dc_rgb(240, 90, 70));
25
+ dc_rect(hx+6, hy+6, 6, 6, dc_rgb(255,255,255)); /* eye */
26
+ dc_rect(hx, hy+32, 32, 20, dc_rgb(40, 60, 200)); /* legs */
27
+ /* a coin that bobs */
28
+ dc_rect(DC_W/2 + 120 - (scroll%240), DC_H-220 + (jump), 18, 18, dc_rgb(255, 220, 60));
29
+ frame++;
30
+ }
31
+ }
@@ -0,0 +1,44 @@
1
+ /* Dreamcast PUZZLE demo — a falling-block (Tetris-like) board that fills + clears
2
+ * rows on a loop. Self-animating (no input). Renders on the PowerVR2 framebuffer. */
3
+ #include "dc.h"
4
+
5
+ #define COLS 10
6
+ #define ROWS 16
7
+ #define CELL 24
8
+
9
+ static unsigned char board[ROWS][COLS];
10
+ static unsigned int rng = 0xACE1u;
11
+ static unsigned int rnd(void){ rng ^= rng << 7; rng ^= rng >> 9; rng ^= rng << 8; return rng; }
12
+
13
+ static const u16 PAL[7] = { 0, 0xF800, 0x07E0, 0x001F, 0xFFE0, 0xF81F, 0x07FF };
14
+
15
+ void main(void){
16
+ int frame = 0, px = COLS/2, py = 0, col = 1, i, r, c;
17
+ dc_video_init();
18
+ for (;;){
19
+ int ox = (DC_W - COLS*CELL)/2, oy = (DC_H - ROWS*CELL)/2;
20
+
21
+ if ((frame % 8) == 0){
22
+ /* drop the active block one row; lock + respawn at the bottom */
23
+ if (py+1 >= ROWS || board[py+1][px]){
24
+ board[py][px] = col;
25
+ /* clear full-ish rows occasionally to keep it moving */
26
+ for (r = 0; r < ROWS; r++){ int full=1; for (c=0;c<COLS;c++) if(!board[r][c]) full=0;
27
+ if (full){ for(c=0;c<COLS;c++) board[r][c]=0; } }
28
+ py = 0; px = rnd()%COLS; col = 1 + rnd()%6;
29
+ if (board[0][px]){ for(r=0;r<ROWS;r++)for(c=0;c<COLS;c++) board[r][c]=0; } /* reset if buried */
30
+ } else py++;
31
+ }
32
+ dc_clear(dc_rgb(18, 18, 30));
33
+ /* board frame */
34
+ dc_rect(ox-4, oy-4, COLS*CELL+8, ROWS*CELL+8, dc_rgb(90,90,120));
35
+ dc_rect(ox, oy, COLS*CELL, ROWS*CELL, dc_rgb(10,10,18));
36
+ /* settled cells */
37
+ for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) if (board[r][c])
38
+ dc_rect(ox + c*CELL+1, oy + r*CELL+1, CELL-2, CELL-2, PAL[board[r][c]]);
39
+ /* active block */
40
+ dc_rect(ox + px*CELL+1, oy + py*CELL+1, CELL-2, CELL-2, PAL[col]);
41
+ frame++;
42
+ (void)i;
43
+ }
44
+ }
@@ -0,0 +1,39 @@
1
+ /* Dreamcast RACING demo — a pseudo-3D road that curves + scrolls toward a horizon,
2
+ * with a car at the bottom that drifts side to side. Self-animating (no input).
3
+ * Renders on the PowerVR2 framebuffer. */
4
+ #include "dc.h"
5
+
6
+ /* tiny fixed sine via a 16-entry quarter table */
7
+ static const int SIN16[17] = {0,49,97,142,181,213,236,251,256,251,236,213,181,142,97,49,0};
8
+ static int isin(int a){ a &= 63; if(a<16) return SIN16[a]; if(a<32) return SIN16[32-a]; if(a<48) return -SIN16[a-32]; return -SIN16[64-a]; }
9
+
10
+ void main(void){
11
+ int frame = 0, y;
12
+ dc_video_init();
13
+ for (;;){
14
+ int horizon = DC_H/3;
15
+ dc_clear(dc_rgb(60, 140, 230)); /* sky */
16
+ dc_rect(0, horizon, DC_W, DC_H-horizon, dc_rgb(40, 120, 50)); /* grass */
17
+
18
+ /* road: for each scanline below the horizon, a trapezoid widening to the
19
+ bottom, shifted by a curve that scrolls. */
20
+ for (y = horizon; y < DC_H; y++){
21
+ int depth = y - horizon;
22
+ int half = depth * (DC_W/2 - 20) / (DC_H - horizon) + 12;
23
+ int curve = isin((frame/2 + depth/4)) * depth / 256;
24
+ int cx = DC_W/2 + curve;
25
+ /* road stripe alternation for motion */
26
+ u16 rc = (((depth + frame) / 12) & 1) ? dc_rgb(70,70,75) : dc_rgb(90,90,95);
27
+ dc_rect(cx - half, y, half*2, 1, rc);
28
+ /* center dashes */
29
+ if ((((depth + frame*2)/16) & 1)) dc_rect(cx-2, y, 4, 1, dc_rgb(240,240,120));
30
+ }
31
+ /* the car (drifts L/R) */
32
+ { int carx = DC_W/2 + isin(frame/3) * 60 / 256 - 24;
33
+ dc_rect(carx, DC_H-70, 48, 40, dc_rgb(220, 40, 40));
34
+ dc_rect(carx+6, DC_H-64, 36, 14, dc_rgb(40,40,60)); /* windshield */
35
+ dc_rect(carx-4, DC_H-44, 10, 18, dc_rgb(20,20,20)); /* wheels */
36
+ dc_rect(carx+42, DC_H-44, 10, 18, dc_rgb(20,20,20)); }
37
+ frame++;
38
+ }
39
+ }
@@ -0,0 +1,50 @@
1
+ /* Dreamcast SHMUP demo — a vertical shooter scene rendered on the PowerVR2 via the
2
+ * framebuffer path. The ship weaves, bullets stream up, enemies descend + recycle.
3
+ * Self-animating (no input) — proves program-controlled graphics render on the GPU.
4
+ * (DC input via dc_pad() reads the resting controller state; full press wiring is a
5
+ * follow-up — see dc.h.) */
6
+ #include "dc.h"
7
+
8
+ #define NB 12
9
+ #define NE 6
10
+
11
+ static int frame;
12
+ static unsigned int rng = 0x1234567u;
13
+ static unsigned int rnd(void){ rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5; return rng; }
14
+
15
+ static int bx[NB], by[NB], ba[NB];
16
+ static int ex[NE], ey[NE];
17
+
18
+ void main(void){
19
+ int i;
20
+ dc_video_init();
21
+ for (i = 0; i < NE; i++){ ex[i] = 60 + i*90; ey[i] = -(int)(rnd()%300); }
22
+ for (;;){
23
+ int shipx = DC_W/2 + (int)(80.0 * ((frame % 120) < 60 ? (frame%60) : (60-(frame%60))) / 60.0 - 40);
24
+ int shipy = DC_H - 80;
25
+
26
+ /* spawn a bullet every 8 frames */
27
+ if ((frame & 7) == 0){
28
+ for (i = 0; i < NB; i++) if (!ba[i]){ ba[i]=1; bx[i]=shipx; by[i]=shipy; break; }
29
+ }
30
+ dc_clear(dc_rgb(8, 10, 28));
31
+ /* starfield */
32
+ for (i = 0; i < 24; i++){
33
+ int sx = (int)((rnd()%DC_W));
34
+ int sy = (int)((frame*2 + i*53) % DC_H);
35
+ dc_rect(sx, sy, 2, 2, dc_rgb(120,120,160));
36
+ }
37
+ /* enemies */
38
+ for (i = 0; i < NE; i++){
39
+ ey[i] += 2;
40
+ if (ey[i] > DC_H){ ey[i] = -(int)(rnd()%200); ex[i] = (int)(rnd()%(DC_W-40)); }
41
+ if (ey[i] >= 0) dc_rect(ex[i], ey[i], 36, 28, dc_rgb(220, 60, 60));
42
+ }
43
+ /* bullets */
44
+ for (i = 0; i < NB; i++) if (ba[i]){ by[i] -= 8; if (by[i] < 0) ba[i]=0; else dc_rect(bx[i]-2, by[i], 4, 12, dc_rgb(255,240,80)); }
45
+ /* player ship (a little arrow of rects) */
46
+ dc_rect(shipx-4, shipy, 8, 24, dc_rgb(80, 200, 255));
47
+ dc_rect(shipx-16, shipy+14, 32, 8, dc_rgb(60, 160, 230));
48
+ frame++;
49
+ }
50
+ }
@@ -0,0 +1,39 @@
1
+ /* Dreamcast SPORTS demo — a top-down pong/air-hockey rally: two paddles track a
2
+ * bouncing ball with a scoreboard. Self-animating (the CPU "AI" plays both sides).
3
+ * Renders on the PowerVR2 framebuffer. */
4
+ #include "dc.h"
5
+
6
+ void main(void){
7
+ int frame = 0;
8
+ int bx = DC_W/2, by = DC_H/2, vx = 5, vy = 3;
9
+ int p0 = DC_H/2, p1 = DC_H/2; /* paddle centers (y) */
10
+ int s0 = 0, s1 = 0, i;
11
+ dc_video_init();
12
+ for (;;){
13
+ /* ball physics */
14
+ bx += vx; by += vy;
15
+ if (by < 12 || by > DC_H-12) vy = -vy;
16
+ /* paddles track the ball (simple AI) */
17
+ if (p0 < by) p0 += 4; else p0 -= 4;
18
+ if (p1 < by) p1 += 4; else p1 -= 4;
19
+ /* bounce off paddles / score */
20
+ if (bx < 40){ if (by > p0-50 && by < p0+50) vx = -vx; else { s1++; bx=DC_W/2; by=DC_H/2; vx=5; } }
21
+ if (bx > DC_W-40){ if (by > p1-50 && by < p1+50) vx = -vx; else { s0++; bx=DC_W/2; by=DC_H/2; vx=-5; } }
22
+
23
+ dc_clear(dc_rgb(15, 60, 30)); /* court green */
24
+ /* center line (dashed) */
25
+ for (i = 0; i < DC_H; i += 32) dc_rect(DC_W/2-2, i+8, 4, 16, dc_rgb(200,200,200));
26
+ /* top/bottom rails */
27
+ dc_rect(0, 0, DC_W, 8, dc_rgb(220,220,220));
28
+ dc_rect(0, DC_H-8, DC_W, 8, dc_rgb(220,220,220));
29
+ /* paddles */
30
+ dc_rect(24, p0-50, 14, 100, dc_rgb(80, 160, 255));
31
+ dc_rect(DC_W-38, p1-50, 14, 100, dc_rgb(255, 160, 80));
32
+ /* ball */
33
+ dc_rect(bx-8, by-8, 16, 16, dc_rgb(255, 240, 80));
34
+ /* scoreboard (score as stacked pips) */
35
+ for (i = 0; i < s0 && i < 9; i++) dc_rect(DC_W/2 - 60 - i*10, 16, 8, 8, dc_rgb(80,160,255));
36
+ for (i = 0; i < s1 && i < 9; i++) dc_rect(DC_W/2 + 56 + i*10, 16, 8, 8, dc_rgb(255,160,80));
37
+ frame++; (void)frame;
38
+ }
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "romdevtools",
3
- "version": "0.70.0",
3
+ "version": "0.71.1",
4
4
  "description": "Tool server giving coding agents full control of homebrew ROM development AND reverse-engineering/romhacking across 17 retro platforms (NES, SNES, GB, Genesis, Atari, C64, PC Engine, MSX, PlayStation, N64, Dreamcast, ...) via WASM toolchains + emulator cores. Use over plain HTTP, as an Agent Skill, or as an MCP server.",
5
5
  "type": "module",
6
6
  "main": "src/mcp/server.js",
@@ -58,9 +58,9 @@
58
58
  "romdev-core-geargrafx": "0.7.0",
59
59
  "romdev-core-gpgx": "0.12.0",
60
60
  "romdev-core-handy": "0.7.0",
61
- "romdev-core-flycast": "0.1.0",
61
+ "romdev-core-flycast": "0.1.1",
62
62
  "romdev-core-parallel-n64": "0.1.0",
63
- "romdev-core-beetle-psx-hw": "0.1.0",
63
+ "romdev-core-beetle-psx-hw": "0.1.1",
64
64
  "romdev-core-prosystem": "0.8.0",
65
65
  "romdev-core-vice": "0.9.0",
66
66
  "romdev-famitone": "0.1.0",
@@ -224,14 +224,15 @@ export const CAPABILITIES = {
224
224
  // beetle_psx_hw: the GPU renders on the REAL GPU via the GLES3/WebGL2 hardware
225
225
  // renderer through native-gles (like glide64-N64 + Flycast-DC), with OpenBIOS
226
226
  // EMBEDDED (PCSX-Redux, MIT, region-free) — no Sony firmware to ship, no BIOS file.
227
- // run/screenshot + cheats live; disasm + decompile work (MIPS Capstone + the
228
- // MIPS:LE:32 SLEIGH spec). cpuState/audioDebug are OFF: those need beetle-side
229
- // romdev_mips_regs_get/romdev_spu_get exports (a future core patch the old
230
- // pcsx_rearmed-software build had them, but it has no WebGL2 GPU path). build needs
231
- // a PS1 toolchain (PSn00bSDK, not yet).
227
+ // run/screenshot + cheats + cpuState + audioDebug live; disasm + decompile work
228
+ // (MIPS Capstone + the MIPS:LE:32 SLEIGH spec). cpuState (R3000A GPR_full/BACKED_PC)
229
+ // + audioDebug (SPU $1F801C00 register block) come from beetle-side
230
+ // romdev_mips_regs_get/romdev_spu_get exports patched into cpu.c (see
231
+ // scripts/patches/romdev-snippets/beetle-psx-regsnap.c). build needs a PS1
232
+ // toolchain (PSn00bSDK, not yet).
232
233
  build: true, run: true, screenshot: true,
233
234
  inspectSprites: false, inspectPalette: false, inspectBackground: false,
234
- renderingContext: false, cpuState: false, audioDebug: false,
235
+ renderingContext: false, cpuState: true, audioDebug: true,
235
236
  cart: false, disasm: true, decompile: true,
236
237
  },
237
238
  },
@@ -264,11 +265,14 @@ export const CAPABILITIES = {
264
265
  // code (run + memory introspection), and the PowerVR2 present-path works — flycast
265
266
  // renders to the GL FBO and the host reads it back (verified: a framebuffer-writing
266
267
  // program shows ~727k captured pixels). `build` lands with the sh-elf-gcc WASM
267
- // toolchain. The 3D renderer has no tile/sprite inspectors (N/A by hw).
268
- // disasm/decompile = SH-4 analysis slice (rizin `sh` + Ghidra SuperH4 SLEIGH).
268
+ // toolchain. cpuState (SH-4 Sh4cntx regs) + audioDebug (AICA 64-channel register
269
+ // window) come from romdev_sh4_regs_get/romdev_aica_get patched into the flycast
270
+ // libretro entry (see scripts/patches/romdev-snippets/flycast-debug.c). The 3D
271
+ // renderer has no tile/sprite inspectors (N/A by hw). disasm/decompile = SH-4
272
+ // analysis slice (rizin `sh` + Ghidra SuperH4 SLEIGH).
269
273
  build: true, run: true, screenshot: true,
270
274
  inspectSprites: false, inspectPalette: false, inspectBackground: false,
271
- renderingContext: false, cpuState: false, audioDebug: false,
275
+ renderingContext: false, cpuState: true, audioDebug: true,
272
276
  cart: false, disasm: true, decompile: true,
273
277
  },
274
278
  },