romdevtools 0.44.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 +293 -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/disasm.js +2 -0
- package/src/mcp/tools/frame.js +18 -9
- 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/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/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
|
@@ -6,6 +6,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { PNG } from "pngjs";
|
|
8
8
|
import { getHost } from "../state.js";
|
|
9
|
+
import { naReason } from "../../cores/capabilities.js";
|
|
9
10
|
import { imageContent, jsonContent, unsupported } from "../util.js";
|
|
10
11
|
|
|
11
12
|
// Consolidation: several handlers in this big shared file are extracted as
|
|
@@ -39,6 +40,8 @@ import { decodeGbApu, decodeGbaApu } from "../../host/gb-apu-state.js";
|
|
|
39
40
|
import { decodeC64Sid } from "../../host/c64-sid-state.js";
|
|
40
41
|
import { decodeLynxMikey, decodeLynxPalette } from "../../host/lynx-mikey-state.js";
|
|
41
42
|
import { getPcePsgState } from "../../host/pce-psg-state.js";
|
|
43
|
+
import { decodePs1Spu } from "../../host/ps1-spu-state.js";
|
|
44
|
+
import { decodeN64Ai } from "../../host/n64-ai-state.js";
|
|
42
45
|
import { getMsxAyState } from "../../host/msx-ay-state.js";
|
|
43
46
|
import { decodeGbaSprites, decodeGbaPalette } from "../../host/gba-video-state.js";
|
|
44
47
|
|
|
@@ -339,7 +342,7 @@ export function registerPlatformTools(server, z, sessionKey) {
|
|
|
339
342
|
}
|
|
340
343
|
|
|
341
344
|
unsupported(p, "inspectPalette", {
|
|
342
|
-
reason: "no palette decoder for this platform",
|
|
345
|
+
reason: naReason(p, "inspectPalette") ?? "no palette decoder for this platform",
|
|
343
346
|
alternative: "platform({op:'capabilities'}) to see what's wired",
|
|
344
347
|
});
|
|
345
348
|
};
|
|
@@ -424,7 +427,20 @@ export function registerPlatformTools(server, z, sessionKey) {
|
|
|
424
427
|
if (!ay) throw new Error("getAudioState chip:'ay8910' — no PSG region (load an MSX ROM into the patched blueMSX core).");
|
|
425
428
|
return { platform: "msx", ...ay };
|
|
426
429
|
}
|
|
427
|
-
|
|
430
|
+
if (chip === "spu") {
|
|
431
|
+
// PS1 SPU — 24 ADPCM voices (volume/pitch/ADSR + key-on/off).
|
|
432
|
+
const regs = host.getSpuRegs?.();
|
|
433
|
+
if (!regs) throw new Error("getAudioState chip:'spu' — no SPU region (load a PS1 program into the rebuilt pcsx_rearmed core).");
|
|
434
|
+
return { platform: "ps1", ...decodePs1Spu(regs) };
|
|
435
|
+
}
|
|
436
|
+
if (chip === "ai") {
|
|
437
|
+
// N64 AI — the audio OUTPUT state (sample rate + playing + DMA source). N64
|
|
438
|
+
// audio is RSP-mixed, so there are no per-voice registers to decode.
|
|
439
|
+
const regs = host.getAiRegs?.();
|
|
440
|
+
if (!regs) throw new Error("getAudioState chip:'ai' — no AI region (load an N64 ROM into the rebuilt parallel_n64 core).");
|
|
441
|
+
return { platform: "n64", ...decodeN64Ai(regs) };
|
|
442
|
+
}
|
|
443
|
+
throw new Error(`getAudioState: unknown chip '${chip}'. Use 'nes' (NES 2A03), 'gb' (Game Boy/GBC), 'gba' (GBA), 'dsp' (SNES), 'psg' (Genesis/SMS/GG SN76489), 'ym2612' (Genesis FM), 'sid' (C64), 'mikey' (Lynx), 'pce', 'ay8910' (MSX), or 'spu' (PS1).`);
|
|
428
444
|
}
|
|
429
445
|
|
|
430
446
|
getAudioStateCore = async ({ chip }, callerSessionKey) => jsonContent(readAudioChip(chip, callerSessionKey));
|
|
@@ -701,7 +717,7 @@ export function registerPlatformTools(server, z, sessionKey) {
|
|
|
701
717
|
}
|
|
702
718
|
|
|
703
719
|
unsupported(p, "inspectSprites", {
|
|
704
|
-
reason: "no sprite decoder for this platform",
|
|
720
|
+
reason: naReason(p, "inspectSprites") ?? "no sprite decoder for this platform",
|
|
705
721
|
alternative: "memory({op:'read'}) the raw OAM/sprite-attribute region, or platform({op:'capabilities'}) to see what's wired",
|
|
706
722
|
});
|
|
707
723
|
};
|
|
@@ -813,7 +829,7 @@ export function registerPlatformTools(server, z, sessionKey) {
|
|
|
813
829
|
return emitImage(r.png, `SNES BG map composite (${r.width}×${r.height}, ${r.mapWidth}×${r.mapHeight} tiles, ${r.bpp}bpp, tilemap@0x${tilemapBaseByte.toString(16)}, tiles@0x${tileBaseByte.toString(16)}). ${r.note}`);
|
|
814
830
|
}
|
|
815
831
|
unsupported(p, "inspectBackground", {
|
|
816
|
-
reason: "no background-map snapshotter for this platform",
|
|
832
|
+
reason: naReason(p, "inspectBackground") ?? "no background-map snapshotter for this platform",
|
|
817
833
|
alternative: "background({view:'renderState'}) for the register-level context, or memory({op:'read'}) the raw VRAM/nametable region",
|
|
818
834
|
});
|
|
819
835
|
};
|
|
@@ -2,7 +2,7 @@ import { CORES, listAvailableCores, resolveCore } from "../../cores/registry.js"
|
|
|
2
2
|
import { TOOLCHAINS } from "../../toolchains/registry.js";
|
|
3
3
|
import { getLanguageOptions } from "../../toolchains/index.js";
|
|
4
4
|
import { jsonContent, safeTool } from "../util.js";
|
|
5
|
-
import { CAPABILITIES,
|
|
5
|
+
import { CAPABILITIES, capabilitiesFor, OP_KEYS, naReason } from "../../cores/capabilities.js";
|
|
6
6
|
import { listToolchainsCore, installToolchainCore } from "./toolchain.js";
|
|
7
7
|
import { listPlatformDocsCore, getPlatformDocCore } from "./platform-docs.js";
|
|
8
8
|
|
|
@@ -66,6 +66,30 @@ const PLATFORM_QUIRKS = {
|
|
|
66
66
|
],
|
|
67
67
|
starterSnippets: ["hello_msx.c", "msx_crt0.s"],
|
|
68
68
|
},
|
|
69
|
+
ps1: {
|
|
70
|
+
multiBank: false,
|
|
71
|
+
maxRomBytesPerBank: 0, // disc/EXE based, not a fixed cart
|
|
72
|
+
headerLocation: "PS-EXE: 'PS-X EXE' magic at 0, entry (pc0) at +0x10, load addr (t_addr) at +0x18; 2048-byte header then code",
|
|
73
|
+
notes: [
|
|
74
|
+
"32-bit MIPS R3000 (little-endian), framebuffer GPU. Runs via PCSX-ReARMed with its BUILT-IN HLE BIOS — no firmware file needed, software-rendered. Load a .exe (PS-EXE), or a disc image if you have one.",
|
|
75
|
+
"WORKS NOW: build (buildSource language:'c' → mips-elf-gcc → a PS-EXE the HLE BIOS runs; write GPU/SPU registers directly, no SDK yet), run, frame({op:'screenshot'}), cpu({op:'read'}) (live R3000 registers), cheats, readMemory/writeMemory (system_ram = 2MB main RAM), disasm + decompile (Ghidra MIPS C), the live-debug tools breakpoint({on:'pc'/'write'/'read'}) + watch({on:'range'}), AND getAudioState({chip:'spu'}) (24-voice SPU). MIPS is little-endian here.",
|
|
76
|
+
"The bare build path: a minimal crt0 sets the stack + clears .bss + calls main(); code loads at 0x80010000. No PSn00bSDK runtime yet — drive the GPU at ports 0x1F801810/0x1F801814. The framebuffer renderer has no tile/sprite inspectors — use screenshot + memory.",
|
|
77
|
+
"For higher fidelity (GL hardware renderer) a real PS1 BIOS + the beetle_psx_hw core is the alternative, but the HLE pcsx_rearmed path ships clean with zero firmware.",
|
|
78
|
+
],
|
|
79
|
+
starterSnippets: [],
|
|
80
|
+
},
|
|
81
|
+
n64: {
|
|
82
|
+
multiBank: false,
|
|
83
|
+
maxRomBytesPerBank: 0,
|
|
84
|
+
headerLocation: ".z64 (big-endian) magic 80 37 12 40 at 0; entry point (big-endian word) at +0x08; 0x1000-byte IPL3 bootcode then game code. .v64/.n64 byte orders are auto-normalized.",
|
|
85
|
+
notes: [
|
|
86
|
+
"32-bit MIPS R4300 (big-endian), 3D RDP/RSP. Runs via ParaLLEl-N64 with the glide64 GL renderer — HW-rendered through the OPTIONAL native GL stack (native-gles + webgl-node). Those are optionalDependencies: install them for N64; the other platforms don't need them.",
|
|
87
|
+
"WORKS NOW: build (buildSource language:'c' → mips-elf-gcc → a big-endian MIPS image; bare crt0, no libdragon yet so it's logic-only for now), run, frame({op:'screenshot'}) (real 3D frames, headless via FBO readback), cpu({op:'read'}) (live R4300 registers), cheats, readMemory/writeMemory (system_ram = 8MB RDRAM, 0x80xxxxxx maps to offset 0), disasm + decompile (Ghidra MIPS C), breakpoint + watch (live-debug instrumentation), AND getAudioState({chip:'ai'}) (audio output: sample rate + playing). MIPS is big-endian here.",
|
|
88
|
+
"build caveat: a fully BOOTABLE N64 ROM needs the IPL3 bootcode + a libdragon-style header (libdragon SDK forthcoming). The bare build compiles+links your C to a flat image — great for logic/RE, not yet a self-booting cart. 3D renderer has no tile/sprite inspectors — use screenshot + memory.",
|
|
89
|
+
"If frame() errors with an install hint, the optional GL module isn't installed: `npm install native-gles webgl-node`.",
|
|
90
|
+
],
|
|
91
|
+
starterSnippets: [],
|
|
92
|
+
},
|
|
69
93
|
};
|
|
70
94
|
|
|
71
95
|
/** op:'list' — every platform with core/toolchains/languages/quirks. */
|
|
@@ -129,10 +153,20 @@ export function registerPlatformTools(server, z) {
|
|
|
129
153
|
case "capabilities": {
|
|
130
154
|
if (args.platform) {
|
|
131
155
|
const cap = capabilitiesFor(args.platform);
|
|
132
|
-
if (!cap) throw new Error(`platform({op:'capabilities'}): unknown platform '${args.platform}'. Known: ${
|
|
133
|
-
|
|
156
|
+
if (!cap) throw new Error(`platform({op:'capabilities'}): unknown platform '${args.platform}'. Known: ${Object.keys(CAPABILITIES).join(", ")}.`);
|
|
157
|
+
// Call out WHY each unsupported op is N/A by hardware (a framebuffer/3D
|
|
158
|
+
// renderer has no tile/sprite tables; a disc system has no cart) so an
|
|
159
|
+
// agent reading the manifest sees "can't, because hardware" — not a
|
|
160
|
+
// bare `false` it might mistake for "not built yet".
|
|
161
|
+
const naReasons = {};
|
|
162
|
+
for (const op of OP_KEYS) {
|
|
163
|
+
if (!cap.ops[op]) { const r = naReason(args.platform, op); if (r) naReasons[op] = r; }
|
|
164
|
+
}
|
|
165
|
+
return jsonContent({ platform: args.platform, ...cap, ...(Object.keys(naReasons).length ? { naReasons } : {}) });
|
|
134
166
|
}
|
|
135
|
-
|
|
167
|
+
// All platforms incl. the partial MIPS tier (ps1/n64) — so an agent can
|
|
168
|
+
// discover their capability map and see which ops are live vs not-yet.
|
|
169
|
+
return jsonContent({ platforms: Object.keys(CAPABILITIES), capabilities: CAPABILITIES });
|
|
136
170
|
}
|
|
137
171
|
case "resolve": {
|
|
138
172
|
if (!args.platform) throw new Error("platform({op:'resolve'}): `platform` is required.");
|
package/src/mcp/tools/project.js
CHANGED
|
@@ -546,6 +546,106 @@ const TEMPLATES = {
|
|
|
546
546
|
},
|
|
547
547
|
};
|
|
548
548
|
})(),
|
|
549
|
+
|
|
550
|
+
// ── PlayStation (mips-elf-gcc R3000) — software 3D engine helper lib + examples ──
|
|
551
|
+
// The PS1 is a 3D machine: the helper lib (psx.{h,c}) is a real software 3D
|
|
552
|
+
// pipeline (fixed-point transform + perspective + cull). 4 examples are 3D; the
|
|
553
|
+
// puzzle is 2D (a flat grid is the right idiom even on 3D hardware).
|
|
554
|
+
ps1: (() => {
|
|
555
|
+
const PS1_RUNTIME = [
|
|
556
|
+
{ src: "lib/c/psx.h", dst: "psx.h" },
|
|
557
|
+
{ src: "lib/c/psx.c", dst: "psx.c" },
|
|
558
|
+
];
|
|
559
|
+
const mk = (name, describe) => ({ main: `${name}/main.c`, runtime: PS1_RUNTIME, lang: "C (mips-elf-gcc)", ext: ".exe", describe });
|
|
560
|
+
return {
|
|
561
|
+
default: mk("shmup", "STARFALL — the canonical PS1 3D starter: a vertical shooter where enemies fly in from depth and grow under perspective projection. Exercises the whole 3D helper lib (camera, model transform, culled cubes) + SIO pad + HUD. Same as 'shmup'."),
|
|
562
|
+
shmup: {
|
|
563
|
+
...mk("shmup", "STARFALL — a 3D PlayStation vertical shooter. The playfield recedes into the screen; enemy cubes fly in from the far distance and scale up under perspective as they approach. Stream bullets into Z, AABB collision, xorshift wave spawner, parallax starfield, title/play/game-over with score + lives. Built on the software 3D pipeline (psx_camera/psx_model/psx_quad3d, 16.16 fixed point)."),
|
|
564
|
+
players: "1 (PS1 has two pad ports; a 2P round can hook port 2 later)",
|
|
565
|
+
sram: "none in this starter — PS1 saves go to a Memory Card via the BIOS; the helper lib keeps hi-score in-session (stated in-file).",
|
|
566
|
+
mechanics: ["3D perspective playfield", "depth-scaled enemies", "projectile pools", "wave spawner", "AABB collision", "lives + score", "title/play/game-over state machine"],
|
|
567
|
+
techniques: ["software 3D: fixed-point camera + model transform", "perspective projection + back-face cull", "GPU flat-shaded quads (GP0 0x20/0x60)", "SIO controller polling", "blocky HUD number font"],
|
|
568
|
+
},
|
|
569
|
+
racing: {
|
|
570
|
+
...mk("racing", "POLE BENDER — a 3D PlayStation racer. The road is a ribbon of perspective quads receding to the horizon and bending with a sine curve; you steer a car between the verges as the world scrolls toward you, rival cubes growing as you close on them. The PS1 signature: a real 3D track, not pseudo-3D scaling. Distance score, collision spin-out, title/race/results."),
|
|
571
|
+
players: "1",
|
|
572
|
+
sram: "none (in-session best time/score; Memory Card path is the real-hardware save).",
|
|
573
|
+
mechanics: ["3D curved road (perspective quads)", "throttle/brake/steer physics", "rival traffic + collision", "distance scoring", "title/race/results state machine"],
|
|
574
|
+
techniques: ["receding road segments with depth-driven centerline curve", "no-cull ground-plane quads (psx_quad3d_nc)", "chase camera with downward tilt", "16.16 fixed-point world scroll"],
|
|
575
|
+
},
|
|
576
|
+
platformer: {
|
|
577
|
+
...mk("platformer", "BLOCK HOP — a 3D PlayStation platformer. A cube hero runs and jumps across floating platforms drawn in perspective; gravity + jump physics in 16.16 fixed point, AABB landing on platform tops, coins to collect, a lethal pit, a chase camera that follows the hero. Title/play/game-over, score + lives."),
|
|
578
|
+
players: "1",
|
|
579
|
+
sram: "none (in-session hi-score; Memory Card is the real save path).",
|
|
580
|
+
mechanics: ["gravity + jump physics", "platform-top AABB landing", "coin pickups", "lethal pit + lives", "follow camera", "title/play/game-over"],
|
|
581
|
+
techniques: ["3D platforms as no-cull flat-topped boxes", "culled hero/coin cubes", "smooth-follow camera (psx_camera per frame)", "fixed-point physics"],
|
|
582
|
+
},
|
|
583
|
+
sports: {
|
|
584
|
+
...mk("sports", "SLAM COURT — a 3D PlayStation sports game (air-hockey / pong). A perspective court seen down its length; you control the near paddle, the CPU the far one, and the ball bounces in 3D (X across, Z into the screen), its size changing with depth. First to 7. Title/match/game-over."),
|
|
585
|
+
players: "1 (vs CPU; a 2P split-paddle round can hook port 2 later)",
|
|
586
|
+
sram: "none (match score is in-session).",
|
|
587
|
+
mechanics: ["3D ball physics (X/Z)", "player + CPU paddles", "wall bounces + scoring", "tracking CPU AI", "first-to-7 match", "title/match/game-over"],
|
|
588
|
+
techniques: ["perspective court floor (no-cull quad)", "depth-scaled ball", "paddle/ball cubes at world positions", "capped CPU tracking"],
|
|
589
|
+
},
|
|
590
|
+
puzzle: {
|
|
591
|
+
...mk("puzzle", "DROP GRID — the one 2D game in the PS1 set (a flat grid is the right idiom even on 3D hardware). A falling-block puzzle: colored blocks drop down a well, full rows clear and score, speed ramps up, stack the top = game over. Drawn with the GPU's 2D rectangle primitive. Title/play/game-over."),
|
|
592
|
+
players: "1",
|
|
593
|
+
sram: "none (in-session hi-score).",
|
|
594
|
+
mechanics: ["integer grid model", "falling block move/drop", "full-row clear + scoring", "ramping fall speed", "stack-out game over", "title/play/game-over"],
|
|
595
|
+
techniques: ["2D GPU rectangles (GP0 0x60 variable-size rect)", "integer board logic (no 3D needed)", "SIO pad edge detection", "row-shift clear"],
|
|
596
|
+
},
|
|
597
|
+
};
|
|
598
|
+
})(),
|
|
599
|
+
|
|
600
|
+
// ── Nintendo 64 (mips-elf-gcc R4300) — software 3D engine helper lib + examples ──
|
|
601
|
+
// The N64 was a 3D-first machine, so ALL 5 examples are 3D (even the puzzle —
|
|
602
|
+
// rendered as a 3D well of cubes). Same software-3D lib as PS1 (n64.{h,c}), a
|
|
603
|
+
// framebuffer backend the headless-angrylion core scans out.
|
|
604
|
+
n64: (() => {
|
|
605
|
+
const N64_RUNTIME = [
|
|
606
|
+
{ src: "lib/c/n64.h", dst: "n64.h" },
|
|
607
|
+
{ src: "lib/c/n64.c", dst: "n64.c" },
|
|
608
|
+
];
|
|
609
|
+
const mk = (name, describe) => ({ main: `${name}/main.c`, runtime: N64_RUNTIME, lang: "C (mips-elf-gcc)", ext: ".z64", describe });
|
|
610
|
+
return {
|
|
611
|
+
default: mk("shmup", "STARFALL 64 — the canonical N64 3D starter: a vertical shooter where enemies fly in from depth and scale up under perspective. Exercises the whole software-3D lib (camera, transform, culled cubes) + SI/PIF pad + HUD, presented through angrylion's VI scanout. Same as 'shmup'."),
|
|
612
|
+
shmup: {
|
|
613
|
+
...mk("shmup", "STARFALL 64 — a 3D Nintendo 64 vertical shooter (the N64 twin of the PS1 STARFALL). Enemy cubes fly in from the far distance and grow under perspective; stream bullets into Z, AABB collision, xorshift wave spawner, starfield, title/play/game-over with score + lives. Software 3D pipeline (n64_camera/n64_model/n64_quad3d) rendered to an RDRAM framebuffer the headless-angrylion core scans out."),
|
|
614
|
+
players: "1 (N64 has 4 controller ports; 2-4P can hook the other ports later)",
|
|
615
|
+
sram: "none in this starter — N64 saves go to Controller Pak / EEPROM / SRAM via the PI; the lib keeps hi-score in-session (stated in-file).",
|
|
616
|
+
mechanics: ["3D perspective playfield", "depth-scaled enemies", "projectile pools", "wave spawner", "AABB collision", "lives + score", "title/play/game-over state machine"],
|
|
617
|
+
techniques: ["software 3D: fixed-point camera + model transform", "perspective projection + back-face cull", "software triangle rasterizer → RDRAM framebuffer", "VI scanout (correct VI register setup)", "SI/PIF controller poll"],
|
|
618
|
+
},
|
|
619
|
+
racing: {
|
|
620
|
+
...mk("racing", "POLE BENDER 64 — a 3D Nintendo 64 racer. A perspective road ribbon recedes to the horizon and bends with a sine curve; steer between the verges as the world scrolls toward you, rival cubes growing as you close. Distance score, collision spin-out, title/race/results. The N64's 3D heritage on display."),
|
|
621
|
+
players: "1",
|
|
622
|
+
sram: "none (in-session best; Controller Pak/EEPROM is the real-hardware save).",
|
|
623
|
+
mechanics: ["3D curved road (perspective quads)", "throttle/brake/steer", "rival traffic + collision", "distance scoring", "title/race/results"],
|
|
624
|
+
techniques: ["receding road segments with depth-driven curve", "no-cull ground-plane quads", "chase camera", "16.16 fixed-point world scroll"],
|
|
625
|
+
},
|
|
626
|
+
platformer: {
|
|
627
|
+
...mk("platformer", "BLOCK HOP 64 — a 3D Nintendo 64 platformer. A cube hero runs and jumps across floating platforms in perspective; gravity + jump physics (16.16 fixed point), AABB landing on platform tops, coins, a lethal pit, a follow camera. Title/play/game-over, score + lives."),
|
|
628
|
+
players: "1",
|
|
629
|
+
sram: "none (in-session hi-score).",
|
|
630
|
+
mechanics: ["gravity + jump physics", "platform-top AABB landing", "coin pickups", "lethal pit + lives", "follow camera", "title/play/game-over"],
|
|
631
|
+
techniques: ["3D platforms as flat-topped boxes", "culled hero/coin cubes", "smooth-follow camera", "fixed-point physics"],
|
|
632
|
+
},
|
|
633
|
+
sports: {
|
|
634
|
+
...mk("sports", "SLAM COURT 64 — a 3D Nintendo 64 sports game (air-hockey / pong). A perspective court down its length; you control the near paddle, the CPU the far, the ball bounces in 3D (X across, Z into the screen) and scales with depth. First to 7. Title/match/game-over."),
|
|
635
|
+
players: "1 (vs CPU; the N64's extra ports can host 2-4P later)",
|
|
636
|
+
sram: "none (match score is in-session).",
|
|
637
|
+
mechanics: ["3D ball physics (X/Z)", "player + CPU paddles", "wall bounces + scoring", "tracking CPU AI", "first-to-7", "title/match/game-over"],
|
|
638
|
+
techniques: ["perspective court floor (no-cull quad)", "depth-scaled ball", "paddle/ball cubes", "capped CPU tracking"],
|
|
639
|
+
},
|
|
640
|
+
puzzle: {
|
|
641
|
+
...mk("puzzle", "DROP GRID 64 — a 3D Nintendo 64 falling-block puzzle. Unlike the flat-2D PS1 puzzle, this is rendered in 3D (the N64 was a 3D-first machine): the well is a perspective box of cube walls and the blocks are shaded cubes you watch fall in depth. Move/drop, full-row clear + scoring, ramping speed, stack-out = game over. Title/play/game-over."),
|
|
642
|
+
players: "1",
|
|
643
|
+
sram: "none (in-session hi-score).",
|
|
644
|
+
mechanics: ["integer grid model", "falling block move/drop", "full-row clear + scoring", "ramping fall speed", "stack-out game over", "title/play/game-over"],
|
|
645
|
+
techniques: ["3D-rendered well (cube walls + floor)", "blocks as shaded 3D cubes", "tilted camera into the well", "integer board logic", "SI/PIF pad edge detection"],
|
|
646
|
+
},
|
|
647
|
+
};
|
|
648
|
+
})(),
|
|
549
649
|
};
|
|
550
650
|
// R37: GBC has its own scaffold tree at examples/gbc/templates/ +
|
|
551
651
|
// src/platforms/gbc/lib/c/. Same runtime files as GB (the APU + Z80 +
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
11
11
|
import { getHost } from "../state.js";
|
|
12
12
|
import { jsonContent, safeTool, unsupported } from "../util.js";
|
|
13
|
+
import { naReason } from "../../cores/capabilities.js";
|
|
13
14
|
import { inspectBackgroundMapCore } from "./platform-tools.js";
|
|
14
15
|
import { whichTilesAreRenderedCore } from "./which-tiles.js";
|
|
15
16
|
|
|
@@ -533,7 +534,7 @@ export async function getRenderingContextCore({ platform, area = "all", sessionK
|
|
|
533
534
|
case "msx": return msxContext(host, area);
|
|
534
535
|
default:
|
|
535
536
|
unsupported(p, "renderingContext", {
|
|
536
|
-
reason: "no rendering-context decoder for this platform",
|
|
537
|
+
reason: naReason(p, "renderingContext") ?? "no rendering-context decoder for this platform",
|
|
537
538
|
alternative: "platform({op:'capabilities'}) to see what's wired",
|
|
538
539
|
});
|
|
539
540
|
}
|
|
@@ -603,7 +603,7 @@ export function registerToolchainTools(server, z, sessionKey) {
|
|
|
603
603
|
}
|
|
604
604
|
|
|
605
605
|
const host = resetHost(sessionKey);
|
|
606
|
-
await host.loadCore(resolved.jsPath, resolved.wasmPath);
|
|
606
|
+
await host.loadCore(resolved.jsPath, resolved.wasmPath, { hwRender: resolved.hwRender });
|
|
607
607
|
// Pass projectName as a virtualName so the playtest window titles itself
|
|
608
608
|
// with the game name (the SDL window reads host.status.mediaPath). Keep
|
|
609
609
|
// a platform-correct extension so shared cores still resolve the system.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/* n64.c — N64 helpers + software 3D pipeline (see n64.h). Framebuffer backend. */
|
|
2
|
+
#include "n64.h"
|
|
3
|
+
|
|
4
|
+
/* Two 320x240x16bpp framebuffers in RDRAM (double-buffered), cached kseg0. */
|
|
5
|
+
#define FB0 0xA0100000u /* uncached kseg1 — writes go straight to RDRAM */
|
|
6
|
+
#define FB1 0xA0120000u
|
|
7
|
+
static volatile unsigned short *fb; /* the back buffer we draw into */
|
|
8
|
+
static unsigned int front;
|
|
9
|
+
|
|
10
|
+
/* VI registers (0xA4400000). */
|
|
11
|
+
#define VI(n) (*(volatile unsigned int*)(0xA4400000u + (n)*4))
|
|
12
|
+
#define VI_STATUS 0
|
|
13
|
+
#define VI_ORIGIN 1
|
|
14
|
+
#define VI_WIDTH 2
|
|
15
|
+
#define VI_V_CURRENT 4
|
|
16
|
+
#define VI_H_START 9
|
|
17
|
+
#define VI_V_START 10
|
|
18
|
+
#define VI_X_SCALE 12
|
|
19
|
+
#define VI_Y_SCALE 13
|
|
20
|
+
|
|
21
|
+
void n64_init(void)
|
|
22
|
+
{
|
|
23
|
+
/* NTSC 320x240 16bpp — register indices per the N64 VI map:
|
|
24
|
+
0=STATUS 1=ORIGIN 2=WIDTH 3=V_INTR 4=CURRENT 5=BURST 6=V_SYNC 7=H_SYNC
|
|
25
|
+
8=LEAP 9=H_START 10=V_START 11=V_BURST 12=X_SCALE 13=Y_SCALE. */
|
|
26
|
+
VI(0) = 0x0000320E; /* STATUS: 16bpp(type=2) + AA + dither + pixel_advance */
|
|
27
|
+
VI(2) = 320; /* WIDTH */
|
|
28
|
+
VI(3) = 2; /* V_INTR */
|
|
29
|
+
VI(4) = 0; /* CURRENT */
|
|
30
|
+
VI(5) = 0x03E52239; /* BURST (NTSC) */
|
|
31
|
+
VI(6) = 0x0000020D; /* V_SYNC = 525 */
|
|
32
|
+
VI(7) = 0x00000C15; /* H_SYNC */
|
|
33
|
+
VI(8) = 0x0C150C15; /* LEAP */
|
|
34
|
+
VI(9) = 0x006C02EC; /* H_START: 108..748 */
|
|
35
|
+
VI(10) = 0x002501FF; /* V_START: 37..511 */
|
|
36
|
+
VI(11) = 0x000E0204; /* V_BURST */
|
|
37
|
+
VI(12) = 0x00000200; /* X_SCALE (320) */
|
|
38
|
+
VI(13) = 0x00000400; /* Y_SCALE (240) */
|
|
39
|
+
front = FB0;
|
|
40
|
+
fb = (volatile unsigned short *)FB1;
|
|
41
|
+
VI(1) = FB0 & 0x1FFFFFFF; /* ORIGIN */
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static inline void putpx(int x, int y, unsigned short c)
|
|
45
|
+
{
|
|
46
|
+
if ((unsigned)x < SCREEN_W && (unsigned)y < SCREEN_H) fb[y * SCREEN_W + x] = c;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
void n64_clear(unsigned short col)
|
|
50
|
+
{
|
|
51
|
+
int i; for (i = 0; i < SCREEN_W * SCREEN_H; i++) fb[i] = col;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
void n64_rect(int x, int y, int w, int h, unsigned short col)
|
|
55
|
+
{
|
|
56
|
+
int r, c;
|
|
57
|
+
for (r = 0; r < h; r++) for (c = 0; c < w; c++) putpx(x + c, y + r, col);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* edge function for the rasterizer */
|
|
61
|
+
static inline int edge(int ax, int ay, int bx, int by, int cx, int cy)
|
|
62
|
+
{ return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); }
|
|
63
|
+
|
|
64
|
+
void n64_tri2d(int x0,int y0,int x1,int y1,int x2,int y2,unsigned short col)
|
|
65
|
+
{
|
|
66
|
+
int minx = x0, maxx = x0, miny = y0, maxy = y0, px, py, w0, w1, w2, area;
|
|
67
|
+
if (x1 < minx) minx = x1; if (x1 > maxx) maxx = x1;
|
|
68
|
+
if (x2 < minx) minx = x2; if (x2 > maxx) maxx = x2;
|
|
69
|
+
if (y1 < miny) miny = y1; if (y1 > maxy) maxy = y1;
|
|
70
|
+
if (y2 < miny) miny = y2; if (y2 > maxy) maxy = y2;
|
|
71
|
+
if (minx < 0) minx = 0; if (miny < 0) miny = 0;
|
|
72
|
+
if (maxx >= SCREEN_W) maxx = SCREEN_W - 1; if (maxy >= SCREEN_H) maxy = SCREEN_H - 1;
|
|
73
|
+
area = edge(x0, y0, x1, y1, x2, y2);
|
|
74
|
+
if (area == 0) return;
|
|
75
|
+
for (py = miny; py <= maxy; py++) {
|
|
76
|
+
for (px = minx; px <= maxx; px++) {
|
|
77
|
+
w0 = edge(x1, y1, x2, y2, px, py);
|
|
78
|
+
w1 = edge(x2, y2, x0, y0, px, py);
|
|
79
|
+
w2 = edge(x0, y0, x1, y1, px, py);
|
|
80
|
+
if (area > 0) { if (w0 >= 0 && w1 >= 0 && w2 >= 0) fb[py * SCREEN_W + px] = col; }
|
|
81
|
+
else { if (w0 <= 0 && w1 <= 0 && w2 <= 0) fb[py * SCREEN_W + px] = col; }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
void n64_flip(void)
|
|
87
|
+
{
|
|
88
|
+
/* swap buffers + point VI at the freshly drawn one, then wait for a frame. */
|
|
89
|
+
unsigned int newfront = (unsigned int)(unsigned long)fb;
|
|
90
|
+
VI(1) = newfront & 0x1FFFFFFF;
|
|
91
|
+
fb = (volatile unsigned short *)(front);
|
|
92
|
+
front = newfront;
|
|
93
|
+
{ volatile int i; for (i = 0; i < 200000; i++) { } }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ── input: read controller port 0 via the SI/PIF. The PIF command 0x01 polls
|
|
97
|
+
the pad; the 16-bit button word is at PIF RAM. We use the simple JoyBus poll. ── */
|
|
98
|
+
static unsigned int read_pad(void)
|
|
99
|
+
{
|
|
100
|
+
volatile unsigned int *pif = (volatile unsigned int *)0xBFC007C0; /* PIF RAM */
|
|
101
|
+
volatile unsigned int *si = (volatile unsigned int *)0xA4800000;
|
|
102
|
+
unsigned int buttons;
|
|
103
|
+
/* command block: read controller 0 (1 byte cmd 0x01, 1 byte send, ...) */
|
|
104
|
+
pif[0] = 0xFF010401; pif[1] = 0xFFFFFFFF; pif[2] = 0xFFFFFFFF;
|
|
105
|
+
pif[3] = 0xFE000000; pif[4] = 0; pif[5] = 0; pif[6] = 0; pif[7] = 1;
|
|
106
|
+
si[1] = 0x1FC007C0; /* SI_PIF_ADDR_RD64B: kick the PIF read */
|
|
107
|
+
{ volatile int t; for (t = 0; t < 5000; t++) { } }
|
|
108
|
+
buttons = pif[1] >> 16; /* the button half-word */
|
|
109
|
+
return (~0u) & buttons; /* JoyBus buttons are active-high already */
|
|
110
|
+
}
|
|
111
|
+
unsigned int n64_pad(void) { return read_pad(); }
|
|
112
|
+
int n64_pressed(unsigned int mask) { return (n64_pad() & mask) ? 1 : 0; }
|
|
113
|
+
|
|
114
|
+
/* ── trig (shared with PS1 lib: 256-step binary angle) ── */
|
|
115
|
+
static const short SINTAB[64] = {
|
|
116
|
+
0,804,1608,2410,3212,4011,4808,5602,6393,7179,7962,8739,9512,10278,11039,11793,
|
|
117
|
+
12539,13279,14010,14732,15446,16151,16846,17530,18204,18868,19519,20159,20787,21403,
|
|
118
|
+
22005,22594,23170,23731,24279,24811,25329,25832,26319,26790,27245,27683,28105,28510,
|
|
119
|
+
28898,29268,29621,29956,30273,30571,30852,31113,31356,31580,31785,31971,32137,32285,
|
|
120
|
+
32412,32521,32609,32678,32728,32757 };
|
|
121
|
+
static fix sin_lut(int idx) {
|
|
122
|
+
int q = (idx >> 6) & 3, p = idx & 63; int v;
|
|
123
|
+
if (q == 0) v = SINTAB[p]; else if (q == 1) v = SINTAB[63 - p];
|
|
124
|
+
else if (q == 2) v = -SINTAB[p]; else v = -SINTAB[63 - p];
|
|
125
|
+
return ((fix)v << 16) / 32768;
|
|
126
|
+
}
|
|
127
|
+
fix n64_sin(fix a) { return sin_lut((F2I(a)) & 255); }
|
|
128
|
+
fix n64_cos(fix a) { return sin_lut((F2I(a) + 64) & 255); }
|
|
129
|
+
|
|
130
|
+
/* ── camera + model (identical to the PS1 lib) ── */
|
|
131
|
+
static fix cam_x, cam_y, cam_z, cam_cy, cam_sy, cam_cp, cam_sp;
|
|
132
|
+
static fix mdl_x, mdl_y, mdl_z, mdl_cy, mdl_sy;
|
|
133
|
+
void n64_camera(fix ex, fix ey, fix ez, fix yaw, fix pitch)
|
|
134
|
+
{ cam_x=ex;cam_y=ey;cam_z=ez; cam_cy=n64_cos(yaw);cam_sy=n64_sin(yaw); cam_cp=n64_cos(pitch);cam_sp=n64_sin(pitch); }
|
|
135
|
+
void n64_model(fix tx, fix ty, fix tz, fix yaw)
|
|
136
|
+
{ mdl_x=tx;mdl_y=ty;mdl_z=tz; mdl_cy=n64_cos(yaw);mdl_sy=n64_sin(yaw); }
|
|
137
|
+
|
|
138
|
+
static Vec3 to_cam(Vec3 v)
|
|
139
|
+
{
|
|
140
|
+
fix mx = FMUL(v.x, mdl_cy) + FMUL(v.z, mdl_sy);
|
|
141
|
+
fix mz = -FMUL(v.x, mdl_sy) + FMUL(v.z, mdl_cy);
|
|
142
|
+
fix wx = mx + mdl_x, wy = v.y + mdl_y, wz = mz + mdl_z;
|
|
143
|
+
fix rx = wx - cam_x, ry = wy - cam_y, rz = wz - cam_z;
|
|
144
|
+
fix cx = FMUL(rx, cam_cy) - FMUL(rz, cam_sy);
|
|
145
|
+
fix cz = FMUL(rx, cam_sy) + FMUL(rz, cam_cy);
|
|
146
|
+
fix cy = FMUL(ry, cam_cp) - FMUL(cz, cam_sp);
|
|
147
|
+
fix cz2 = FMUL(ry, cam_sp) + FMUL(cz, cam_cp);
|
|
148
|
+
Vec3 o; o.x = cx; o.y = cy; o.z = cz2; return o;
|
|
149
|
+
}
|
|
150
|
+
#define NEAR FIXF(0.5f)
|
|
151
|
+
#define FOV FIX(220)
|
|
152
|
+
static int project(Vec3 c, int *sx, int *sy)
|
|
153
|
+
{
|
|
154
|
+
if (c.z <= NEAR) return 0;
|
|
155
|
+
*sx = (SCREEN_W / 2) + F2I(FDIV(FMUL(c.x, FOV), c.z));
|
|
156
|
+
*sy = (SCREEN_H / 2) - F2I(FDIV(FMUL(c.y, FOV), c.z));
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
159
|
+
void n64_tri3d(Vec3 a, Vec3 b, Vec3 c, unsigned short col)
|
|
160
|
+
{
|
|
161
|
+
Vec3 ca=to_cam(a), cb=to_cam(b), cc=to_cam(c);
|
|
162
|
+
int x0,y0,x1,y1,x2,y2;
|
|
163
|
+
if (!project(ca,&x0,&y0)||!project(cb,&x1,&y1)||!project(cc,&x2,&y2)) return;
|
|
164
|
+
if ((x1-x0)*(y2-y0)-(x2-x0)*(y1-y0) <= 0) return; /* back-face cull */
|
|
165
|
+
n64_tri2d(x0,y0,x1,y1,x2,y2,col);
|
|
166
|
+
}
|
|
167
|
+
void n64_quad3d(Vec3 a, Vec3 b, Vec3 c, Vec3 d, unsigned short col)
|
|
168
|
+
{ n64_tri3d(a,b,c,col); n64_tri3d(a,c,d,col); }
|
|
169
|
+
void n64_tri3d_nc(Vec3 a, Vec3 b, Vec3 c, unsigned short col)
|
|
170
|
+
{
|
|
171
|
+
Vec3 ca=to_cam(a), cb=to_cam(b), cc=to_cam(c);
|
|
172
|
+
int x0,y0,x1,y1,x2,y2;
|
|
173
|
+
if (!project(ca,&x0,&y0)||!project(cb,&x1,&y1)||!project(cc,&x2,&y2)) return;
|
|
174
|
+
n64_tri2d(x0,y0,x1,y1,x2,y2,col);
|
|
175
|
+
}
|
|
176
|
+
void n64_quad3d_nc(Vec3 a, Vec3 b, Vec3 c, Vec3 d, unsigned short col)
|
|
177
|
+
{ n64_tri3d_nc(a,b,c,col); n64_tri3d_nc(a,c,d,col); }
|
|
178
|
+
|
|
179
|
+
/* ── RNG ── */
|
|
180
|
+
static unsigned int rng = 0x12345678u;
|
|
181
|
+
void n64_srand(unsigned int s) { rng = s ? s : 1; }
|
|
182
|
+
unsigned int n64_rand(void) { unsigned int x=rng; x^=x<<13; x^=x>>17; x^=x<<5; return rng=x; }
|
|
183
|
+
|
|
184
|
+
/* ── HUD number (3x5 cells scaled) ── */
|
|
185
|
+
static const unsigned char DIG[10][5] = {
|
|
186
|
+
{0x7,0x5,0x5,0x5,0x7},{0x2,0x6,0x2,0x2,0x7},{0x7,0x1,0x7,0x4,0x7},{0x7,0x1,0x3,0x1,0x7},
|
|
187
|
+
{0x5,0x5,0x7,0x1,0x1},{0x7,0x4,0x7,0x1,0x7},{0x7,0x4,0x7,0x5,0x7},{0x7,0x1,0x1,0x1,0x1},
|
|
188
|
+
{0x7,0x5,0x7,0x5,0x7},{0x7,0x5,0x7,0x1,0x7} };
|
|
189
|
+
static void digit(int x,int y,int d,unsigned short col)
|
|
190
|
+
{ int r,c; for(r=0;r<5;r++)for(c=0;c<3;c++) if(DIG[d][r]&(1<<(2-c))) n64_rect(x+c*3,y+r*3,3,3,col); }
|
|
191
|
+
void n64_number(int x, int y, unsigned int value, unsigned short col)
|
|
192
|
+
{
|
|
193
|
+
char buf[10]; int n=0,i;
|
|
194
|
+
if(!value)buf[n++]=0; while(value&&n<10){buf[n++]=value%10;value/=10;}
|
|
195
|
+
for(i=0;i<n;i++) digit(x+(n-1-i)*12,y,buf[i],col);
|
|
196
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* n64.h — N64 (R4300) helper lib with a software 3D pipeline, for romdev.
|
|
2
|
+
*
|
|
3
|
+
* The N64 is a 3D machine; this is a real software 3D engine (identical fixed-point
|
|
4
|
+
* math to the PS1 lib, so games port directly) with an N64 framebuffer backend: a
|
|
5
|
+
* 320x240 16-bit (RGBA5551) framebuffer in RDRAM that the Video Interface scans out,
|
|
6
|
+
* and a software triangle rasterizer (the RDP is far more complex; direct FB writes
|
|
7
|
+
* are portable + reliable). Big-endian.
|
|
8
|
+
*
|
|
9
|
+
* Build: build({ platform:"n64", language:"c" }) → a self-booting .z64 (our clean
|
|
10
|
+
* IPL3 DMAs the game to RDRAM and jumps in). main() loops forever.
|
|
11
|
+
*/
|
|
12
|
+
#ifndef ROMDEV_N64_H
|
|
13
|
+
#define ROMDEV_N64_H
|
|
14
|
+
|
|
15
|
+
typedef int fix; /* 16.16 */
|
|
16
|
+
#define FIX(n) ((fix)((n) << 16))
|
|
17
|
+
#define FIXF(f) ((fix)((f) * 65536.0f))
|
|
18
|
+
#define FMUL(a,b) ((fix)(((long long)(a) * (b)) >> 16))
|
|
19
|
+
#define FDIV(a,b) ((fix)(((long long)(a) << 16) / (b)))
|
|
20
|
+
#define F2I(a) ((a) >> 16)
|
|
21
|
+
|
|
22
|
+
typedef struct { fix x, y, z; } Vec3;
|
|
23
|
+
|
|
24
|
+
#define SCREEN_W 320
|
|
25
|
+
#define SCREEN_H 240
|
|
26
|
+
|
|
27
|
+
/* RGBA5551 color (the VI framebuffer format): 5-5-5-1. */
|
|
28
|
+
#define RGB(r,g,b) ((unsigned short)((((r)>>3)<<11)|(((g)>>3)<<6)|(((b)>>3)<<1)|1))
|
|
29
|
+
|
|
30
|
+
/* controller digital buttons (active-high after read). */
|
|
31
|
+
#define PAD_A 0x8000
|
|
32
|
+
#define PAD_B 0x4000
|
|
33
|
+
#define PAD_Z 0x2000
|
|
34
|
+
#define PAD_START 0x1000
|
|
35
|
+
#define PAD_UP 0x0800
|
|
36
|
+
#define PAD_DOWN 0x0400
|
|
37
|
+
#define PAD_LEFT 0x0200
|
|
38
|
+
#define PAD_RIGHT 0x0100
|
|
39
|
+
#define PAD_L 0x0020
|
|
40
|
+
#define PAD_R 0x0010
|
|
41
|
+
|
|
42
|
+
/* ── framebuffer / 2D ── */
|
|
43
|
+
void n64_init(void);
|
|
44
|
+
void n64_clear(unsigned short col);
|
|
45
|
+
void n64_rect(int x, int y, int w, int h, unsigned short col);
|
|
46
|
+
void n64_tri2d(int x0,int y0,int x1,int y1,int x2,int y2,unsigned short col);
|
|
47
|
+
void n64_flip(void); /* present the framebuffer (swap + wait vblank) */
|
|
48
|
+
|
|
49
|
+
/* ── input ── */
|
|
50
|
+
unsigned int n64_pad(void);
|
|
51
|
+
int n64_pressed(unsigned int mask);
|
|
52
|
+
|
|
53
|
+
/* ── 3D pipeline (same API as the PS1 lib) ── */
|
|
54
|
+
void n64_camera(fix ex, fix ey, fix ez, fix yaw, fix pitch);
|
|
55
|
+
void n64_model(fix tx, fix ty, fix tz, fix yaw);
|
|
56
|
+
void n64_tri3d(Vec3 a, Vec3 b, Vec3 c, unsigned short col);
|
|
57
|
+
void n64_quad3d(Vec3 a, Vec3 b, Vec3 c, Vec3 d, unsigned short col);
|
|
58
|
+
void n64_tri3d_nc(Vec3 a, Vec3 b, Vec3 c, unsigned short col);
|
|
59
|
+
void n64_quad3d_nc(Vec3 a, Vec3 b, Vec3 c, Vec3 d, unsigned short col);
|
|
60
|
+
|
|
61
|
+
/* ── misc ── */
|
|
62
|
+
fix n64_sin(fix a);
|
|
63
|
+
fix n64_cos(fix a);
|
|
64
|
+
unsigned int n64_rand(void);
|
|
65
|
+
void n64_srand(unsigned int seed);
|
|
66
|
+
void n64_number(int x, int y, unsigned int value, unsigned short col);
|
|
67
|
+
|
|
68
|
+
#endif
|