romdevtools 0.43.0 → 0.56.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.
- package/CHANGELOG.md +322 -0
- package/examples/n64/platformer/main.c +158 -0
- package/examples/n64/puzzle/main.c +117 -0
- package/examples/n64/racing/main.c +147 -0
- package/examples/n64/shmup/main.c +122 -0
- package/examples/n64/sports/main.c +127 -0
- package/examples/ps1/platformer/main.c +158 -0
- package/examples/ps1/puzzle/main.c +125 -0
- package/examples/ps1/racing/main.c +147 -0
- package/examples/ps1/shmup/main.c +192 -0
- package/examples/ps1/sports/main.c +127 -0
- package/examples/ps1/sprite_move/main.c +38 -0
- package/package.json +9 -2
- package/src/analysis/analyze.js +169 -29
- package/src/analysis/decompile.js +4 -0
- package/src/analysis/decompiler/sleigh/mips.ldefs +25 -0
- package/src/analysis/decompiler/sleigh/mips32.pspec +78 -0
- package/src/analysis/decompiler/sleigh/mips32be.cspec +107 -0
- package/src/analysis/decompiler/sleigh/mips32be.sla +211162 -0
- package/src/analysis/decompiler/sleigh/mips32le.cspec +106 -0
- package/src/analysis/decompiler/sleigh/mips32le.sla +210624 -0
- package/src/analysis/rizin.js +22 -1
- package/src/cores/capabilities.js +90 -2
- package/src/cores/registry.js +12 -0
- package/src/host/LibretroGL.js +270 -0
- package/src/host/LibretroGLBridge.js +836 -0
- package/src/host/LibretroHost.js +144 -3
- package/src/host/callbacks.js +18 -2
- package/src/host/coreLoader.js +49 -2
- package/src/host/cpu-state.js +27 -0
- package/src/host/framebuffer.js +14 -1
- package/src/host/glOptionalDep.js +60 -0
- package/src/host/n64-ai-state.js +43 -0
- package/src/host/ps1-spu-state.js +65 -0
- package/src/host/retroConstants.js +14 -0
- package/src/mcp/tools/cheats.js +73 -4
- package/src/mcp/tools/disasm.js +2 -0
- package/src/mcp/tools/frame.js +18 -9
- package/src/mcp/tools/index.js +12 -2
- package/src/mcp/tools/lifecycle.js +1 -1
- package/src/mcp/tools/platform-tools.js +20 -4
- package/src/mcp/tools/platforms.js +38 -4
- package/src/mcp/tools/project.js +100 -0
- package/src/mcp/tools/rendering-context.js +2 -1
- package/src/mcp/tools/toolchain.js +1 -1
- package/src/mcp/tools/watch-memory.js +93 -20
- package/src/platforms/n64/lib/c/n64.c +196 -0
- package/src/platforms/n64/lib/c/n64.h +68 -0
- package/src/platforms/ps1/lib/c/psx.c +200 -0
- package/src/platforms/ps1/lib/c/psx.h +83 -0
- package/src/toolchains/cc65/cc65.js +11 -0
- package/src/toolchains/index.js +35 -0
- package/src/toolchains/mips-c/lib/be/libc.a +0 -0
- package/src/toolchains/mips-c/lib/be/libgcc.a +0 -0
- package/src/toolchains/mips-c/lib/be/libm.a +0 -0
- package/src/toolchains/mips-c/lib/el/libc.a +0 -0
- package/src/toolchains/mips-c/lib/el/libm.a +0 -0
- package/src/toolchains/mips-c/lib/n64-crt0.s +21 -0
- package/src/toolchains/mips-c/lib/n64-ipl3.s +30 -0
- package/src/toolchains/mips-c/lib/n64.ld +15 -0
- package/src/toolchains/mips-c/lib/ps1-crt0.s +20 -0
- package/src/toolchains/mips-c/lib/ps1.ld +15 -0
- package/src/toolchains/mips-c/lib/softint.c +37 -0
- package/src/toolchains/mips-c/mips-c.js +155 -0
- package/src/toolchains/mips-elf-gcc/gcc.js +130 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,328 @@ 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.56.1 — 2026-06-26
|
|
8
|
+
|
|
9
|
+
### ascii screenshot: legible default grid + lighter default color (0.44.0 feedback #1)
|
|
10
|
+
|
|
11
|
+
`frame({op:'screenshot', format:'ascii'})` defaulted to a `fb/16` grid (16×14 for a
|
|
12
|
+
256×224 NES frame — too coarse to read any game state) AND truecolor (`38;2;r;g;b`
|
|
13
|
+
per cell, ~7.9KB of escapes). So the "cheap text screenshot" was both expensive and
|
|
14
|
+
useless for an "are we in gameplay?" check.
|
|
15
|
+
|
|
16
|
+
- default grid is now `fb/8` (one cell per 8×8 tile → 32×28 for NES, legible).
|
|
17
|
+
- default `colors` is now `'256'` (indexed) instead of `'true'` — near-identical read,
|
|
18
|
+
far fewer escape bytes. Net for the NES case: 4× more cells AND ~55% smaller
|
|
19
|
+
(7876B → 3551B).
|
|
20
|
+
- when a caller forces a grid too coarse to show state, the result carries a `note`
|
|
21
|
+
pointing at `memory({op:'read'})` as the cheaper exact path for a pass/fail check.
|
|
22
|
+
|
|
23
|
+
## 0.56.0 — 2026-06-26
|
|
24
|
+
|
|
25
|
+
### N64 + PS1 are now SHIPPABLE: core packages + toolchain wired for publish
|
|
26
|
+
|
|
27
|
+
The functionality was done, but the artifacts weren't distributable — the cores lived
|
|
28
|
+
only in the gitignored dev-staging dir, with no npm package to publish. Fixed:
|
|
29
|
+
|
|
30
|
+
- **New `romdev-core-pcsx-rearmed`** (PS1) and **`romdev-core-parallel-n64`** (N64,
|
|
31
|
+
headless-angrylion software) packages, mirroring the 14's structure (package.json
|
|
32
|
+
+ index.js + README + gitignored `wasm/` shipped via the `files` allowlist +
|
|
33
|
+
verify-wasm prepublish guard). These are CUSTOM romdev builds (the romdev debug
|
|
34
|
+
exports + IPL3) — documented in each README + built by scripts/build-*.sh.
|
|
35
|
+
- **`romdev-toolchain-mips-gcc`** moved to required `dependencies` (build needs it);
|
|
36
|
+
the two cores added to `dependencies` (always installed, like the 14). lockfile
|
|
37
|
+
regenerated — `npm ci` passes.
|
|
38
|
+
- publish-all.mjs now discovers all 3 (26 packages total).
|
|
39
|
+
|
|
40
|
+
### A real shipping bug the npm-pack smoke test caught
|
|
41
|
+
|
|
42
|
+
The cores' Emscripten glue had a DEV SCRATCH wasm name baked in (`pcsx_vram.wasm`,
|
|
43
|
+
`pn64_sw.wasm` from `-o $SCRATCH/...`) instead of the published `*_libretro.wasm`.
|
|
44
|
+
The host always passes `wasmBinary` so it worked in dev — but any consumer loading
|
|
45
|
+
the factory directly got ENOENT. Re-linked both glues with the correct `-o` name (the
|
|
46
|
+
build scripts already used it; only my hand-links were wrong). A clean-install smoke
|
|
47
|
+
test (npm pack → install into a bare dir → resolve + instantiate + check exports) +
|
|
48
|
+
a packaging contract test now pin this so it can't silently regress.
|
|
49
|
+
|
|
50
|
+
Verified: clean-install smoke test passes, full build→load-from-package→run→render
|
|
51
|
+
pipeline works for both, npm ci + lint + 1054/1054 tests green. Ready for `npm publish`.
|
|
52
|
+
|
|
53
|
+
## 0.55.0 — 2026-06-26
|
|
54
|
+
|
|
55
|
+
### System manifests now call out the features a platform CAN'T have (and why)
|
|
56
|
+
|
|
57
|
+
A bare `inspectSprites: false` in the capability manifest is ambiguous — an agent
|
|
58
|
+
can't tell "N/A by hardware, permanent" from "a decoder we haven't built." For the
|
|
59
|
+
14 tile-based systems this never bit (they support those ops); for the framebuffer
|
|
60
|
+
(PS1) / 3D (N64) systems, FOUR ops are false with no stated reason.
|
|
61
|
+
|
|
62
|
+
- New `naReason(platform, op)` in the capability manifest returns a HARDWARE-grounded
|
|
63
|
+
explanation, keyed on `renderingKind`: a framebuffer/3D renderer has no tile/
|
|
64
|
+
sprite-attribute/nametable/palette tables for the tile-era inspectors to read —
|
|
65
|
+
those ops are *meaningless on the hardware*, not merely absent. (PS1 `cart` →
|
|
66
|
+
"disc-based, no cartridge ROM".)
|
|
67
|
+
- `platform({op:'capabilities', platform})` now includes a `naReasons` map alongside
|
|
68
|
+
`ops`, so the manifest itself states what each platform can't do + why.
|
|
69
|
+
- The `unsupported()` signal from inspectSprites/inspectPalette/inspectBackground/
|
|
70
|
+
renderingContext now carries that hardware reason instead of a generic "no decoder
|
|
71
|
+
for this platform" — an agent gets "this is a 3D renderer, there are no sprite
|
|
72
|
+
tables" and won't retry or request a decoder that can't exist.
|
|
73
|
+
- Conformance test pins it: every false introspection op on ps1/n64 must carry a
|
|
74
|
+
hardware-grounded N/A reason. Suite 1054/1054.
|
|
75
|
+
|
|
76
|
+
## 0.54.0 — 2026-06-26
|
|
77
|
+
|
|
78
|
+
### Audit: two real N64/PS1 parity gaps found + fixed
|
|
79
|
+
|
|
80
|
+
A full per-tool audit (all 32 tools, live-probed on both platforms) surfaced two
|
|
81
|
+
genuine gaps where the platform was CAPABLE but the tool didn't deliver:
|
|
82
|
+
|
|
83
|
+
1. **PS1 reverse-engineering returned bogus addresses.** `disasm`/`cfg`/`xrefs`/
|
|
84
|
+
`functions`/`decompile` on a PS1 PS-EXE found a single fake `fcn.00000000` at
|
|
85
|
+
file offset 0 (not the real 0x80010000+ VA), and `decompile` then threw "address
|
|
86
|
+
maps outside the image." Root cause: rizin ignores `-B` on a raw buffer, so the
|
|
87
|
+
stripped .text was analyzed flat from 0, and PS1's ABSOLUTE jal targets dangled →
|
|
88
|
+
no cross-function discovery. Fix: left-pad the .text so flat offset == the VA's
|
|
89
|
+
low 20 bits (jal-following now works) and add the high bits back as a rebase, so
|
|
90
|
+
every reported address is a real VA that round-trips. PS1 now finds all functions
|
|
91
|
+
(16 vs 1) at correct VAs, CFG/xrefs/decompile all resolve. (N64 was already fine.)
|
|
92
|
+
|
|
93
|
+
2. **PS1 `video_ram` was claimed but empty.** The manifest lists `video_ram` for
|
|
94
|
+
every platform, but pcsx_rearmed never exposed the GPU VRAM. Added a
|
|
95
|
+
`romdev_vram_get` export (1024×512×16bpp) wired into `memory({region:'video_ram'})`
|
|
96
|
+
— PS1 GPU VRAM is now readable (verified: rendered pixels show up).
|
|
97
|
+
|
|
98
|
+
Everything else in the audit was already at parity or genuinely N/A-by-hardware
|
|
99
|
+
(the tile/sprite/nametable/palette inspectors need tables a framebuffer/3D renderer
|
|
100
|
+
doesn't have; `cart` is for cartridge ROMs). save/loadState, runUntil, frame-verify,
|
|
101
|
+
romPatch, and the agnostic art/snippet tools all work on both. Suite 1053/1053.
|
|
102
|
+
|
|
103
|
+
## 0.53.0 — 2026-06-26
|
|
104
|
+
|
|
105
|
+
### N64 + PS1 reach EXAMPLE parity: 5 full genre games each
|
|
106
|
+
|
|
107
|
+
Both new platforms now ship the same caliber of bundled, playable examples as the
|
|
108
|
+
other 14 (parity = "5 decent full games"), registered in project.js and forkable +
|
|
109
|
+
buildable through the normal createProject/build tool flow.
|
|
110
|
+
|
|
111
|
+
**PS1** (4 × 3D + 1 × 2D, on a software 3D engine + the GPU): shmup STARFALL,
|
|
112
|
+
racing POLE BENDER (a real receding 3D road), platformer BLOCK HOP, sports
|
|
113
|
+
SLAM COURT (3D pong/air-hockey), puzzle DROP GRID (2D — the right idiom for a grid).
|
|
114
|
+
|
|
115
|
+
**N64** (all 5 × 3D — the N64 was a 3D-first machine, so even the puzzle is a 3D
|
|
116
|
+
well of cubes): STARFALL 64 / POLE BENDER 64 / BLOCK HOP 64 / SLAM COURT 64 /
|
|
117
|
+
DROP GRID 64, on the same software-3D lib with a framebuffer backend the
|
|
118
|
+
headless-angrylion core scans out.
|
|
119
|
+
|
|
120
|
+
Every example builds + boots + renders, verified on the actual cores (frames
|
|
121
|
+
inspected). The N64 path required the three earlier breakthroughs (self-booting
|
|
122
|
+
.z64 via a clean IPL3, a headless-angrylion GL-free core, and the VI-scanout
|
|
123
|
+
software rasterizer). Suite 1052/1052.
|
|
124
|
+
|
|
125
|
+
**This completes N64 + PS1 parity** — every tool the other 14 have works, and both
|
|
126
|
+
ship 5 full idiomatic examples.
|
|
127
|
+
|
|
128
|
+
## 0.51.0 — 2026-06-26
|
|
129
|
+
|
|
130
|
+
### N64 audioDebug — FULL parity (zero functional gaps)
|
|
131
|
+
|
|
132
|
+
- **`getAudioState({chip:'ai'})`** decodes the N64 Audio Interface: sample rate
|
|
133
|
+
(from DACRATE + the VI clock), whether audio is playing (a buffer is DMA-queued),
|
|
134
|
+
the DMA source address. (N64 audio is RSP-mixed, so the AI is the OUTPUT state, not
|
|
135
|
+
per-voice — that lives in game-specific RSP audio lists in RDRAM.) Verified: a real
|
|
136
|
+
ROM reports `playing:true, sampleRate:32006`. From a `romdev_ai_get` export added
|
|
137
|
+
to parallel_n64. n64 `audioChips:['ai']`, `audioDebug:true`.
|
|
138
|
+
|
|
139
|
+
**This closes the last functional gap.** A programmatic audit confirms N64/PS1 now
|
|
140
|
+
match the canonical 14 on every applicable op — build, run, screenshot, memory,
|
|
141
|
+
cpuState, audioDebug, disasm, decompile, cheats, breakpoint, watch. The only manifest
|
|
142
|
+
differences are N/A BY HARDWARE: the tile/sprite/nametable/palette inspectors
|
|
143
|
+
(framebuffer/3D renderers have no such tables) and `cart` (disc-based). Suite 1052/1052.
|
|
144
|
+
|
|
145
|
+
## 0.50.0 — 2026-06-26
|
|
146
|
+
|
|
147
|
+
### PS1 SPU audioDebug
|
|
148
|
+
|
|
149
|
+
- **`getAudioState({chip:'spu'})`** decodes the PS1 SPU's 24 ADPCM voices (per-voice
|
|
150
|
+
volume L/R, pitch→Hz, ADSR, key-on/off + main volume + control), from a
|
|
151
|
+
`romdev_spu_get` export added to pcsx_rearmed (copies the SPU regArea). Verified:
|
|
152
|
+
a toolchain-built program's SPU register writes read back correctly.
|
|
153
|
+
- ps1 `audioDebug:true` in the manifest.
|
|
154
|
+
|
|
155
|
+
**Parity status (N64/PS1 vs the canonical 14):** at parity on build, run, screenshot,
|
|
156
|
+
memory, cpuState, disasm, decompile, cheats, breakpoint, watch (+ PS1 audioDebug).
|
|
157
|
+
Genuinely N/A by hardware: the tile/sprite/nametable/palette inspectors (framebuffer/3D
|
|
158
|
+
renderers have no such tables) and `cart` (disc-based). **Remaining real TODO:** N64
|
|
159
|
+
audioDebug (the RSP/AI audio path — a deeper decode than the PS1 SPU register block).
|
|
160
|
+
|
|
161
|
+
## 0.49.0 — 2026-06-26
|
|
162
|
+
|
|
163
|
+
### N64 + PS1 live-debug — breakpoint + watch now work
|
|
164
|
+
|
|
165
|
+
The remaining big parity gap: the live-debugging RE tools. parallel_n64 (R4300) and
|
|
166
|
+
pcsx_rearmed (R3000) are rebuilt with romdev's CPU instrumentation, so:
|
|
167
|
+
|
|
168
|
+
- **`breakpoint({on:'pc'|'write'|'read'})`** + **`watch({on:'range'|'mem'})`** work on
|
|
169
|
+
both MIPS platforms — verified live: a C program (built by the romdev toolchain)
|
|
170
|
+
writing to a known address is caught by a write watchpoint with the writing PC, and
|
|
171
|
+
a range watch captures the write stream during emulation.
|
|
172
|
+
- A `romdev_debug.c` instrumentation unit (per core) hooks the memory write/read paths
|
|
173
|
+
(write/read watch + range watch + coverage) and the interpreter step (PC break +
|
|
174
|
+
single-step). Exports the full `romdev_watchpoint`/`readwatch`/`pcbreak`/`range`/`cov`/
|
|
175
|
+
`regsnap`/`watchdog` set the host already expects — so the host needed ZERO changes
|
|
176
|
+
(its debug methods were already generic).
|
|
177
|
+
- Reproducible via the updated build-pcsx-rearmed.sh / build-parallel-n64.sh
|
|
178
|
+
(patch the source + hooks + exports).
|
|
179
|
+
|
|
180
|
+
**Remaining honest gap:** `audioDebug` (PS1 SPU / N64 audio chip decoders) is still
|
|
181
|
+
TODO. The tile/sprite/nametable inspectors stay N/A by hardware (framebuffer/3D
|
|
182
|
+
renderers have no such tables). Everything else is at parity. Suite 1050/1050.
|
|
183
|
+
|
|
184
|
+
## 0.48.0 — 2026-06-25
|
|
185
|
+
|
|
186
|
+
### N64 + PS1 `build` op — full feature parity
|
|
187
|
+
|
|
188
|
+
The last missing op. N64 and PS1 now **build** C from source, completing parity
|
|
189
|
+
with the 14 (every op: build, run, screenshot, memory, disasm, decompile, cheats,
|
|
190
|
+
cpuState).
|
|
191
|
+
|
|
192
|
+
- **A from-scratch `mips-elf` GCC toolchain compiled to WASM** — binutils 2.42 +
|
|
193
|
+
gcc 14.2.0 + newlib 4.4.0, the same two-stage pipeline as the Genesis m68k
|
|
194
|
+
toolchain. STAGE 1 builds it natively; STAGE 2 re-compiles cc1/as/ld/objcopy/
|
|
195
|
+
objdump to WASM. One toolchain serves both endiannesses (cc1 `-mel`/`-meb`,
|
|
196
|
+
as/ld `-EL`/`-EB`).
|
|
197
|
+
- **`buildSource({platform:'ps1'|'n64', language:'c'})`** — cc1 → as → ld → objcopy,
|
|
198
|
+
links a minimal crt0 + libc/libm/libgcc. PS1 wraps the image in a PS-EXE the HLE
|
|
199
|
+
BIOS loads; N64 emits a big-endian flat image. **Verified end-to-end:** a C program
|
|
200
|
+
compiled by the toolchain boots and renders on the PS1 core (GPU fill executed).
|
|
201
|
+
- Four GCC-15/emscripten build incompatibilities fixed along the way (all in the
|
|
202
|
+
build scripts): binutils `static_assert`-as-identifier (`-std=gnu11`), newlib
|
|
203
|
+
libgloss pre-C23 idioms (permissive target CFLAGS), libiberty `psignal` conflict
|
|
204
|
+
(source patch, both gcc + binutils copies), and the `all-gcc`/`all` aux-tool
|
|
205
|
+
targets pulling in ftw/gprofng (build the specific `cc1`/`as`/`ld` targets).
|
|
206
|
+
- New `romdev-toolchain-mips-gcc` package (the WASM tools); pinned + reproducible
|
|
207
|
+
via `build-mips-toolchain.sh` + `build-mips-wasm-tools.sh`.
|
|
208
|
+
|
|
209
|
+
**Caveats:** the bare build path has no SDK yet — PS1 is "drive the GPU/SPU
|
|
210
|
+
registers yourself" (PSn00bSDK forthcoming); N64 compiles+links but a self-booting
|
|
211
|
+
cart needs IPL3 + a libdragon header (libdragon forthcoming). Both are logic/RE-
|
|
212
|
+
complete and the foundation for the SDKs. The framebuffer/3D renderers have no
|
|
213
|
+
tile/sprite inspectors by nature.
|
|
214
|
+
|
|
215
|
+
## 0.47.0 — 2026-06-25
|
|
216
|
+
|
|
217
|
+
### N64 + PS1 reach feature parity (cheats + cpuState + decompile)
|
|
218
|
+
|
|
219
|
+
The MIPS tier now matches the 14 on everything except `build`:
|
|
220
|
+
|
|
221
|
+
- **MIPS decompile** — `disasm({platform:'n64'|'ps1', target:'decompile'})` produces
|
|
222
|
+
real Ghidra C pseudocode. **No Python**: the stock Ghidra MIPS SLEIGH spec is
|
|
223
|
+
compiled to `.sla` (MIPS:BE:32 for N64, MIPS:LE:32 for PS1) by the same
|
|
224
|
+
`sleigh_opt` that builds the other 8 CPU tables. (m2c/Pyodide was rejected — it
|
|
225
|
+
would drag a CPython runtime into the tool.)
|
|
226
|
+
- **cpuState** — `cpu({op:'read'})` decodes the live R4300 (N64) / R3000 (PS1)
|
|
227
|
+
register file: 32 GPRs by o32 ABI name + lo/hi + PC. Backed by a tiny
|
|
228
|
+
`romdev_mips_regs_get` appended to each core's `libretro.c` (the new
|
|
229
|
+
`build-parallel-n64.sh` / `build-pcsx-rearmed.sh` recipes).
|
|
230
|
+
- **cheats** — `_retro_cheat_set`/`_retro_cheat_reset` are now exported from the
|
|
231
|
+
rebuilt cores (they were in the upstream C, just unexported).
|
|
232
|
+
- The cores are reproducible: pinned in `versions.json`, built by the new recipes
|
|
233
|
+
(clone → append the regsnap → emcc with the cheat+regsnap exports; the regsnap is
|
|
234
|
+
`EMSCRIPTEN_KEEPALIVE` so LTO doesn't strip it).
|
|
235
|
+
|
|
236
|
+
**Parity scorecard** — N64 & PS1 now have: run, screenshot, memory r/w, disasm
|
|
237
|
+
(cfg/xrefs/functions), decompile, cheats, cpuState. The one remaining op is
|
|
238
|
+
**build** (a MIPS GCC→WASM toolchain — PSn00bSDK/libdragon — the largest separate
|
|
239
|
+
piece). The framebuffer/3D renderers have no tile/sprite/nametable inspectors by
|
|
240
|
+
nature.
|
|
241
|
+
|
|
242
|
+
## 0.46.0 — 2026-06-25
|
|
243
|
+
|
|
244
|
+
### N64 + PS1 run-side: boot, run, render (the MIPS tier goes live)
|
|
245
|
+
|
|
246
|
+
Past analysis-only — N64 and PS1 now **boot, run, and present real frames** through
|
|
247
|
+
the host. The 32-bit MIPS tier is a new partial tier (`tier:"mips"`), distinct from
|
|
248
|
+
the canonical 14.
|
|
249
|
+
|
|
250
|
+
- **N64** (ParaLLEl-N64, HW 3D render) — `run` + `frame({op:'screenshot'})` produce
|
|
251
|
+
real 3D frames headlessly via a GL framebuffer readback. Rendering goes through a
|
|
252
|
+
ported GL bridge (`LibretroGL`/`LibretroGLBridge`) backed by `native-gles` +
|
|
253
|
+
`webgl-node`, which are **optionalDependencies**: lazy-loaded only when an N64/PS1
|
|
254
|
+
core boots, so the 14 software platforms and headless installs without the GPU
|
|
255
|
+
module are completely unaffected (clear install hint if absent).
|
|
256
|
+
- **PS1** (PCSX-ReARMed, software + **built-in HLE BIOS**) — `run` + screenshot with
|
|
257
|
+
zero firmware to ship, no GL dependency. Loads PS-EXE (and disc images).
|
|
258
|
+
- **Memory** works for both: `readMemory`/`writeMemory` on `system_ram` (N64 = 8MB
|
|
259
|
+
RDRAM, PS1 = 2MB main RAM) — poke values directly.
|
|
260
|
+
- **Static RE** (from 0.45.0) still works: `disasm({platform:'n64'|'ps1',
|
|
261
|
+
target:'rom'|'functions'|'cfg'|'xrefs'})`.
|
|
262
|
+
- N64 `.v64`/`.n64` byte orders auto-normalize to `.z64`; PS-EXE headers are stripped.
|
|
263
|
+
|
|
264
|
+
Three GL-binding bugs fixed to get frames on screen: the Emscripten `getContext`
|
|
265
|
+
shim's `instanceof WebGLRenderingContext` check (needs WebGL1 to be a distinct
|
|
266
|
+
class), the signed-vs-unsigned `RETRO_HW_FRAME_BUFFER_VALID` compare, and the
|
|
267
|
+
all-transparent FBO alpha (forced opaque in a new RGBA decode path).
|
|
268
|
+
|
|
269
|
+
**Still pending** for full parity (honestly off in the manifest): `build` (needs a
|
|
270
|
+
MIPS toolchain — PSn00bSDK/libdragon), `decompile` (needs a MIPS SLEIGH spec in the
|
|
271
|
+
decompiler — slaspec sources exist, a decompiler-package rebuild), `getCPUState` +
|
|
272
|
+
`cheats` (need a core rebuild exposing those exports). The framebuffer/3D renderers
|
|
273
|
+
have no tile/sprite/nametable inspectors by nature.
|
|
274
|
+
|
|
275
|
+
## 0.45.0 — 2026-06-25
|
|
276
|
+
|
|
277
|
+
### PS1 + N64 analysis-first (the 32-bit MIPS tier)
|
|
278
|
+
|
|
279
|
+
The first step past the GBA-era line: static RE for PlayStation (R3000) and
|
|
280
|
+
Nintendo 64 (R4300), via the MIPS plugin already in the shipped `rizin.wasm` — no
|
|
281
|
+
new core, no toolchain, no GPU bridge in this slice.
|
|
282
|
+
|
|
283
|
+
- **`disasm({platform:'ps1'|'n64', target:'functions'|'cfg'|'xrefs'|'rom'})`** —
|
|
284
|
+
works on real ROMs. Verified: a libdragon N64 homebrew (`FlappyBird.z64`)
|
|
285
|
+
recovers 251 functions with control-flow graphs and cross-references. The raw
|
|
286
|
+
N64 ROM has no entry `aaa` recognizes, so MIPS analysis is seeded at the
|
|
287
|
+
post-IPL3 code start (`af` + `aac`) — `aaa` finds 0, the seed finds the tree.
|
|
288
|
+
- **Endianness is wired right:** PS1 is little-endian, N64 is big-endian (same
|
|
289
|
+
`mips` arch). N64 `.v64`/`.n64` dumps are auto-normalized to `.z64` byte order;
|
|
290
|
+
PS1 PS-EXE headers are stripped to the load address.
|
|
291
|
+
- **Capability manifest:** `ps1` (framebuffer) / `n64` (3d) are an analysis-only
|
|
292
|
+
tier — `disasm` true; run-side ops (build/run/screenshot/the tile/sprite
|
|
293
|
+
inspectors) and `decompile` false. The tile/nametable inspectors are meaningless
|
|
294
|
+
on a framebuffer/3D renderer, so an agent gets the clean `unsupported()` signal.
|
|
295
|
+
- **`decompile` (C pseudocode) is NOT available yet** for MIPS — the rz-ghidra
|
|
296
|
+
decompiler ships no MIPS SLEIGH spec (adding `MIPS.sla` is a later
|
|
297
|
+
`romdev-analysis-decompiler` rebuild). It returns a clear steer to the working
|
|
298
|
+
disasm targets, not a cryptic failure.
|
|
299
|
+
|
|
300
|
+
## 0.44.0 — 2026-06-25
|
|
301
|
+
|
|
302
|
+
### v0.41.0 feedback (part 2) — RE-session ergonomics
|
|
303
|
+
|
|
304
|
+
- **`watch({on:'range'})` dedupe + digest.** A range watch over a churny window
|
|
305
|
+
flooded with per-frame writes (a counter inc'd at one PC → hundreds of
|
|
306
|
+
near-identical rows). New `dedupe:true` collapses identical `(pc,address,value)`
|
|
307
|
+
events to one row with an `occurrences` count (parity with `on:'dma'`), and
|
|
308
|
+
`distinctPCsOnly:true` returns JUST the per-PC digest (`byPC[{pc,count,
|
|
309
|
+
sampleAddress,sampleValue}]`) and suppresses the raw event list — the
|
|
310
|
+
token-cheap "which routines touch this range?" answer. (133737 N1)
|
|
311
|
+
- **`catalog({op:'status'}).capabilities` build-toolchain flags.** Added
|
|
312
|
+
`cc65Build`, `ld65Link`, `da65Disasm` so an agent knows before calling
|
|
313
|
+
`build`/`disasm` whether the toolchain is present (the analysis subtargets
|
|
314
|
+
cfg/xrefs/functions/decompile are all da65-backed). (184553 #1, 190223 #1)
|
|
315
|
+
- **`cheats` slot lifecycle.** `apply` now REPLACES an active freeze on the same
|
|
316
|
+
address instead of stacking a second one that fights for the byte
|
|
317
|
+
(`replacedSameAddress`), and new `op:'remove'` drops ONE cheat by slot/code
|
|
318
|
+
without `clear`'s nuke-all. (213831 #3)
|
|
319
|
+
- **`breakpoint` `settleFrames`.** Back-to-back driven runs on the same live host
|
|
320
|
+
could inherit the prior run's held-button shadow on frame 0 (false-positiving a
|
|
321
|
+
negative control). `settleFrames:N` releases the pad to neutral and steps N
|
|
322
|
+
frames before the run so it starts clean. (213831 #1)
|
|
323
|
+
- **`breakpoint({on:'pc'})` miss-note** now names the negative-control case: a
|
|
324
|
+
`hit:false` may be the DESIRED result (proving input X does NOT reach a branch),
|
|
325
|
+
not a wrong-address failure. (213831 #2)
|
|
326
|
+
- Confirmed already shipped (0.42.0): hex-string `address`/`offset` params, the
|
|
327
|
+
base capabilities map. (133737 N2, 002129 #2)
|
|
328
|
+
|
|
7
329
|
## 0.43.0 — 2026-06-25
|
|
8
330
|
|
|
9
331
|
### Port engine is now GENERIC (any source → any target), not NES→SNES-only
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* platformer/main.c — BLOCK HOP: a 3D Nintendo 64 platformer.
|
|
3
|
+
*
|
|
4
|
+
* A 3D character (cube hero) runs and jumps across floating platforms rendered in
|
|
5
|
+
* perspective. Gravity + jump physics in 16.16 fixed point, AABB landing tests
|
|
6
|
+
* against platform tops, coins to collect, a pit you can fall into (lose a life),
|
|
7
|
+
* a chase camera that follows the hero. Title -> play -> game-over, score + lives.
|
|
8
|
+
*
|
|
9
|
+
* Build: build({ platform:"n64", language:"c" }). Controls: LEFT/RIGHT run,
|
|
10
|
+
* CROSS jump, START begin/restart.
|
|
11
|
+
*
|
|
12
|
+
* 3D technique: platforms are flat-topped boxes drawn as no-cull quads; the hero
|
|
13
|
+
* is a culled cube. The camera tracks the hero's X with a slight downward tilt so
|
|
14
|
+
* you see the platform tops. Coins are small spinning cubes.
|
|
15
|
+
*/
|
|
16
|
+
#include "n64.h"
|
|
17
|
+
|
|
18
|
+
#define NPLAT 6
|
|
19
|
+
#define NCOIN 6
|
|
20
|
+
enum { TITLE, PLAY, OVER };
|
|
21
|
+
|
|
22
|
+
typedef struct { fix x, y, z, w; } Plat; /* top-center + half-width */
|
|
23
|
+
static Plat plat[NPLAT];
|
|
24
|
+
static struct { fix x, y, z; int got; } coin[NCOIN];
|
|
25
|
+
|
|
26
|
+
static fix hx, hy, vy; /* hero pos + vertical velocity */
|
|
27
|
+
static int onground;
|
|
28
|
+
static int state, score, lives, hi;
|
|
29
|
+
static fix cam_x, spin;
|
|
30
|
+
static unsigned int prev_pad;
|
|
31
|
+
|
|
32
|
+
static void box_top(fix cx, fix top, fix z, fix hw, fix depth, unsigned short col)
|
|
33
|
+
{
|
|
34
|
+
Vec3 a,b,c,d;
|
|
35
|
+
a.x=cx-hw; a.y=top; a.z=z-depth; b.x=cx+hw; b.y=top; b.z=z-depth;
|
|
36
|
+
c.x=cx+hw; c.y=top; c.z=z+depth; d.x=cx-hw; d.y=top; d.z=z+depth;
|
|
37
|
+
n64_quad3d_nc(a,b,c,d,col); /* top face */
|
|
38
|
+
/* front face for thickness */
|
|
39
|
+
a.y=top; b.y=top; c.y=top-FIX(2); d.y=top-FIX(2);
|
|
40
|
+
a.x=cx-hw; a.z=z+depth; b.x=cx+hw; b.z=z+depth; c.x=cx+hw; c.z=z+depth; d.x=cx-hw; d.z=z+depth;
|
|
41
|
+
n64_quad3d_nc(a,b,c,d, col - RGB(20,20,20));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static void draw_cube_at(fix x, fix y, fix z, fix s, fix yaw, unsigned short col)
|
|
45
|
+
{
|
|
46
|
+
Vec3 v[8]; int i;
|
|
47
|
+
static const int sx[8]={-1,1,1,-1,-1,1,1,-1},sy[8]={-1,-1,1,1,-1,-1,1,1},sz[8]={-1,-1,-1,-1,1,1,1,1};
|
|
48
|
+
n64_model(x,y,z,yaw);
|
|
49
|
+
for(i=0;i<8;i++){v[i].x=sx[i]*s;v[i].y=sy[i]*s;v[i].z=sz[i]*s;}
|
|
50
|
+
n64_quad3d(v[0],v[1],v[2],v[3],col); n64_quad3d(v[5],v[4],v[7],v[6],col);
|
|
51
|
+
n64_quad3d(v[4],v[0],v[3],v[7],col); n64_quad3d(v[1],v[5],v[6],v[2],col);
|
|
52
|
+
n64_quad3d(v[4],v[5],v[1],v[0],col); n64_quad3d(v[3],v[2],v[6],v[7],col);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static void reset_game(void)
|
|
56
|
+
{
|
|
57
|
+
int i;
|
|
58
|
+
/* a run of platforms along +X with a gap (pit) in the middle */
|
|
59
|
+
for (i = 0; i < NPLAT; i++) {
|
|
60
|
+
plat[i].x = (fix)(i * 4 - 4) << 16;
|
|
61
|
+
plat[i].y = (fix)((i % 3) - 1) << 16;
|
|
62
|
+
plat[i].z = FIX(8);
|
|
63
|
+
plat[i].w = FIX(2);
|
|
64
|
+
}
|
|
65
|
+
plat[3].y = FIX(-6); /* the pit: a platform dropped far below */
|
|
66
|
+
for (i = 0; i < NCOIN; i++) { coin[i].x = (fix)(i*4-3)<<16; coin[i].y = plat[i].y + FIX(2); coin[i].z = FIX(8); coin[i].got = 0; }
|
|
67
|
+
hx = FIX(-4); hy = FIX(2); vy = 0; onground = 0;
|
|
68
|
+
score = 0; lives = 3;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* find the platform top under the hero's x (or a very low floor = pit). */
|
|
72
|
+
static fix ground_at(fix x)
|
|
73
|
+
{
|
|
74
|
+
int i; fix best = FIX(-20);
|
|
75
|
+
for (i = 0; i < NPLAT; i++) {
|
|
76
|
+
fix dx = x - plat[i].x; if (dx < 0) dx = -dx;
|
|
77
|
+
if (dx < plat[i].w) { if (plat[i].y > best) best = plat[i].y; }
|
|
78
|
+
}
|
|
79
|
+
return best;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static void update(void)
|
|
83
|
+
{
|
|
84
|
+
unsigned int pad = n64_pad();
|
|
85
|
+
int i;
|
|
86
|
+
if (pad & PAD_LEFT) hx -= FIXF(0.18f);
|
|
87
|
+
if (pad & PAD_RIGHT) hx += FIXF(0.18f);
|
|
88
|
+
if (hx < FIX(-6)) hx = FIX(-6); if (hx > FIX(14)) hx = FIX(14);
|
|
89
|
+
|
|
90
|
+
if ((pad & PAD_A) && !(prev_pad & PAD_A) && onground) { vy = FIXF(0.55f); onground = 0; }
|
|
91
|
+
|
|
92
|
+
vy -= FIXF(0.04f); /* gravity */
|
|
93
|
+
hy += vy;
|
|
94
|
+
{
|
|
95
|
+
fix g = ground_at(hx) + FIX(1); /* hero sits 1 unit above the platform top */
|
|
96
|
+
if (hy <= g && vy <= 0) {
|
|
97
|
+
if (g < FIX(-8)) { /* fell in the pit */
|
|
98
|
+
if (--lives <= 0) { if (score>hi) hi=score; state = OVER; }
|
|
99
|
+
hx = FIX(-4); hy = FIX(4); vy = 0;
|
|
100
|
+
} else { hy = g; vy = 0; onground = 1; }
|
|
101
|
+
} else onground = 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (i = 0; i < NCOIN; i++) if (!coin[i].got) {
|
|
105
|
+
fix dx = hx - coin[i].x, dy = hy - coin[i].y;
|
|
106
|
+
if (dx<0)dx=-dx; if (dy<0)dy=-dy;
|
|
107
|
+
if (dx < FIX(1) && dy < FIX(2)) { coin[i].got = 1; score += 100; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
cam_x += (hx - cam_x) >> 3; /* smooth follow */
|
|
111
|
+
spin += FIX(4);
|
|
112
|
+
prev_pad = pad;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static void render(void)
|
|
116
|
+
{
|
|
117
|
+
int i;
|
|
118
|
+
n64_clear(RGB(40, 60, 120)); /* sky */
|
|
119
|
+
n64_camera(cam_x, FIX(2), FIX(-1), 0, FIXF(-0.22f));
|
|
120
|
+
|
|
121
|
+
for (i = 0; i < NPLAT; i++)
|
|
122
|
+
box_top(plat[i].x, plat[i].y, plat[i].z, plat[i].w, FIX(2),
|
|
123
|
+
(i==3)?RGB(120,40,40):RGB(90, 150, 70));
|
|
124
|
+
for (i = 0; i < NCOIN; i++) if (!coin[i].got)
|
|
125
|
+
draw_cube_at(coin[i].x, coin[i].y, coin[i].z, FIXF(0.3f), spin, RGB(255, 220, 40));
|
|
126
|
+
|
|
127
|
+
draw_cube_at(hx, hy, FIX(8), FIXF(0.6f), 0, RGB(230, 90, 60));
|
|
128
|
+
|
|
129
|
+
n64_number(8, 6, (unsigned)score, RGB(255,255,255));
|
|
130
|
+
for (i = 0; i < lives; i++) n64_rect(290 - i*10, 8, 6, 6, RGB(230, 90, 60));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
int main(void)
|
|
134
|
+
{
|
|
135
|
+
n64_init();
|
|
136
|
+
n64_srand(0xBEEF);
|
|
137
|
+
state = TITLE; prev_pad = 0; cam_x = 0;
|
|
138
|
+
for (;;) {
|
|
139
|
+
unsigned int pad = n64_pad();
|
|
140
|
+
if (state == TITLE) {
|
|
141
|
+
n64_clear(RGB(20, 30, 60));
|
|
142
|
+
n64_camera(0, 0, FIX(-1), 0, 0);
|
|
143
|
+
spin += FIX(3);
|
|
144
|
+
draw_cube_at(0, 0, FIX(6), FIX(1), spin, RGB(230, 90, 60));
|
|
145
|
+
if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
|
|
146
|
+
prev_pad = pad;
|
|
147
|
+
} else if (state == PLAY) { update(); render(); }
|
|
148
|
+
else {
|
|
149
|
+
n64_clear(RGB(8, 8, 30));
|
|
150
|
+
n64_number(110, 100, (unsigned)score, RGB(255, 220, 40));
|
|
151
|
+
n64_number(120, 130, (unsigned)hi, RGB(255, 255, 120));
|
|
152
|
+
if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
|
|
153
|
+
prev_pad = pad;
|
|
154
|
+
}
|
|
155
|
+
n64_flip();
|
|
156
|
+
}
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* puzzle/main.c — DROP GRID 64: a 3D Nintendo 64 falling-block puzzle.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the PS1 puzzle (flat 2D), this is rendered in 3D — the N64 was a 3D-first
|
|
5
|
+
* machine, so the well is a perspective box and the blocks are shaded cubes you
|
|
6
|
+
* watch fall in depth. Same falling-block logic (move, drop, clear full rows, ramp
|
|
7
|
+
* speed, stack-out = game over) but presented through the 3D pipeline at an angle.
|
|
8
|
+
*
|
|
9
|
+
* Build: build({ platform:"n64", language:"c" }). Controls: LEFT/RIGHT move the
|
|
10
|
+
* falling block, DOWN soft-drop, START begin/restart.
|
|
11
|
+
*
|
|
12
|
+
* 3D technique: each grid cell is a cube at (col, -row, 0) in world space; a tilted
|
|
13
|
+
* camera looks into the well so you see depth. The board logic is plain integers.
|
|
14
|
+
*/
|
|
15
|
+
#include "n64.h"
|
|
16
|
+
|
|
17
|
+
#define GW 6
|
|
18
|
+
#define GH 10
|
|
19
|
+
enum { TITLE, PLAY, OVER };
|
|
20
|
+
|
|
21
|
+
static unsigned char grid[GH][GW];
|
|
22
|
+
static int fx, fy, fc;
|
|
23
|
+
static int tick, fall_period;
|
|
24
|
+
static int state, score, hi;
|
|
25
|
+
static unsigned int prev_pad;
|
|
26
|
+
|
|
27
|
+
static const unsigned short PAL[5] = {
|
|
28
|
+
0, RGB(230,60,60), RGB(60,200,90), RGB(80,150,255), RGB(240,200,60)
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
static void cube_at(fix x, fix y, fix z, fix s, unsigned short col)
|
|
32
|
+
{
|
|
33
|
+
Vec3 v[8]; int i;
|
|
34
|
+
static const int sx[8]={-1,1,1,-1,-1,1,1,-1},sy[8]={-1,-1,1,1,-1,-1,1,1},sz[8]={-1,-1,-1,-1,1,1,1,1};
|
|
35
|
+
n64_model(x,y,z,0);
|
|
36
|
+
for(i=0;i<8;i++){v[i].x=sx[i]*s;v[i].y=sy[i]*s;v[i].z=sz[i]*s;}
|
|
37
|
+
n64_quad3d(v[0],v[1],v[2],v[3],col); n64_quad3d(v[5],v[4],v[7],v[6],col);
|
|
38
|
+
n64_quad3d(v[4],v[0],v[3],v[7],col); n64_quad3d(v[1],v[5],v[6],v[2],col);
|
|
39
|
+
n64_quad3d(v[4],v[5],v[1],v[0],col); n64_quad3d(v[3],v[2],v[6],v[7],col);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* grid cell (c,r) → world position (centered, y up). */
|
|
43
|
+
static fix cellx(int c) { return (fix)(c - GW/2) << 16; }
|
|
44
|
+
static fix celly(int r) { return (fix)((GH/2) - r) << 16; }
|
|
45
|
+
|
|
46
|
+
static void new_block(void)
|
|
47
|
+
{ fx=GW/2; fy=0; fc=1+(n64_rand()%4); if(grid[0][fx]){ if(score>hi)hi=score; state=OVER; } }
|
|
48
|
+
|
|
49
|
+
static void reset_game(void)
|
|
50
|
+
{ int r,c; for(r=0;r<GH;r++)for(c=0;c<GW;c++)grid[r][c]=0; score=0; tick=0; fall_period=30; new_block(); }
|
|
51
|
+
|
|
52
|
+
static int blocked(int x,int y){ return x<0||x>=GW||y>=GH||(y>=0&&grid[y][x]); }
|
|
53
|
+
|
|
54
|
+
static void lock_and_clear(void)
|
|
55
|
+
{
|
|
56
|
+
int r,c,rr;
|
|
57
|
+
grid[fy][fx]=fc;
|
|
58
|
+
for(r=GH-1;r>=0;r--){
|
|
59
|
+
int full=1; for(c=0;c<GW;c++) if(!grid[r][c]){ full=0; break; }
|
|
60
|
+
if(full){ for(rr=r;rr>0;rr--)for(c=0;c<GW;c++)grid[rr][c]=grid[rr-1][c]; for(c=0;c<GW;c++)grid[0][c]=0; score+=100; if(fall_period>8)fall_period--; r++; }
|
|
61
|
+
}
|
|
62
|
+
new_block();
|
|
63
|
+
}
|
|
64
|
+
static void step_fall(void){ if(!blocked(fx,fy+1)) fy++; else lock_and_clear(); }
|
|
65
|
+
|
|
66
|
+
static void update(void)
|
|
67
|
+
{
|
|
68
|
+
unsigned int pad = n64_pad();
|
|
69
|
+
if((pad&PAD_LEFT)&&!(prev_pad&PAD_LEFT)&&!blocked(fx-1,fy)) fx--;
|
|
70
|
+
if((pad&PAD_RIGHT)&&!(prev_pad&PAD_RIGHT)&&!blocked(fx+1,fy)) fx++;
|
|
71
|
+
if(pad&PAD_DOWN){ step_fall(); tick=0; }
|
|
72
|
+
if(++tick>=fall_period){ step_fall(); tick=0; }
|
|
73
|
+
prev_pad=pad;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static void render(void)
|
|
77
|
+
{
|
|
78
|
+
int r,c;
|
|
79
|
+
n64_clear(RGB(12,12,24));
|
|
80
|
+
n64_camera(0,0,FIX(-12),0,FIXF(-0.05f)); /* look into the well */
|
|
81
|
+
/* settled blocks */
|
|
82
|
+
for(r=0;r<GH;r++) for(c=0;c<GW;c++) if(grid[r][c])
|
|
83
|
+
cube_at(cellx(c), celly(r), 0, FIXF(0.45f), PAL[grid[r][c]]);
|
|
84
|
+
/* falling block */
|
|
85
|
+
if(fy>=0) cube_at(cellx(fx), celly(fy), 0, FIXF(0.45f), PAL[fc]);
|
|
86
|
+
/* well floor + walls as dim cubes for depth cue */
|
|
87
|
+
for(c=-1;c<=GW;c++){ cube_at(cellx(c), celly(GH), 0, FIXF(0.45f), RGB(50,50,70)); }
|
|
88
|
+
for(r=0;r<=GH;r++){ cube_at(cellx(-1), celly(r), 0, FIXF(0.45f), RGB(40,40,60)); cube_at(cellx(GW), celly(r), 0, FIXF(0.45f), RGB(40,40,60)); }
|
|
89
|
+
n64_number(8,6,(unsigned)score,RGB(255,255,255));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
int main(void)
|
|
93
|
+
{
|
|
94
|
+
n64_init();
|
|
95
|
+
n64_srand(0xD0D0);
|
|
96
|
+
state=TITLE; prev_pad=0;
|
|
97
|
+
for(;;){
|
|
98
|
+
unsigned int pad = n64_pad();
|
|
99
|
+
if(state==TITLE){
|
|
100
|
+
static fix t; t+=FIX(3);
|
|
101
|
+
n64_clear(RGB(20,12,30));
|
|
102
|
+
n64_camera(0,0,FIX(-6),0,0);
|
|
103
|
+
n64_model(0,0,0,t); cube_at(0,0,0,FIX(1),RGB(80,150,255));
|
|
104
|
+
if((pad&PAD_START)&&!(prev_pad&PAD_START)){ reset_game(); state=PLAY; }
|
|
105
|
+
prev_pad=pad;
|
|
106
|
+
} else if(state==PLAY){ update(); render(); }
|
|
107
|
+
else {
|
|
108
|
+
n64_clear(RGB(30,8,8));
|
|
109
|
+
n64_number(110,100,(unsigned)score,RGB(255,120,120));
|
|
110
|
+
n64_number(120,130,(unsigned)hi,RGB(255,255,120));
|
|
111
|
+
if((pad&PAD_START)&&!(prev_pad&PAD_START)){ reset_game(); state=PLAY; }
|
|
112
|
+
prev_pad=pad;
|
|
113
|
+
}
|
|
114
|
+
n64_flip();
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
}
|