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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// mips-elf-gcc — WASM toolchain wrappers for N64 / PS1 C builds.
|
|
2
|
+
//
|
|
3
|
+
// Pipeline (mirrors m68k-elf-gcc/gcc.js — gcc-the-driver can't fork/exec under
|
|
4
|
+
// emscripten, so we orchestrate cc1 → as → ld → objcopy through callMain):
|
|
5
|
+
// runCc1mips({source, headers, options, endian}) → MIPS assembly (.s)
|
|
6
|
+
// runMipsAs({source, includes, endian}) → .o ELF object
|
|
7
|
+
// runMipsLd({objects, linkScript, ...}) → linked .elf (+ map)
|
|
8
|
+
// runMipsObjcopy({elf}) → raw .bin
|
|
9
|
+
//
|
|
10
|
+
// Endianness: N64 (R4300) is big-endian (default), PS1 (R3000) is little-endian
|
|
11
|
+
// (-EL). The same WASM toolchain emits both — pass endian:'little' for PS1.
|
|
12
|
+
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
|
|
17
|
+
import { runIsolated, textFile, binaryFile, getOutputBytes, getOutputText } from "../_worker/run.js";
|
|
18
|
+
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = path.dirname(__filename);
|
|
21
|
+
|
|
22
|
+
// The WASM ships in romdev-toolchain-mips-gcc; fall back to the in-tree dev copy.
|
|
23
|
+
function resolveMipsGlue(file) {
|
|
24
|
+
try {
|
|
25
|
+
const u = import.meta.resolve("romdev-toolchain-mips-gcc");
|
|
26
|
+
const p = path.join(path.dirname(fileURLToPath(u)), "wasm", file);
|
|
27
|
+
if (existsSync(p)) return p;
|
|
28
|
+
} catch { /* not resolvable — fall through */ }
|
|
29
|
+
const local = path.join(__dirname, "wasm", file);
|
|
30
|
+
if (existsSync(local)) return local;
|
|
31
|
+
throw new Error(`mips-elf-gcc WASM (${file}) not found — build it with scripts/build-mips-wasm-tools.sh`);
|
|
32
|
+
}
|
|
33
|
+
const _glue = {};
|
|
34
|
+
const mipsGlue = (file) => (_glue[file] ??= resolveMipsGlue(file));
|
|
35
|
+
|
|
36
|
+
/** Endian flags differ by TOOL: cc1 (the C frontend) wants `-mel`/`-meb`; the
|
|
37
|
+
* assembler + linker want `-EL`/`-EB`. `-mabi=32` (cc1) covers both R3000 (PS1)
|
|
38
|
+
* and R4300 (N64) 32-bit code. */
|
|
39
|
+
function cc1ArchFlags(endian) {
|
|
40
|
+
return [endian === "little" ? "-mel" : "-meb", "-mabi=32"];
|
|
41
|
+
}
|
|
42
|
+
function asArchFlags(endian) {
|
|
43
|
+
// -G0: never use GP-relative (small-data) addressing. Without it, statics land in
|
|
44
|
+
// .sdata/.sbss and the 16-bit GPREL offsets overflow ("relocation truncated") on
|
|
45
|
+
// anything but a tiny program. -G0 forces normal .data/.bss addressing.
|
|
46
|
+
return [endian === "little" ? "-EL" : "-EB", "-mabi=32", "-G0"];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── cc1 — MIPS gcc C frontend, source → assembly ─────────────────────
|
|
50
|
+
export async function runCc1mips(args) {
|
|
51
|
+
const { source, options = [], endian = "big" } = args;
|
|
52
|
+
const headers = args.headers ?? {};
|
|
53
|
+
const inputFiles = [textFile("/work/main.c", source)];
|
|
54
|
+
for (const [name, content] of Object.entries(headers)) inputFiles.push(textFile("/work/" + name, content));
|
|
55
|
+
const argv = [
|
|
56
|
+
...cc1ArchFlags(endian),
|
|
57
|
+
"-iquote", "/work", "-I", "/work",
|
|
58
|
+
...options,
|
|
59
|
+
"/work/main.c", "-o", "/work/main.s",
|
|
60
|
+
];
|
|
61
|
+
const r = await runIsolated({
|
|
62
|
+
gluePath: mipsGlue("cc1.mjs"),
|
|
63
|
+
argv, inputFiles,
|
|
64
|
+
outputFiles: [{ vfsPath: "/work/main.s", encoding: "utf8" }],
|
|
65
|
+
});
|
|
66
|
+
return { log: r.log, exitCode: r.exitCode, asmSource: getOutputText(r, "/work/main.s") || null,
|
|
67
|
+
...(r.crash ? { crash: r.crash, stage: "crash" } : {}) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── mips-elf-as — GNU assembler, .s → .o ─────────────────────────────
|
|
71
|
+
export async function runMipsAs(args) {
|
|
72
|
+
const { source, options = [], endian = "big" } = args;
|
|
73
|
+
const includes = args.includes ?? {};
|
|
74
|
+
const binaryIncludes = args.binaryIncludes ?? {};
|
|
75
|
+
const inputFiles = [textFile("/work/main.s", source)];
|
|
76
|
+
for (const [name, content] of Object.entries(includes)) inputFiles.push(textFile("/work/" + name, content));
|
|
77
|
+
for (const [name, bytes] of Object.entries(binaryIncludes)) inputFiles.push(binaryFile("/work/" + name, bytes));
|
|
78
|
+
const argv = [...asArchFlags(endian), "-I", "/work", ...options, "/work/main.s", "-o", "/work/main.o"];
|
|
79
|
+
const r = await runIsolated({
|
|
80
|
+
gluePath: mipsGlue("mips-elf-as.mjs"),
|
|
81
|
+
argv, inputFiles,
|
|
82
|
+
outputFiles: [{ vfsPath: "/work/main.o", encoding: "base64" }],
|
|
83
|
+
});
|
|
84
|
+
return { log: r.log, exitCode: r.exitCode, object: getOutputBytes(r, "/work/main.o"),
|
|
85
|
+
...(r.crash ? { crash: r.crash, stage: "crash" } : {}) };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── mips-elf-ld — GNU linker, .o + linker script → .elf ──────────────
|
|
89
|
+
export async function runMipsLd(args) {
|
|
90
|
+
const { objects, linkScript, libraries = [], libraryPaths = [], options = [], endian = "big" } = args;
|
|
91
|
+
const archives = args.archives ?? {};
|
|
92
|
+
const inputFiles = [textFile("/work/link.ld", linkScript)];
|
|
93
|
+
for (const [name, bytes] of Object.entries(objects)) inputFiles.push(binaryFile("/work/" + name, bytes));
|
|
94
|
+
for (const [name, bytes] of Object.entries(archives)) inputFiles.push(binaryFile("/work/" + name, bytes));
|
|
95
|
+
const argv = [
|
|
96
|
+
endian === "little" ? "-EL" : "-EB",
|
|
97
|
+
"-T", "/work/link.ld",
|
|
98
|
+
"-o", "/work/main.elf",
|
|
99
|
+
"-Map=/work/main.map",
|
|
100
|
+
...libraryPaths.flatMap((p) => ["-L", p]),
|
|
101
|
+
...Object.keys(objects).map((n) => "/work/" + n),
|
|
102
|
+
...libraries.map((l) => `-l${l}`),
|
|
103
|
+
...options,
|
|
104
|
+
];
|
|
105
|
+
const r = await runIsolated({
|
|
106
|
+
gluePath: mipsGlue("mips-elf-ld.mjs"),
|
|
107
|
+
argv, inputFiles,
|
|
108
|
+
outputFiles: [
|
|
109
|
+
{ vfsPath: "/work/main.elf", encoding: "base64" },
|
|
110
|
+
{ vfsPath: "/work/main.map", encoding: "utf8" },
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
return { log: r.log, exitCode: r.exitCode, elf: getOutputBytes(r, "/work/main.elf"),
|
|
114
|
+
map: getOutputText(r, "/work/main.map") || null,
|
|
115
|
+
...(r.crash ? { crash: r.crash, stage: "crash" } : {}) };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── mips-elf-objcopy — ELF → raw .bin ────────────────────────────────
|
|
119
|
+
export async function runMipsObjcopy(args) {
|
|
120
|
+
const { elf, options = [] } = args;
|
|
121
|
+
const inputFiles = [binaryFile("/work/main.elf", elf)];
|
|
122
|
+
const argv = ["-O", "binary", ...options, "/work/main.elf", "/work/main.bin"];
|
|
123
|
+
const r = await runIsolated({
|
|
124
|
+
gluePath: mipsGlue("mips-elf-objcopy.mjs"),
|
|
125
|
+
argv, inputFiles,
|
|
126
|
+
outputFiles: [{ vfsPath: "/work/main.bin", encoding: "base64" }],
|
|
127
|
+
});
|
|
128
|
+
return { log: r.log, exitCode: r.exitCode, binary: getOutputBytes(r, "/work/main.bin"),
|
|
129
|
+
...(r.crash ? { crash: r.crash, stage: "crash" } : {}) };
|
|
130
|
+
}
|