romdevtools 0.44.0 → 0.70.0

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 (71) hide show
  1. package/CHANGELOG.md +572 -0
  2. package/README.md +9 -7
  3. package/examples/dreamcast/hello/main.c +24 -0
  4. package/examples/n64/platformer/main.c +158 -0
  5. package/examples/n64/puzzle/main.c +117 -0
  6. package/examples/n64/racing/main.c +147 -0
  7. package/examples/n64/shmup/main.c +122 -0
  8. package/examples/n64/sports/main.c +127 -0
  9. package/examples/ps1/platformer/main.c +158 -0
  10. package/examples/ps1/puzzle/main.c +125 -0
  11. package/examples/ps1/racing/main.c +147 -0
  12. package/examples/ps1/shmup/main.c +192 -0
  13. package/examples/ps1/sports/main.c +127 -0
  14. package/examples/ps1/sprite_move/main.c +38 -0
  15. package/package.json +11 -2
  16. package/src/analysis/analyze.js +224 -29
  17. package/src/analysis/decompile.js +7 -0
  18. package/src/analysis/decompiler/sleigh/mips.ldefs +25 -0
  19. package/src/analysis/decompiler/sleigh/mips32.pspec +78 -0
  20. package/src/analysis/decompiler/sleigh/mips32be.cspec +107 -0
  21. package/src/analysis/decompiler/sleigh/mips32be.sla +211162 -0
  22. package/src/analysis/decompiler/sleigh/mips32le.cspec +106 -0
  23. package/src/analysis/decompiler/sleigh/mips32le.sla +210624 -0
  24. package/src/analysis/rizin.js +24 -1
  25. package/src/cores/capabilities.js +122 -2
  26. package/src/cores/registry.js +17 -0
  27. package/src/host/LibretroGL.js +273 -0
  28. package/src/host/LibretroGLBridge.js +836 -0
  29. package/src/host/LibretroHost.js +203 -3
  30. package/src/host/callbacks.js +22 -2
  31. package/src/host/coreLoader.js +65 -5
  32. package/src/host/cpu-state.js +27 -0
  33. package/src/host/framebuffer.js +14 -1
  34. package/src/host/glOptionalDep.js +60 -0
  35. package/src/host/n64-ai-state.js +43 -0
  36. package/src/host/ps1-spu-state.js +65 -0
  37. package/src/host/retroConstants.js +14 -0
  38. package/src/mcp/tools/disasm.js +2 -0
  39. package/src/mcp/tools/frame.js +18 -9
  40. package/src/mcp/tools/lifecycle.js +1 -1
  41. package/src/mcp/tools/platform-tools.js +20 -4
  42. package/src/mcp/tools/platforms.js +38 -4
  43. package/src/mcp/tools/project.js +116 -0
  44. package/src/mcp/tools/rendering-context.js +2 -1
  45. package/src/mcp/tools/toolchain.js +1 -1
  46. package/src/platforms/n64/lib/c/n64.c +196 -0
  47. package/src/platforms/n64/lib/c/n64.h +68 -0
  48. package/src/platforms/ps1/lib/c/psx.c +200 -0
  49. package/src/platforms/ps1/lib/c/psx.h +83 -0
  50. package/src/toolchains/index.js +65 -0
  51. package/src/toolchains/mips-c/lib/be/libc.a +0 -0
  52. package/src/toolchains/mips-c/lib/be/libgcc.a +0 -0
  53. package/src/toolchains/mips-c/lib/be/libm.a +0 -0
  54. package/src/toolchains/mips-c/lib/el/libc.a +0 -0
  55. package/src/toolchains/mips-c/lib/el/libm.a +0 -0
  56. package/src/toolchains/mips-c/lib/n64-crt0.s +21 -0
  57. package/src/toolchains/mips-c/lib/n64-ipl3.s +30 -0
  58. package/src/toolchains/mips-c/lib/n64.ld +15 -0
  59. package/src/toolchains/mips-c/lib/ps1-crt0.s +20 -0
  60. package/src/toolchains/mips-c/lib/ps1.ld +15 -0
  61. package/src/toolchains/mips-c/lib/softint.c +37 -0
  62. package/src/toolchains/mips-c/mips-c.js +155 -0
  63. package/src/toolchains/mips-elf-gcc/gcc.js +130 -0
  64. package/src/toolchains/sh-c/lib/dc-crt0.s +23 -0
  65. package/src/toolchains/sh-c/lib/dc.h +102 -0
  66. package/src/toolchains/sh-c/lib/dc.ld +11 -0
  67. package/src/toolchains/sh-c/lib/libc.a +0 -0
  68. package/src/toolchains/sh-c/lib/libgcc.a +0 -0
  69. package/src/toolchains/sh-c/lib/libm.a +0 -0
  70. package/src/toolchains/sh-c/sh-c.js +101 -0
  71. package/src/toolchains/sh-elf-gcc/gcc.js +122 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,578 @@ 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.70.0 — 2026-06-26
8
+
9
+ ### Dreamcast is ship-ready: verified build → run → screenshot, with a renderable example
10
+
11
+ The Dreamcast platform is now verified end-to-end and release-ready:
12
+
13
+ - **build → run → screenshot proven + regression-tested.** A C homebrew compiles via
14
+ sh-elf-gcc, boots DIRECTLY on Flycast's reios HLE BIOS (no GD-ROM image, no firmware),
15
+ and renders on the real GPU through native-gles at 640×480 — confirmed by the new
16
+ `test/dreamcast-runside.test.js`.
17
+ - **dc.h is now auto-bundled.** The PowerVR2 framebuffer helper (640×480 RGB565 bring-up:
18
+ FB_R_CTRL/SIZE/SOF1 + SPG) ships in the sh-c toolchain lib and is injected automatically,
19
+ so `#include "dc.h"` just works — a DC program is a single `main.c` with no glue. A
20
+ caller-supplied `dc.h` still overrides the bundled one.
21
+ - **A renderable example** — `examples/dreamcast/hello` (DCHELLO): the canonical starter
22
+ that paints a test pattern through the GPU pipeline. Registered with createProject /
23
+ scaffold like the other platforms.
24
+
25
+ (The 640×480 screenshot path was already correct — the GL FBO is sized to Flycast's
26
+ upscale bound and the host crops the readback to the core's reported geometry; a raw
27
+ readback without crop args was the only thing that looked oversized.)
28
+
29
+ ## 0.69.0 — 2026-06-26
30
+
31
+ ### PS1 renders on the REAL GPU via native-gles (Beetle PSX HW + OpenBIOS)
32
+
33
+ The PS1 core is now **Beetle PSX HW** (mednafen) with its GLES3/WebGL2 hardware renderer,
34
+ rendering the PS1 GPU on the real GPU through native-gles — the same path as glide64-N64
35
+ and Flycast-DC. It ships with **OpenBIOS embedded** (PCSX-Redux, MIT-licensed, region-free),
36
+ so there's NO copyrighted Sony firmware to ship and no BIOS file to supply. (Verified:
37
+ mod.GL exposed, SET_HW_RENDER fires, hwActive=true, the OpenBIOS 3D-cube boot animation
38
+ renders in shaded perspective through native-gles.)
39
+
40
+ - New package romdev-core-beetle-psx-hw (replaces romdev-core-pcsx-rearmed as the PS1
41
+ core); build-beetle-psx-hw.sh reuses the N64 GL recipe (all-.o link, -lGL +
42
+ GL_ENABLE_GET_PROC_ADDRESS + GL in EXPORTED_RUNTIME_METHODS, the libretro-common EXTRAS).
43
+ - Registry: ps1 → beetle_psx_hw, hwRender:true. Manifest: renderingKind 3d, run+screenshot
44
+ +disasm+decompile+build true; cpuState/audioDebug now false (beetle lacks the romdev
45
+ MIPS/SPU debug exports the old pcsx_rearmed-software build had — a future core patch;
46
+ pcsx had no WebGL2 GPU path).
47
+
48
+ NOTE (both MIPS platforms): the GL cores render RDP/GPU display lists, NOT raw CPU-written
49
+ framebuffers — so the romdev software-3D example libs (which rasterize into a framebuffer)
50
+ render black on them; REAL games with GPU geometry render correctly. SDK-based examples
51
+ (libdragon rdpq / PSn00bSDK GTE) are the renderable path. Suite 1059/1059.
52
+
53
+ ## 0.68.0 — 2026-06-26
54
+
55
+ ### N64 renders on the REAL GPU via native-gles (glide64), not software
56
+
57
+ parallel_n64 now renders the RDP through **glide64 → GLES2/WebGL2 → native-gles** (the
58
+ real GPU, headless EGL pbuffer), replacing the angrylion software-RDP build. SET_HW_RENDER
59
+ fires, the host's LibretroGL drives native-gles, and frames read back via glReadPixels —
60
+ the same GPU path as Flycast/Dreamcast. (Verified: hwActive=true, the GL context engages
61
+ from boot.)
62
+
63
+ The build + host fixes that made it work (every one was a real wall):
64
+ - **Link all .o directly**, not via a .bc/.a archive — the archive route drops the
65
+ GLSM/context_reset/glide64 objects (no externally-referenced symbol), so the core never
66
+ calls SET_HW_RENDER. (build-parallel-n64.sh)
67
+ - **`-lGL` + `GL_ENABLE_GET_PROC_ADDRESS=1` + `"GL"` in EXPORTED_RUNTIME_METHODS** — these
68
+ make Emscripten emit `Module["GL"]=GL`, so the returned module actually exposes the GL
69
+ context object the host needs. Without them, `mod.GL` is undefined and HW render never
70
+ initializes.
71
+ - **coreLoader: don't set `noInitialRun`/`wasmBinary` for GL (canvas) cores** — both
72
+ suppress the Emscripten GL runtime init; use `locateFile` so the .wasm loads the normal way.
73
+ - **Pre-seed PLATFORM_CORE_OPTIONS before `_retro_init`** (loadCore now infers the platform
74
+ from the core filename): the core picks its renderer (glide64 vs angrylion) from
75
+ `parallel-n64-gfxplugin` during retro_init, so the override must be set before then.
76
+ - Registry: n64 is now `hwRender:true` (glide64 GL).
77
+
78
+ ## 0.67.0 — 2026-06-26
79
+
80
+ ### Dreamcast examples + sh-c -O level fix
81
+
82
+ Four DC example programs (rom-games/dreamcast/): dchello (test card), bounce (animation),
83
+ starfield (shmup background), grid (puzzle/board) — all build via the WASM toolchain and
84
+ render distinct content through Flycast, exercising the dc.h helper (no-KOS PowerVR2
85
+ framebuffer + 2D primitives).
86
+
87
+ sh-c driver: a user-supplied `-O<level>` now wins over the default `-O2` (gcc honors the
88
+ LAST -O, so a default appended after the user's would clobber it — only add -O2 if absent).
89
+ This matters because the WASM cc1 hangs at -O2 on some sources (the interprocedural-
90
+ optimization phase; native sh-elf-gcc compiles them in <1s), and -O1 is the workaround.
91
+ Suite 1059/1059.
92
+
93
+ ## 0.66.0 — 2026-06-26
94
+
95
+ ### Dreamcast: build() works — full WASM toolchain + packaging
96
+
97
+ `build({platform:"dreamcast"})` now compiles SH-4 C to a bootable ELF entirely in WASM,
98
+ and the result boots + renders through Flycast — the complete zero-install pipeline
99
+ (build → run → screenshot), verified end-to-end via the public `buildForPlatform`.
100
+
101
+ - **sh-elf-gcc WASM toolchain** (cc1 + as + ld + objcopy + objdump): gcc 14.2.0 +
102
+ binutils 2.42 + newlib 4.4.0 for sh-elf (little-endian SH-4, m4-single-only). The cc1
103
+ build needed CC_FOR_BUILD forced native (emconfigure makes $(CC)=emcc, which would
104
+ build the gen tools as WASM) + the host-side libcpp/libiberty configured first.
105
+ - **sh-c build driver** + lib (dc-crt0.s zeroes .bss + calls main; dc.ld links at
106
+ 0x8c010000; newlib libc/libm/libgcc). The ELF IS the deliverable — reios boots it.
107
+ - **Packages:** romdev-core-flycast (the DC core) + romdev-toolchain-sh-gcc (the WASM
108
+ compiler), both added as romdevtools deps; the registry resolves the DC core from the
109
+ package. Manifest `dreamcast.build` is now true.
110
+
111
+ Dreamcast core parity reached: disasm + decompile + build + run + screenshot. Suite
112
+ 1059/1059.
113
+
114
+ ## 0.65.0 — 2026-06-26
115
+
116
+ ### Dreamcast: homebrew renders correct graphics + HW-frame crop-to-native
117
+
118
+ A minimal DC homebrew (no KallistiOS — a dependency-free `dc.h` helper that programs the
119
+ PowerVR2 FB_R_CTRL/FB_R_SIZE/FB_R_SOF1 + SPG for a 640x480 RGB565 framebuffer) builds with
120
+ the native sh-elf toolchain, boots via reios HLE, runs, and renders a test pattern that the
121
+ host captures **pixel-exact** (verified: dark-blue background + equal red/green/blue bars +
122
+ white frame, the program's exact colors). This closes the DC render-fidelity loop.
123
+
124
+ Host change (helps any HW-render core): the video_refresh callback now records the core's
125
+ reported active resolution, and the GL readback crops the FBO to it — so a 640x480 DC frame
126
+ no longer comes back as the full 853x853 GL viewport with a dead border. `readbackFrame`
127
+ takes optional (cropW, cropH); `_afterRun` passes the core's w/h. Suite 1059/1059.
128
+
129
+ ## 0.64.0 — 2026-06-26
130
+
131
+ ### Dreamcast: present-path verified — screenshot works
132
+
133
+ Flycast renders to the GL FBO and the host reads it back: a framebuffer-writing homebrew
134
+ program shows ~727k captured pixels through the host's normal frame path (hwFramePending →
135
+ readbackFrame). Manifest `dreamcast.screenshot` is now true. With the CPU executing
136
+ (0.63.0), the direct-framebuffer present path lights up.
137
+
138
+ Added `flycast_emulate_framebuffer: enabled` to the host's DC options — it scans the DC
139
+ framebuffer out on every VBlank (the 2D path, no PowerVR2 tile list), so simple homebrew
140
+ that writes RGB565 to VRAM presents reliably without authoring a full TA list. (Full
141
+ TA/3D fidelity + correct framebuffer addressing come with the KOS helper lib; the pipeline
142
+ itself — boot → run → render → host capture — is proven end-to-end.) Suite 1059/1059.
143
+
144
+ ## 0.63.0 — 2026-06-26
145
+
146
+ ### Dreamcast: HOMEBREW EXECUTES — run + memory introspection live
147
+
148
+ Custom Flycast core patches make the SH-4 actually execute guest code. A homebrew .elf
149
+ boots via reios HLE, the SH-4 runs it, and the guest's RAM writes are visible through the
150
+ host (verified: a 0xDC0DC0DC marker the test program writes appears in DC RAM). Manifest
151
+ `dreamcast.run` is now true.
152
+
153
+ Root cause (3 stacked bugs): (1) worker-thread aborts — fixed by single-thread +
154
+ `--wrap pthread_create` no-op (0.62.0); (2) HLE BIOS off — only the reios path loads a raw
155
+ .elf, and the option is latched at retro_init, so default `UseReios=true` in source for
156
+ emscripten; (3) THE BIG ONE — `ThreadedRendering` defaulted ON, which runs the CPU on a
157
+ `std::async` worker that our pthread no-op kills → the SH-4 never steps (PC stuck at the
158
+ reset vector, reios_boot never fires, RAM stays zero). Fixed by unconditionally
159
+ `config::ThreadedRendering.override(false)` for emscripten in update_variables.
160
+
161
+ `screenshot` stays false until the PowerVR2 present-path is verified with a TA-driving
162
+ program (the KOS helper lib — next phase). All patches reproducible in build-flycast.sh.
163
+ Suite 1059/1059.
164
+
165
+ ## 0.62.0 — 2026-06-26
166
+
167
+ ### Dreamcast run-side BREAKTHROUGH: Flycast boots + runs DC ELFs
168
+
169
+ The threading wall is solved. Flycast (the full DC emulator) now **boots a homebrew
170
+ .elf via reios HLE and runs frames** through the romdev host — `retro_init` /
171
+ `retro_load_game` / `retro_run` all succeed, av_info reports 640×480, and video_refresh
172
+ fires every frame. No abort, no `unwind`.
173
+
174
+ The fix (after trying PROXY_TO_PTHREAD + ASYNCIFY, which deadlocks in Node on the
175
+ GL↔proxy-thread dependency): build single-threaded and **`--wrap` pthread_create to a
176
+ no-op** (`scripts/patches/romdev-snippets/flycast-pthread-noop.c`). pthread_create
177
+ returns success but spawns nothing; join/detach are no-ops. So flycast's worker threads
178
+ (achievements/http/network/audio-async) never run, `std::thread`'s ctor doesn't abort
179
+ (no "thread constructor failed"), and — crucially — the main thread never blocks on a
180
+ worker, so there's no emscripten `unwind`. Emulation runs synchronously on retro_run
181
+ (ThreadedRendering defaulted false). This is far cleaner than stubbing thread sites
182
+ one-by-one (there were 4+ wrapper classes plus raw std::thread/std::async).
183
+
184
+ Also: the GL `get_proc_address` bridge fix (0.59.0) is what got context_reset past the
185
+ signature mismatch; the 512MB-mmap fallback (posix_vmem) got init past the trap.
186
+
187
+ NEXT: a DC program that actually renders via PowerVR2 (the KOS helper lib) to verify the
188
+ present path end-to-end, then flip run/screenshot true. Captured in build-flycast.sh.
189
+ Suite green.
190
+
191
+ ## 0.60.0 — 2026-06-26
192
+
193
+ ### Dreamcast: SuperH4 SLEIGH metadata shipped + Flycast threading progress
194
+
195
+ - **SuperH4 SLEIGH metadata committed** (`.ldefs`/`.pspec`/`.cspec`) — these ship
196
+ alongside the gitignored `.sla` (like every other CPU); without them the decompiler
197
+ couldn't load the SuperH4 spec at runtime. (0.57.0 shipped the wiring but missed the
198
+ metadata files. Now complete.)
199
+ - Flycast run-side threading: the single-threaded WASM build needs worker-thread
200
+ creation stubbed (emscripten can't spawn them without -pthread; with -pthread the
201
+ main thread can't block → unwind). Captured the patches in `build-flycast.sh`
202
+ (`cThread::Start` / `VPeriodicThread::start` no-op, `ThreadedRendering` defaults
203
+ false on emscripten) + the `flycast_threaded_rendering: disabled` host option.
204
+
205
+ KNOWN-OPEN: Flycast's load still aborts on a not-yet-located worker thread (no-pthread)
206
+ or unwinds (pthread). The thread/main-loop model integration is the remaining run-side
207
+ fight — see N64_PS1_LESSONS_FOR_DREAMCAST.md. DC analysis (disasm+decompile) is fully
208
+ shipped + tested. Suite 1059/1059.
209
+
210
+ ## 0.59.0 — 2026-06-26
211
+
212
+ ### Dreamcast run-side: Flycast loads ELFs + GL bridge fix (general)
213
+
214
+ Flycast WASM now boots a homebrew `.elf` (reios HLE — no BIOS/disc) and requests GL:
215
+ - threads: `-pthread -sPTHREAD_POOL_SIZE=8` (Flycast spawns std::threads; without
216
+ pthreads the ctor aborts). The pthread build bakes the module filename into the
217
+ worker bootstrap, so it MUST link with the final `flycast_libretro.js` name.
218
+ - **GL proc-address (general bridge fix in LibretroGL.js):** emscripten-WebGL cores
219
+ (Flycast) resolve GL via libretro `get_proc_address`. The bridge previously returned
220
+ a no-op 0-arg stub → the core called multi-arg GL fns through it → "null function or
221
+ function signature mismatch" in context_reset. Now the bridge returns the real
222
+ `emscripten_GetProcAddress` table pointer (built with `-sGL_ENABLE_GET_PROC_ADDRESS=1
223
+ -lGL`); the native-gles stub path stays as the glide64-N64 fallback. This fix helps
224
+ ANY future emscripten-WebGL core.
225
+ - `ROMDEV_CORE_LOG=1` env gates core stdout/stderr (was always suppressed) — found the
226
+ thread + mmap aborts with it.
227
+
228
+ KNOWN-OPEN: `retro_run` throws emscripten's `unwind` (Flycast yields via
229
+ emscripten_set_main_loop / Asyncify, incompatible with the host's synchronous
230
+ frame-step). That main-loop integration is the next run-side step. Suite 1059/1059.
231
+
232
+ ## 0.58.0 — 2026-06-26
233
+
234
+ ### Dreamcast run-side: Flycast WASM core builds + inits; sh-elf toolchain built
235
+
236
+ The two heaviest Dreamcast pieces, both landed (build infrastructure; host present-path
237
+ integration is the next step):
238
+
239
+ - **sh-elf-gcc toolchain** (SH-4): binutils 2.42 + gcc 14.2.0 + newlib 4.4.0, built
240
+ for `sh-elf` (m4-single-only, little-endian — the DC ABI). Compiles SH-4 C end to
241
+ end. `scripts/build-sh-toolchain.sh` (adapted from the mips one; SH-4 is single-
242
+ endian so no be/el split).
243
+ - **Flycast → WASM**: the full Dreamcast emulator (785 C++ files, GLES3/WebGL2)
244
+ compiled to a 5.9MB WASM module that instantiates with all libretro entry points
245
+ and `retro_init` succeeds. Flycast has no upstream emscripten build, so
246
+ `scripts/build-flycast.sh` applies the romdev WASM patches discovered here:
247
+ - a `CPU_GENERIC` host (no JIT) → the SH-4/ARM/DSP interpreters (`TARGET_NO_REC`);
248
+ CMake `DetectArchitecture` + `build.h` + the FPU-control / JIT-segfault-recovery
249
+ arch branches all get a generic no-op path.
250
+ - asio single-threaded (`ASIO_DISABLE_THREADS`) so it doesn't need POSIX
251
+ signal_blocker/tss_ptr (emscripten without -pthread isn't detected as POSIX).
252
+ - Vulkan OFF (WASM uses WebGL); networking/UPnP stubbed.
253
+ - **the key boot fix:** emscripten can't `mmap` a 512MB contiguous reservation (it
254
+ traps), so `posix_vmem::init` declines fast-vmem on emscripten → Flycast's
255
+ malloc-based memory fallback engages and `retro_init` succeeds.
256
+ - `dreamcast` registry entry (Flycast, hwRender — PowerVR2 is GPU-first, no software
257
+ framebuffer; HLE reios BIOS, no firmware to ship).
258
+
259
+ Suite 1059/1059. Next: the GL present-path (load_game + run + frame through the host's
260
+ WebGL2 bridge), then the helper lib + 5 example games + packaging.
261
+
262
+ ## 0.57.0 — 2026-06-26
263
+
264
+ ### Dreamcast (SH-4) analysis slice — disasm + decompile
265
+
266
+ First slice of Dreamcast support: the SH-4 (SuperH) reverse-engineering path, on the
267
+ same pattern as the PS1/N64 analysis slice.
268
+
269
+ - rizin's `sh` plugin (already in rizin.wasm) wired for disasm/cfg/xrefs/functions
270
+ (arch=sh, little-endian).
271
+ - Ghidra's **SuperH4** SLEIGH spec compiled (`SuperH4_le.sla`) + shipped in
272
+ romdev-analysis-decompiler → `decompile` produces real C (langid
273
+ `SuperH4:LE:32:default`). Verified: SH-4 bytes → disasm + decompiled C.
274
+ - analyze.js handles DC binaries: strips an ELF to its first PT_LOAD segment (vaddr =
275
+ loadBase 0x8c010000) or treats a raw image as flat; left-pads so flat offset ==
276
+ the VA's low bits + rebases the high bits (same trick PS1 needed for absolute-
277
+ addressed calls — SH-4's PC-relative + absolute addressing needs it too).
278
+ - `dreamcast` added to the capability manifest as the new `sh` tier (analysis-first;
279
+ run/build/etc. land in later phases). The 3D (PowerVR2) renderer's tile/sprite
280
+ inspectors are N/A by hardware with a stated reason. Excluded from the all-14
281
+ contract via the generalized NEXTGEN_TIER (mips + sh).
282
+
283
+ Suite 1059/1059. Run-side (Flycast WASM), build (sh-elf-gcc), the helper lib, and the
284
+ 5 example games are the next phases.
285
+
286
+ ## 0.56.1 — 2026-06-26
287
+
288
+ ### ascii screenshot: legible default grid + lighter default color (0.44.0 feedback #1)
289
+
290
+ `frame({op:'screenshot', format:'ascii'})` defaulted to a `fb/16` grid (16×14 for a
291
+ 256×224 NES frame — too coarse to read any game state) AND truecolor (`38;2;r;g;b`
292
+ per cell, ~7.9KB of escapes). So the "cheap text screenshot" was both expensive and
293
+ useless for an "are we in gameplay?" check.
294
+
295
+ - default grid is now `fb/8` (one cell per 8×8 tile → 32×28 for NES, legible).
296
+ - default `colors` is now `'256'` (indexed) instead of `'true'` — near-identical read,
297
+ far fewer escape bytes. Net for the NES case: 4× more cells AND ~55% smaller
298
+ (7876B → 3551B).
299
+ - when a caller forces a grid too coarse to show state, the result carries a `note`
300
+ pointing at `memory({op:'read'})` as the cheaper exact path for a pass/fail check.
301
+
302
+ ## 0.56.0 — 2026-06-26
303
+
304
+ ### N64 + PS1 are now SHIPPABLE: core packages + toolchain wired for publish
305
+
306
+ The functionality was done, but the artifacts weren't distributable — the cores lived
307
+ only in the gitignored dev-staging dir, with no npm package to publish. Fixed:
308
+
309
+ - **New `romdev-core-pcsx-rearmed`** (PS1) and **`romdev-core-parallel-n64`** (N64,
310
+ headless-angrylion software) packages, mirroring the 14's structure (package.json
311
+ + index.js + README + gitignored `wasm/` shipped via the `files` allowlist +
312
+ verify-wasm prepublish guard). These are CUSTOM romdev builds (the romdev debug
313
+ exports + IPL3) — documented in each README + built by scripts/build-*.sh.
314
+ - **`romdev-toolchain-mips-gcc`** moved to required `dependencies` (build needs it);
315
+ the two cores added to `dependencies` (always installed, like the 14). lockfile
316
+ regenerated — `npm ci` passes.
317
+ - publish-all.mjs now discovers all 3 (26 packages total).
318
+
319
+ ### A real shipping bug the npm-pack smoke test caught
320
+
321
+ The cores' Emscripten glue had a DEV SCRATCH wasm name baked in (`pcsx_vram.wasm`,
322
+ `pn64_sw.wasm` from `-o $SCRATCH/...`) instead of the published `*_libretro.wasm`.
323
+ The host always passes `wasmBinary` so it worked in dev — but any consumer loading
324
+ the factory directly got ENOENT. Re-linked both glues with the correct `-o` name (the
325
+ build scripts already used it; only my hand-links were wrong). A clean-install smoke
326
+ test (npm pack → install into a bare dir → resolve + instantiate + check exports) +
327
+ a packaging contract test now pin this so it can't silently regress.
328
+
329
+ Verified: clean-install smoke test passes, full build→load-from-package→run→render
330
+ pipeline works for both, npm ci + lint + 1054/1054 tests green. Ready for `npm publish`.
331
+
332
+ ## 0.55.0 — 2026-06-26
333
+
334
+ ### System manifests now call out the features a platform CAN'T have (and why)
335
+
336
+ A bare `inspectSprites: false` in the capability manifest is ambiguous — an agent
337
+ can't tell "N/A by hardware, permanent" from "a decoder we haven't built." For the
338
+ 14 tile-based systems this never bit (they support those ops); for the framebuffer
339
+ (PS1) / 3D (N64) systems, FOUR ops are false with no stated reason.
340
+
341
+ - New `naReason(platform, op)` in the capability manifest returns a HARDWARE-grounded
342
+ explanation, keyed on `renderingKind`: a framebuffer/3D renderer has no tile/
343
+ sprite-attribute/nametable/palette tables for the tile-era inspectors to read —
344
+ those ops are *meaningless on the hardware*, not merely absent. (PS1 `cart` →
345
+ "disc-based, no cartridge ROM".)
346
+ - `platform({op:'capabilities', platform})` now includes a `naReasons` map alongside
347
+ `ops`, so the manifest itself states what each platform can't do + why.
348
+ - The `unsupported()` signal from inspectSprites/inspectPalette/inspectBackground/
349
+ renderingContext now carries that hardware reason instead of a generic "no decoder
350
+ for this platform" — an agent gets "this is a 3D renderer, there are no sprite
351
+ tables" and won't retry or request a decoder that can't exist.
352
+ - Conformance test pins it: every false introspection op on ps1/n64 must carry a
353
+ hardware-grounded N/A reason. Suite 1054/1054.
354
+
355
+ ## 0.54.0 — 2026-06-26
356
+
357
+ ### Audit: two real N64/PS1 parity gaps found + fixed
358
+
359
+ A full per-tool audit (all 32 tools, live-probed on both platforms) surfaced two
360
+ genuine gaps where the platform was CAPABLE but the tool didn't deliver:
361
+
362
+ 1. **PS1 reverse-engineering returned bogus addresses.** `disasm`/`cfg`/`xrefs`/
363
+ `functions`/`decompile` on a PS1 PS-EXE found a single fake `fcn.00000000` at
364
+ file offset 0 (not the real 0x80010000+ VA), and `decompile` then threw "address
365
+ maps outside the image." Root cause: rizin ignores `-B` on a raw buffer, so the
366
+ stripped .text was analyzed flat from 0, and PS1's ABSOLUTE jal targets dangled →
367
+ no cross-function discovery. Fix: left-pad the .text so flat offset == the VA's
368
+ low 20 bits (jal-following now works) and add the high bits back as a rebase, so
369
+ every reported address is a real VA that round-trips. PS1 now finds all functions
370
+ (16 vs 1) at correct VAs, CFG/xrefs/decompile all resolve. (N64 was already fine.)
371
+
372
+ 2. **PS1 `video_ram` was claimed but empty.** The manifest lists `video_ram` for
373
+ every platform, but pcsx_rearmed never exposed the GPU VRAM. Added a
374
+ `romdev_vram_get` export (1024×512×16bpp) wired into `memory({region:'video_ram'})`
375
+ — PS1 GPU VRAM is now readable (verified: rendered pixels show up).
376
+
377
+ Everything else in the audit was already at parity or genuinely N/A-by-hardware
378
+ (the tile/sprite/nametable/palette inspectors need tables a framebuffer/3D renderer
379
+ doesn't have; `cart` is for cartridge ROMs). save/loadState, runUntil, frame-verify,
380
+ romPatch, and the agnostic art/snippet tools all work on both. Suite 1053/1053.
381
+
382
+ ## 0.53.0 — 2026-06-26
383
+
384
+ ### N64 + PS1 reach EXAMPLE parity: 5 full genre games each
385
+
386
+ Both new platforms now ship the same caliber of bundled, playable examples as the
387
+ other 14 (parity = "5 decent full games"), registered in project.js and forkable +
388
+ buildable through the normal createProject/build tool flow.
389
+
390
+ **PS1** (4 × 3D + 1 × 2D, on a software 3D engine + the GPU): shmup STARFALL,
391
+ racing POLE BENDER (a real receding 3D road), platformer BLOCK HOP, sports
392
+ SLAM COURT (3D pong/air-hockey), puzzle DROP GRID (2D — the right idiom for a grid).
393
+
394
+ **N64** (all 5 × 3D — the N64 was a 3D-first machine, so even the puzzle is a 3D
395
+ well of cubes): STARFALL 64 / POLE BENDER 64 / BLOCK HOP 64 / SLAM COURT 64 /
396
+ DROP GRID 64, on the same software-3D lib with a framebuffer backend the
397
+ headless-angrylion core scans out.
398
+
399
+ Every example builds + boots + renders, verified on the actual cores (frames
400
+ inspected). The N64 path required the three earlier breakthroughs (self-booting
401
+ .z64 via a clean IPL3, a headless-angrylion GL-free core, and the VI-scanout
402
+ software rasterizer). Suite 1052/1052.
403
+
404
+ **This completes N64 + PS1 parity** — every tool the other 14 have works, and both
405
+ ship 5 full idiomatic examples.
406
+
407
+ ## 0.51.0 — 2026-06-26
408
+
409
+ ### N64 audioDebug — FULL parity (zero functional gaps)
410
+
411
+ - **`getAudioState({chip:'ai'})`** decodes the N64 Audio Interface: sample rate
412
+ (from DACRATE + the VI clock), whether audio is playing (a buffer is DMA-queued),
413
+ the DMA source address. (N64 audio is RSP-mixed, so the AI is the OUTPUT state, not
414
+ per-voice — that lives in game-specific RSP audio lists in RDRAM.) Verified: a real
415
+ ROM reports `playing:true, sampleRate:32006`. From a `romdev_ai_get` export added
416
+ to parallel_n64. n64 `audioChips:['ai']`, `audioDebug:true`.
417
+
418
+ **This closes the last functional gap.** A programmatic audit confirms N64/PS1 now
419
+ match the canonical 14 on every applicable op — build, run, screenshot, memory,
420
+ cpuState, audioDebug, disasm, decompile, cheats, breakpoint, watch. The only manifest
421
+ differences are N/A BY HARDWARE: the tile/sprite/nametable/palette inspectors
422
+ (framebuffer/3D renderers have no such tables) and `cart` (disc-based). Suite 1052/1052.
423
+
424
+ ## 0.50.0 — 2026-06-26
425
+
426
+ ### PS1 SPU audioDebug
427
+
428
+ - **`getAudioState({chip:'spu'})`** decodes the PS1 SPU's 24 ADPCM voices (per-voice
429
+ volume L/R, pitch→Hz, ADSR, key-on/off + main volume + control), from a
430
+ `romdev_spu_get` export added to pcsx_rearmed (copies the SPU regArea). Verified:
431
+ a toolchain-built program's SPU register writes read back correctly.
432
+ - ps1 `audioDebug:true` in the manifest.
433
+
434
+ **Parity status (N64/PS1 vs the canonical 14):** at parity on build, run, screenshot,
435
+ memory, cpuState, disasm, decompile, cheats, breakpoint, watch (+ PS1 audioDebug).
436
+ Genuinely N/A by hardware: the tile/sprite/nametable/palette inspectors (framebuffer/3D
437
+ renderers have no such tables) and `cart` (disc-based). **Remaining real TODO:** N64
438
+ audioDebug (the RSP/AI audio path — a deeper decode than the PS1 SPU register block).
439
+
440
+ ## 0.49.0 — 2026-06-26
441
+
442
+ ### N64 + PS1 live-debug — breakpoint + watch now work
443
+
444
+ The remaining big parity gap: the live-debugging RE tools. parallel_n64 (R4300) and
445
+ pcsx_rearmed (R3000) are rebuilt with romdev's CPU instrumentation, so:
446
+
447
+ - **`breakpoint({on:'pc'|'write'|'read'})`** + **`watch({on:'range'|'mem'})`** work on
448
+ both MIPS platforms — verified live: a C program (built by the romdev toolchain)
449
+ writing to a known address is caught by a write watchpoint with the writing PC, and
450
+ a range watch captures the write stream during emulation.
451
+ - A `romdev_debug.c` instrumentation unit (per core) hooks the memory write/read paths
452
+ (write/read watch + range watch + coverage) and the interpreter step (PC break +
453
+ single-step). Exports the full `romdev_watchpoint`/`readwatch`/`pcbreak`/`range`/`cov`/
454
+ `regsnap`/`watchdog` set the host already expects — so the host needed ZERO changes
455
+ (its debug methods were already generic).
456
+ - Reproducible via the updated build-pcsx-rearmed.sh / build-parallel-n64.sh
457
+ (patch the source + hooks + exports).
458
+
459
+ **Remaining honest gap:** `audioDebug` (PS1 SPU / N64 audio chip decoders) is still
460
+ TODO. The tile/sprite/nametable inspectors stay N/A by hardware (framebuffer/3D
461
+ renderers have no such tables). Everything else is at parity. Suite 1050/1050.
462
+
463
+ ## 0.48.0 — 2026-06-25
464
+
465
+ ### N64 + PS1 `build` op — full feature parity
466
+
467
+ The last missing op. N64 and PS1 now **build** C from source, completing parity
468
+ with the 14 (every op: build, run, screenshot, memory, disasm, decompile, cheats,
469
+ cpuState).
470
+
471
+ - **A from-scratch `mips-elf` GCC toolchain compiled to WASM** — binutils 2.42 +
472
+ gcc 14.2.0 + newlib 4.4.0, the same two-stage pipeline as the Genesis m68k
473
+ toolchain. STAGE 1 builds it natively; STAGE 2 re-compiles cc1/as/ld/objcopy/
474
+ objdump to WASM. One toolchain serves both endiannesses (cc1 `-mel`/`-meb`,
475
+ as/ld `-EL`/`-EB`).
476
+ - **`buildSource({platform:'ps1'|'n64', language:'c'})`** — cc1 → as → ld → objcopy,
477
+ links a minimal crt0 + libc/libm/libgcc. PS1 wraps the image in a PS-EXE the HLE
478
+ BIOS loads; N64 emits a big-endian flat image. **Verified end-to-end:** a C program
479
+ compiled by the toolchain boots and renders on the PS1 core (GPU fill executed).
480
+ - Four GCC-15/emscripten build incompatibilities fixed along the way (all in the
481
+ build scripts): binutils `static_assert`-as-identifier (`-std=gnu11`), newlib
482
+ libgloss pre-C23 idioms (permissive target CFLAGS), libiberty `psignal` conflict
483
+ (source patch, both gcc + binutils copies), and the `all-gcc`/`all` aux-tool
484
+ targets pulling in ftw/gprofng (build the specific `cc1`/`as`/`ld` targets).
485
+ - New `romdev-toolchain-mips-gcc` package (the WASM tools); pinned + reproducible
486
+ via `build-mips-toolchain.sh` + `build-mips-wasm-tools.sh`.
487
+
488
+ **Caveats:** the bare build path has no SDK yet — PS1 is "drive the GPU/SPU
489
+ registers yourself" (PSn00bSDK forthcoming); N64 compiles+links but a self-booting
490
+ cart needs IPL3 + a libdragon header (libdragon forthcoming). Both are logic/RE-
491
+ complete and the foundation for the SDKs. The framebuffer/3D renderers have no
492
+ tile/sprite inspectors by nature.
493
+
494
+ ## 0.47.0 — 2026-06-25
495
+
496
+ ### N64 + PS1 reach feature parity (cheats + cpuState + decompile)
497
+
498
+ The MIPS tier now matches the 14 on everything except `build`:
499
+
500
+ - **MIPS decompile** — `disasm({platform:'n64'|'ps1', target:'decompile'})` produces
501
+ real Ghidra C pseudocode. **No Python**: the stock Ghidra MIPS SLEIGH spec is
502
+ compiled to `.sla` (MIPS:BE:32 for N64, MIPS:LE:32 for PS1) by the same
503
+ `sleigh_opt` that builds the other 8 CPU tables. (m2c/Pyodide was rejected — it
504
+ would drag a CPython runtime into the tool.)
505
+ - **cpuState** — `cpu({op:'read'})` decodes the live R4300 (N64) / R3000 (PS1)
506
+ register file: 32 GPRs by o32 ABI name + lo/hi + PC. Backed by a tiny
507
+ `romdev_mips_regs_get` appended to each core's `libretro.c` (the new
508
+ `build-parallel-n64.sh` / `build-pcsx-rearmed.sh` recipes).
509
+ - **cheats** — `_retro_cheat_set`/`_retro_cheat_reset` are now exported from the
510
+ rebuilt cores (they were in the upstream C, just unexported).
511
+ - The cores are reproducible: pinned in `versions.json`, built by the new recipes
512
+ (clone → append the regsnap → emcc with the cheat+regsnap exports; the regsnap is
513
+ `EMSCRIPTEN_KEEPALIVE` so LTO doesn't strip it).
514
+
515
+ **Parity scorecard** — N64 & PS1 now have: run, screenshot, memory r/w, disasm
516
+ (cfg/xrefs/functions), decompile, cheats, cpuState. The one remaining op is
517
+ **build** (a MIPS GCC→WASM toolchain — PSn00bSDK/libdragon — the largest separate
518
+ piece). The framebuffer/3D renderers have no tile/sprite/nametable inspectors by
519
+ nature.
520
+
521
+ ## 0.46.0 — 2026-06-25
522
+
523
+ ### N64 + PS1 run-side: boot, run, render (the MIPS tier goes live)
524
+
525
+ Past analysis-only — N64 and PS1 now **boot, run, and present real frames** through
526
+ the host. The 32-bit MIPS tier is a new partial tier (`tier:"mips"`), distinct from
527
+ the canonical 14.
528
+
529
+ - **N64** (ParaLLEl-N64, HW 3D render) — `run` + `frame({op:'screenshot'})` produce
530
+ real 3D frames headlessly via a GL framebuffer readback. Rendering goes through a
531
+ ported GL bridge (`LibretroGL`/`LibretroGLBridge`) backed by `native-gles` +
532
+ `webgl-node`, which are **optionalDependencies**: lazy-loaded only when an N64/PS1
533
+ core boots, so the 14 software platforms and headless installs without the GPU
534
+ module are completely unaffected (clear install hint if absent).
535
+ - **PS1** (PCSX-ReARMed, software + **built-in HLE BIOS**) — `run` + screenshot with
536
+ zero firmware to ship, no GL dependency. Loads PS-EXE (and disc images).
537
+ - **Memory** works for both: `readMemory`/`writeMemory` on `system_ram` (N64 = 8MB
538
+ RDRAM, PS1 = 2MB main RAM) — poke values directly.
539
+ - **Static RE** (from 0.45.0) still works: `disasm({platform:'n64'|'ps1',
540
+ target:'rom'|'functions'|'cfg'|'xrefs'})`.
541
+ - N64 `.v64`/`.n64` byte orders auto-normalize to `.z64`; PS-EXE headers are stripped.
542
+
543
+ Three GL-binding bugs fixed to get frames on screen: the Emscripten `getContext`
544
+ shim's `instanceof WebGLRenderingContext` check (needs WebGL1 to be a distinct
545
+ class), the signed-vs-unsigned `RETRO_HW_FRAME_BUFFER_VALID` compare, and the
546
+ all-transparent FBO alpha (forced opaque in a new RGBA decode path).
547
+
548
+ **Still pending** for full parity (honestly off in the manifest): `build` (needs a
549
+ MIPS toolchain — PSn00bSDK/libdragon), `decompile` (needs a MIPS SLEIGH spec in the
550
+ decompiler — slaspec sources exist, a decompiler-package rebuild), `getCPUState` +
551
+ `cheats` (need a core rebuild exposing those exports). The framebuffer/3D renderers
552
+ have no tile/sprite/nametable inspectors by nature.
553
+
554
+ ## 0.45.0 — 2026-06-25
555
+
556
+ ### PS1 + N64 analysis-first (the 32-bit MIPS tier)
557
+
558
+ The first step past the GBA-era line: static RE for PlayStation (R3000) and
559
+ Nintendo 64 (R4300), via the MIPS plugin already in the shipped `rizin.wasm` — no
560
+ new core, no toolchain, no GPU bridge in this slice.
561
+
562
+ - **`disasm({platform:'ps1'|'n64', target:'functions'|'cfg'|'xrefs'|'rom'})`** —
563
+ works on real ROMs. Verified: a libdragon N64 homebrew (`FlappyBird.z64`)
564
+ recovers 251 functions with control-flow graphs and cross-references. The raw
565
+ N64 ROM has no entry `aaa` recognizes, so MIPS analysis is seeded at the
566
+ post-IPL3 code start (`af` + `aac`) — `aaa` finds 0, the seed finds the tree.
567
+ - **Endianness is wired right:** PS1 is little-endian, N64 is big-endian (same
568
+ `mips` arch). N64 `.v64`/`.n64` dumps are auto-normalized to `.z64` byte order;
569
+ PS1 PS-EXE headers are stripped to the load address.
570
+ - **Capability manifest:** `ps1` (framebuffer) / `n64` (3d) are an analysis-only
571
+ tier — `disasm` true; run-side ops (build/run/screenshot/the tile/sprite
572
+ inspectors) and `decompile` false. The tile/nametable inspectors are meaningless
573
+ on a framebuffer/3D renderer, so an agent gets the clean `unsupported()` signal.
574
+ - **`decompile` (C pseudocode) is NOT available yet** for MIPS — the rz-ghidra
575
+ decompiler ships no MIPS SLEIGH spec (adding `MIPS.sla` is a later
576
+ `romdev-analysis-decompiler` rebuild). It returns a clear steer to the working
577
+ disasm targets, not a cryptic failure.
578
+
7
579
  ## 0.44.0 — 2026-06-25
8
580
 
9
581
  ### v0.41.0 feedback (part 2) — RE-session ergonomics
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # romdev
2
2
 
3
- The entry point for **romdev** — vibe-code real retro games. Build, run, inspect, and reverse-engineer actual homebrew ROMs (NES, SNES, Game Boy, Genesis, Atari, C64, GBA, and more) with one command — drive it yourself or let a coding assistant do it.
3
+ The entry point for **romdev** — vibe-code real retro games. Build, run, inspect, and reverse-engineer actual homebrew ROMs (NES, SNES, Game Boy, Genesis, Atari, C64, GBA, PC Engine, MSX — and the 3D consoles N64, PlayStation, and Dreamcast) with one command — drive it yourself or let a coding assistant do it.
4
4
 
5
5
  ```bash
6
6
  npx romdevtools
@@ -9,7 +9,7 @@ npx romdevtools
9
9
  **What you get:**
10
10
 
11
11
  - **Build** — bundled per-platform toolchains (cc65, SDCC, RGBDS, asar, vasm, SGDK, PVSnesLib, libtonc, …) as WASM. Write source, compile, get a real ROM.
12
- - **Run + see + drive** — load the ROM into an emulated console (libretro cores as WASM), step frames, screenshot, script controller input.
12
+ - **Run + see + drive** — load the ROM into an emulated console (libretro cores as WASM), step frames, screenshot, script controller input. The 2D consoles render in software; the 3D consoles (N64 via glide64, PlayStation via Beetle PSX HW, Dreamcast via Flycast) render on the **real GPU** through [`native-gles`](https://github.com/monteslu/native-gles) — headless OpenGL/EGL, no browser. PlayStation ships [OpenBIOS](https://github.com/grumpycoders/pcsx-redux) (MIT) embedded, so there's no proprietary firmware to supply.
13
13
  - **Inspect + romhack** — read CPU/video/save RAM, watch memory, write-breakpoints, the Cheat-Engine value-search loop, a bundled cheat database, mapper-aware disassembly, and a byte-exact rebuildable-project disassembler.
14
14
  - **Reverse-engineering analysis engine (all 14 platforms)** — control-flow graphs, deep cross-references, auto-detected functions (ranked real-code-first), a one-shot structural map, and a Ghidra **decompiler** (C-like pseudocode, with hardware registers named and 6502 SLEIGH clutter folded to readable C): `disasm({target:'cfg'|'xrefs'|'functions'|'decompile'})` and `symbols({op:'analyze'})`. And the piece no static tool has: **live computed-jumptable recovery** — `breakpoint({on:'jumptable'})` runs the emulator to resolve the `JMP (table,X)` / RTS-trick dispatchers (state machines, script/battle VMs) that static analysis collapses to "could not recover." Understand *how* a routine works before you touch it — no $3,000 IDA license, no install.
15
15
  - **Convert assets** — PNG → platform tiles/tilemaps, quantize-to-palette, audio importers (BRR for SNES, XGM2 PCM for Genesis).
@@ -20,7 +20,7 @@ Point any coding agent at it three ways:
20
20
  - **Agent Skill** — `GET /skills/romdev/SKILL.md` (the [Agent Skills](https://agentskills.io) standard; save it to your skills dir as `skills/romdev/SKILL.md`; ~100 tokens until invoked).
21
21
  - **MCP** — it's also a [Model Context Protocol](https://modelcontextprotocol.io/) server at `/mcp` for clients that want it.
22
22
 
23
- This package contains all the JavaScript — the tool surface, the WASM emulator host, the per-platform example games, runtime/library source, and debug helpers — but **no emulator or compiler WASM itself.** Those ship in the `romdev-*` binary packages it depends on, loaded on demand the first time you build or run a given platform.
23
+ This package contains all the JavaScript — the tool surface, the WASM emulator host, the per-platform example games, runtime/library source, and debug helpers — but **no emulator or compiler WASM itself.** Those ship in the `romdev-*` binary packages it depends on; each platform's core/toolchain WASM is resolved (`import.meta.resolve`) and instantiated only the first time you build or run that platform, so memory stays proportional to what you actually use.
24
24
 
25
25
  > For the full project — what romdev is, the supported-platform matrix, how the pieces fit together, and how to develop on it — see the [repository README](https://github.com/monteslu/romdev#readme).
26
26
 
@@ -34,11 +34,13 @@ This package contains all the JavaScript — the tool surface, the WASM emulator
34
34
 
35
35
  ## Dependencies
36
36
 
37
- `romdevtools` hard-depends (exact-pinned) on the binary/data packages it needs, so a single install gets a matched, tested set:
37
+ `romdevtools` depends on the binary/data packages it needs (exact-pinned), so a single install gets a matched, tested set:
38
38
 
39
- - Cores: `romdev-core-{fceumm,gambatte,gpgx,vice,handy,prosystem,geargrafx,bluemsx}`
40
- - Platforms: `romdev-platform-{snes,gba,atari2600}`
41
- - Toolchains: `romdev-toolchain-{cc65,sdcc,m68k-gcc,vasm,rgbds}`
39
+ - 2D cores: `romdev-core-{fceumm,gambatte,gpgx,vice,handy,prosystem,geargrafx,bluemsx}`
40
+ - 3D / GPU cores (rendered through `native-gles`): `romdev-core-{parallel-n64,beetle-psx-hw,flycast}`
41
+ - Platforms (core + dedicated toolchain bundled): `romdev-platform-{snes,gba,atari2600}`
42
+ - Toolchains: `romdev-toolchain-{cc65,sdcc,m68k-gcc,vasm,rgbds,mips-gcc,sh-gcc}` (mips-gcc = N64/PS1 C; sh-gcc = Dreamcast C)
43
+ - GPU deps (OPTIONAL — only the 3D cores need them; the 2D cores never touch GL): `native-gles` + `webgl-node`. A headless user without a GPU stack can still run all the 2D platforms.
42
44
  - Analysis: `romdev-analysis` (Rizin → WASM: control-flow graphs, cross-references, function detection) and `romdev-analysis-decompiler` (Ghidra's C++ decompiler → WASM + SLEIGH processor specs for all 14 CPUs). Power `disasm({target:'cfg'|'xrefs'|'functions'|'decompile'})` and `symbols({op:'analyze'})`. Lazy-loaded on first use.
43
45
  - Data: `romdev_game_codes` — the bundled game-code / cheat database (a free labeled RAM/code map for thousands of known ROMs), split out so it can grow independently. Lazy-loaded one platform at a time.
44
46
 
@@ -0,0 +1,24 @@
1
+ /* dchello — a minimal Dreamcast homebrew that draws a recognizable test pattern,
2
+ * proving the romdev DC pipeline renders program-controlled graphics end-to-end. */
3
+ #include "dc.h"
4
+
5
+ void main(void) {
6
+ dc_video_init();
7
+
8
+ /* Clear to a dark blue background. */
9
+ dc_clear(dc_rgb(16, 24, 64));
10
+
11
+ /* Three solid bars (red, green, blue) — distinct, easy to verify in a screenshot. */
12
+ dc_rect(64, 80, 160, 320, dc_rgb(220, 40, 40)); /* red */
13
+ dc_rect(240, 80, 160, 320, dc_rgb(40, 200, 60)); /* green */
14
+ dc_rect(416, 80, 160, 320, dc_rgb(50, 90, 230)); /* blue */
15
+
16
+ /* A white frame around the screen. */
17
+ dc_rect(0, 0, DC_W, 4, dc_rgb(255, 255, 255));
18
+ dc_rect(0, DC_H - 4, DC_W, 4, dc_rgb(255, 255, 255));
19
+ dc_rect(0, 0, 4, DC_H, dc_rgb(255, 255, 255));
20
+ dc_rect(DC_W - 4, 0, 4, DC_H, dc_rgb(255, 255, 255));
21
+
22
+ /* Hold the frame. */
23
+ for (;;) { }
24
+ }