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
@@ -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
@@ -0,0 +1,200 @@
1
+ /* psx.c — PS1 helpers + software 3D pipeline (see psx.h). */
2
+ #include "psx.h"
3
+
4
+ /* ── GPU bring-up ── */
5
+ void psx_init(void)
6
+ {
7
+ GP1 = 0x00000000u; /* reset */
8
+ GP1 = 0x08000001u; /* display mode 320x240 NTSC 15bpp */
9
+ GP1 = 0x03000000u; /* display enable (arg 0 = on) */
10
+ GP1 = 0x05000000u; /* display start (0,0) */
11
+ GP1 = 0x06000000u | (0x260u | ((0x260u + 320u*8u) << 12));
12
+ GP1 = 0x07000000u | (16u | ((16u + 240u) << 10));
13
+ GP0 = 0xE3000000u; /* draw area TL (0,0) */
14
+ GP0 = 0xE4000000u | (319u) | (239u << 10); /* draw area BR */
15
+ GP0 = 0xE5000000u; /* draw offset (0,0) */
16
+ }
17
+
18
+ void psx_clear(unsigned int bgr) {
19
+ /* GP0 0x02 = fast fill of a VRAM rectangle (ignores draw area, no blending). */
20
+ GP0 = 0x02000000u | (bgr & 0x00FFFFFFu);
21
+ GP0 = 0; /* (x,y) = (0,0) */
22
+ GP0 = (240u << 16) | 320u; /* (h,w) */
23
+ }
24
+
25
+ void psx_rect(int x, int y, int w, int h, unsigned int bgr)
26
+ {
27
+ /* GP0 0x60 = monochrome variable-size RECTANGLE: color, (y,x) corner, (h,w) size. */
28
+ GP0 = 0x60000000u | (bgr & 0x00FFFFFFu);
29
+ GP0 = (((unsigned)y & 0xFFFF) << 16) | ((unsigned)x & 0xFFFF);
30
+ GP0 = (((unsigned)h & 0xFFFF) << 16) | ((unsigned)w & 0xFFFF);
31
+ }
32
+
33
+ void psx_tri2d(int x0,int y0,int x1,int y1,int x2,int y2,unsigned int bgr)
34
+ {
35
+ GP0 = 0x20000000u | (bgr & 0x00FFFFFFu); /* flat opaque triangle */
36
+ GP0 = ((unsigned)(y0)<<16)|((unsigned)(x0)&0xFFFF);
37
+ GP0 = ((unsigned)(y1)<<16)|((unsigned)(x1)&0xFFFF);
38
+ GP0 = ((unsigned)(y2)<<16)|((unsigned)(x2)&0xFFFF);
39
+ }
40
+
41
+ void psx_vsync(void) { volatile int i; for (i = 0; i < 60000; i++) { } }
42
+
43
+ /* ── input: read controller port 0 via the SIO (the way real PS1 homebrew does;
44
+ the HLE core feeds host input into this SIO emulation). The digital pad reply
45
+ is 0x5A then two button bytes (active-LOW). We return active-HIGH. ── */
46
+ #define SIO_DATA (*(volatile unsigned char*)0x1F801040)
47
+ #define SIO_STAT (*(volatile unsigned short*)0x1F801044)
48
+ #define SIO_CTRL (*(volatile unsigned short*)0x1F80104A)
49
+ #define SIO_BAUD (*(volatile unsigned short*)0x1F80104E)
50
+ #define SIO_MODE (*(volatile unsigned short*)0x1F801048)
51
+
52
+ static unsigned char sio_xfer(unsigned char out) {
53
+ volatile int t;
54
+ while (!(SIO_STAT & 0x4)) { } /* TX ready */
55
+ SIO_DATA = out;
56
+ for (t = 0; t < 200; t++) { } /* settle */
57
+ while (!(SIO_STAT & 0x2)) { } /* RX ready */
58
+ return SIO_DATA;
59
+ }
60
+
61
+ unsigned int psx_pad(void) {
62
+ unsigned char b0, b1; volatile int t;
63
+ /* init SIO for controller (8N1, ~250kbps) */
64
+ SIO_MODE = 0x000D; SIO_BAUD = 0x0088; SIO_CTRL = 0x1003; /* TX/RX en + DTR + port0 */
65
+ sio_xfer(0x01); /* address: controller */
66
+ if (sio_xfer(0x42) == 0xFF) { SIO_CTRL = 0; return 0; } /* request data; 0xFF=no pad */
67
+ sio_xfer(0x00); /* discard 0x5A */
68
+ b0 = sio_xfer(0x00); /* buttons lo (active-low) */
69
+ b1 = sio_xfer(0x00); /* buttons hi (active-low) */
70
+ SIO_CTRL = 0; /* deselect */
71
+ for (t = 0; t < 100; t++) { }
72
+ return (~((unsigned int)b0 | ((unsigned int)b1 << 8))) & 0xFFFF; /* → active-high */
73
+ }
74
+ int psx_pressed(unsigned int mask) { return (psx_pad() & mask) ? 1 : 0; }
75
+
76
+ /* ── trig: 256-entry quarter→full sine, arg in "binary angle" 16.16 where
77
+ FIX(256) = one full turn. ── */
78
+ static const short SINTAB[64] = {
79
+ 0,804,1608,2410,3212,4011,4808,5602,6393,7179,7962,8739,9512,10278,11039,11793,
80
+ 12539,13279,14010,14732,15446,16151,16846,17530,18204,18868,19519,20159,20787,21403,
81
+ 22005,22594,23170,23731,24279,24811,25329,25832,26319,26790,27245,27683,28105,28510,
82
+ 28898,29268,29621,29956,30273,30571,30852,31113,31356,31580,31785,31971,32137,32285,
83
+ 32412,32521,32609,32678,32728,32757 };
84
+ static fix sin_lut(int idx) { /* idx 0..255 → 16.16 sine */
85
+ int q = (idx >> 6) & 3, p = idx & 63; int v;
86
+ if (q == 0) v = SINTAB[p];
87
+ else if (q == 1) v = SINTAB[63 - p];
88
+ else if (q == 2) v = -SINTAB[p];
89
+ else v = -SINTAB[63 - p];
90
+ return ((fix)v << 16) / 32768; /* normalize to 16.16 (~1.0 peak) */
91
+ }
92
+ fix psx_sin(fix a) { return sin_lut((F2I(a)) & 255); }
93
+ fix psx_cos(fix a) { return sin_lut((F2I(a) + 64) & 255); }
94
+
95
+ /* ── camera + model state ── */
96
+ static fix cam_x, cam_y, cam_z, cam_cy, cam_sy, cam_cp, cam_sp;
97
+ static fix mdl_x, mdl_y, mdl_z, mdl_cy, mdl_sy;
98
+
99
+ void psx_camera(fix ex, fix ey, fix ez, fix yaw, fix pitch)
100
+ {
101
+ cam_x = ex; cam_y = ey; cam_z = ez;
102
+ cam_cy = psx_cos(yaw); cam_sy = psx_sin(yaw);
103
+ cam_cp = psx_cos(pitch); cam_sp = psx_sin(pitch);
104
+ }
105
+ void psx_model(fix tx, fix ty, fix tz, fix yaw)
106
+ {
107
+ mdl_x = tx; mdl_y = ty; mdl_z = tz;
108
+ mdl_cy = psx_cos(yaw); mdl_sy = psx_sin(yaw);
109
+ }
110
+
111
+ /* model-space vertex → camera space (16.16). */
112
+ static Vec3 to_cam(Vec3 v)
113
+ {
114
+ /* model yaw (around Y) + translate */
115
+ fix mx = FMUL(v.x, mdl_cy) + FMUL(v.z, mdl_sy);
116
+ fix mz = -FMUL(v.x, mdl_sy) + FMUL(v.z, mdl_cy);
117
+ fix wx = mx + mdl_x, wy = v.y + mdl_y, wz = mz + mdl_z;
118
+ /* world → relative to camera */
119
+ fix rx = wx - cam_x, ry = wy - cam_y, rz = wz - cam_z;
120
+ /* camera yaw */
121
+ fix cx = FMUL(rx, cam_cy) - FMUL(rz, cam_sy);
122
+ fix cz = FMUL(rx, cam_sy) + FMUL(rz, cam_cy);
123
+ /* camera pitch */
124
+ fix cy = FMUL(ry, cam_cp) - FMUL(cz, cam_sp);
125
+ fix cz2 = FMUL(ry, cam_sp) + FMUL(cz, cam_cp);
126
+ Vec3 o; o.x = cx; o.y = cy; o.z = cz2; return o;
127
+ }
128
+
129
+ /* perspective project camera-space → screen. returns 0 if behind/near plane. */
130
+ #define NEAR FIXF(0.5f)
131
+ #define FOV FIX(220) /* focal length in pixels-ish */
132
+ static int project(Vec3 c, int *sx, int *sy)
133
+ {
134
+ if (c.z <= NEAR) return 0;
135
+ fix sxf = FDIV(FMUL(c.x, FOV), c.z);
136
+ fix syf = FDIV(FMUL(c.y, FOV), c.z);
137
+ *sx = (SCREEN_W / 2) + F2I(sxf);
138
+ *sy = (SCREEN_H / 2) - F2I(syf);
139
+ return 1;
140
+ }
141
+
142
+ fix psx_depth3(Vec3 a, Vec3 b, Vec3 c)
143
+ { Vec3 ca=to_cam(a), cb=to_cam(b), cc=to_cam(c); return (ca.z + cb.z + cc.z) / 3; }
144
+ fix psx_depth4(Vec3 a, Vec3 b, Vec3 c, Vec3 d)
145
+ { Vec3 ca=to_cam(a),cb=to_cam(b),cc=to_cam(c),cd=to_cam(d); return (ca.z+cb.z+cc.z+cd.z)/4; }
146
+
147
+ void psx_tri3d(Vec3 a, Vec3 b, Vec3 c, unsigned int bgr)
148
+ {
149
+ Vec3 ca=to_cam(a), cb=to_cam(b), cc=to_cam(c);
150
+ int x0,y0,x1,y1,x2,y2;
151
+ if (!project(ca,&x0,&y0) || !project(cb,&x1,&y1) || !project(cc,&x2,&y2)) return;
152
+ /* back-face cull (screen-space cross product) */
153
+ int cross = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
154
+ if (cross <= 0) return;
155
+ psx_tri2d(x0,y0,x1,y1,x2,y2,bgr);
156
+ }
157
+
158
+ void psx_tri3d_nc(Vec3 a, Vec3 b, Vec3 c, unsigned int bgr)
159
+ {
160
+ Vec3 ca=to_cam(a), cb=to_cam(b), cc=to_cam(c);
161
+ int x0,y0,x1,y1,x2,y2;
162
+ if (!project(ca,&x0,&y0) || !project(cb,&x1,&y1) || !project(cc,&x2,&y2)) return;
163
+ psx_tri2d(x0,y0,x1,y1,x2,y2,bgr); /* no back-face cull */
164
+ }
165
+ void psx_quad3d_nc(Vec3 a, Vec3 b, Vec3 c, Vec3 d, unsigned int bgr)
166
+ { psx_tri3d_nc(a,b,c,bgr); psx_tri3d_nc(a,c,d,bgr); }
167
+
168
+ void psx_quad3d(Vec3 a, Vec3 b, Vec3 c, Vec3 d, unsigned int bgr)
169
+ {
170
+ /* split into two tris; both share the back-face test inside */
171
+ psx_tri3d(a,b,c,bgr);
172
+ psx_tri3d(a,c,d,bgr);
173
+ }
174
+
175
+ /* ── RNG (xorshift) ── */
176
+ static unsigned int rng_state = 0x12345678u;
177
+ void psx_srand(unsigned int s) { rng_state = s ? s : 1; }
178
+ unsigned int psx_rand(void)
179
+ { unsigned int x = rng_state; x ^= x << 13; x ^= x >> 17; x ^= x << 5; return rng_state = x; }
180
+
181
+ /* ── blocky decimal number (3x5 cells scaled to 8x10), for HUD/score ── */
182
+ static const unsigned char DIGITS[10][5] = {
183
+ {0x7,0x5,0x5,0x5,0x7},{0x2,0x6,0x2,0x2,0x7},{0x7,0x1,0x7,0x4,0x7},{0x7,0x1,0x3,0x1,0x7},
184
+ {0x5,0x5,0x7,0x1,0x1},{0x7,0x4,0x7,0x1,0x7},{0x7,0x4,0x7,0x5,0x7},{0x7,0x1,0x1,0x1,0x1},
185
+ {0x7,0x5,0x7,0x5,0x7},{0x7,0x5,0x7,0x1,0x7} };
186
+ static void draw_digit(int x, int y, int d, unsigned int bgr)
187
+ {
188
+ int r, c;
189
+ for (r = 0; r < 5; r++)
190
+ for (c = 0; c < 3; c++)
191
+ if (DIGITS[d][r] & (1 << (2 - c)))
192
+ psx_rect(x + c*3, y + r*3, 3, 3, bgr);
193
+ }
194
+ void psx_number(int x, int y, unsigned int value, unsigned int bgr)
195
+ {
196
+ char buf[10]; int n = 0, i;
197
+ if (value == 0) buf[n++] = 0;
198
+ while (value && n < 10) { buf[n++] = value % 10; value /= 10; }
199
+ for (i = 0; i < n; i++) draw_digit(x + (n-1-i)*12, y, buf[i], bgr);
200
+ }
@@ -0,0 +1,83 @@
1
+ /* psx.h — PS1 (R3000) helper lib with a software 3D pipeline, for romdev.
2
+ *
3
+ * A minimal-but-real 3D engine + GPU/pad helpers — what the PlayStation is FOR.
4
+ * Fixed-point (16.16) vector math, a camera (position + yaw/pitch), perspective
5
+ * projection, and painter-sorted flat-shaded triangles/quads drawn through the
6
+ * GPU. No GTE/SDK dependency — pure C math, so the same pipeline ports to N64.
7
+ *
8
+ * Build: build({ platform:"ps1", language:"c" }). Output is a PS-EXE the HLE BIOS
9
+ * loads at 0x80010000; main() loops forever. Color is BGR (GPU native); RGB() packs.
10
+ *
11
+ * 2D is also available (psx_rect / psx_sprite) for HUDs + the puzzle game.
12
+ */
13
+ #ifndef ROMDEV_PSX_H
14
+ #define ROMDEV_PSX_H
15
+
16
+ /* ── fixed-point (16.16) ── */
17
+ typedef int fix; /* 16.16 */
18
+ #define FIX(n) ((fix)((n) << 16))
19
+ #define FIXF(f) ((fix)((f) * 65536.0f))
20
+ #define FMUL(a,b) ((fix)(((long long)(a) * (b)) >> 16))
21
+ #define FDIV(a,b) ((fix)(((long long)(a) << 16) / (b)))
22
+ #define F2I(a) ((a) >> 16)
23
+
24
+ typedef struct { fix x, y, z; } Vec3;
25
+
26
+ /* ── GPU ports ── */
27
+ #define GP0 (*(volatile unsigned int*)0x1F801810)
28
+ #define GP1 (*(volatile unsigned int*)0x1F801814)
29
+
30
+ /* ── pad (digital, active-HIGH: bit set = pressed) ── */
31
+ #define PAD_SELECT (1u<<0)
32
+ #define PAD_START (1u<<3)
33
+ #define PAD_UP (1u<<4)
34
+ #define PAD_RIGHT (1u<<5)
35
+ #define PAD_DOWN (1u<<6)
36
+ #define PAD_LEFT (1u<<7)
37
+ #define PAD_L1 (1u<<10)
38
+ #define PAD_R1 (1u<<11)
39
+ #define PAD_TRIANGLE (1u<<12)
40
+ #define PAD_CIRCLE (1u<<13)
41
+ #define PAD_CROSS (1u<<14)
42
+ #define PAD_SQUARE (1u<<15)
43
+
44
+ #define RGB(r,g,b) (((unsigned int)(b)<<16)|((unsigned int)(g)<<8)|(unsigned int)(r))
45
+ #define SCREEN_W 320
46
+ #define SCREEN_H 240
47
+
48
+ /* ── GPU / 2D ── */
49
+ void psx_init(void);
50
+ void psx_clear(unsigned int bgr);
51
+ void psx_rect(int x, int y, int w, int h, unsigned int bgr);
52
+ void psx_tri2d(int x0,int y0,int x1,int y1,int x2,int y2,unsigned int bgr);
53
+ void psx_vsync(void);
54
+
55
+ /* ── input ── */
56
+ unsigned int psx_pad(void); /* current buttons (active-high) */
57
+ int psx_pressed(unsigned int mask); /* 1 if any of mask is held */
58
+
59
+ /* ── 3D pipeline ── */
60
+ /* camera: eye position + yaw (around Y) + pitch (around X), all 16.16. */
61
+ void psx_camera(fix ex, fix ey, fix ez, fix yaw, fix pitch);
62
+ /* set a model translation+rotation applied before the camera (simple: yaw only). */
63
+ void psx_model(fix tx, fix ty, fix tz, fix yaw);
64
+ /* draw a flat triangle of 3 model-space vertices; projected + back-face/near
65
+ culled. Z-buffered by draw order (call back-to-front, or use psx_sort). */
66
+ void psx_tri3d(Vec3 a, Vec3 b, Vec3 c, unsigned int bgr);
67
+ void psx_quad3d(Vec3 a, Vec3 b, Vec3 c, Vec3 d, unsigned int bgr);
68
+ /* no-back-face-cull variants — for ground planes / floors (ambiguous winding). */
69
+ void psx_tri3d_nc(Vec3 a, Vec3 b, Vec3 c, unsigned int bgr);
70
+ void psx_quad3d_nc(Vec3 a, Vec3 b, Vec3 c, Vec3 d, unsigned int bgr);
71
+ /* returns the average camera-space Z of 3/4 model verts (for manual sorting). */
72
+ fix psx_depth3(Vec3 a, Vec3 b, Vec3 c);
73
+ fix psx_depth4(Vec3 a, Vec3 b, Vec3 c, Vec3 d);
74
+
75
+ /* ── misc ── */
76
+ fix psx_sin(fix a); /* a in 16.16 turns? no — a in fix radians-ish (see psx.c) */
77
+ fix psx_cos(fix a);
78
+ unsigned int psx_rand(void);
79
+ void psx_srand(unsigned int seed);
80
+ /* draw a small decimal number (HUD/score), 8x8 blocky digits. */
81
+ void psx_number(int x, int y, unsigned int value, unsigned int bgr);
82
+
83
+ #endif
@@ -85,6 +85,15 @@ const LANGUAGE_TOOLCHAIN = {
85
85
  c: { toolchain: "sdcc", available: true, note: "C for MSX via SDCC's z80 port. Cartridge ROM with the 'AB' header + INIT pointer at $4000; boots on C-BIOS (open MSX BIOS). MSX2 by default (runs MSX1 carts too)." },
86
86
  asm: { toolchain: "sdcc", available: true },
87
87
  },
88
+ ps1: {
89
+ c: { toolchain: "mips-elf-gcc", available: true, note: "C for PS1 via gcc 14.2.0 + binutils + newlib (mips-elf, little-endian R3000), compiled to WASM. The bare path: a minimal crt0 sets the stack + clears .bss + calls main(); the output is a PS-EXE the HLE BIOS loads (load at 0x80010000). No SDK (PSn00bSDK) yet — bring your own GPU/SPU register writes, or use this for logic. write to GPU ports 0x1F801810/0x1F801814." },
90
+ },
91
+ n64: {
92
+ c: { toolchain: "mips-elf-gcc", available: true, note: "C for N64 via gcc 14.2.0 + binutils + newlib (mips-elf, big-endian R4300), compiled to WASM. Bare path: minimal crt0 (stack + .bss + main()) → flat code image at 0x80000400. NOTE: a fully bootable N64 ROM needs the IPL3 bootcode + a libdragon-style header (libdragon SDK forthcoming) — this path exercises the toolchain + is the basis for the SDK." },
93
+ },
94
+ dreamcast: {
95
+ c: { toolchain: "sh-elf-gcc", available: true, note: "C for Dreamcast via gcc 14.2.0 + binutils + newlib (sh-elf, little-endian SH-4, m4-single-only FP), compiled to WASM. Bare path: a minimal crt0 sets the stack + clears .bss + calls main(); the output is an ELF that Flycast's reios HLE BIOS boots DIRECTLY (no GD-ROM/CDI image, no firmware). No KallistiOS yet — bring up the PowerVR2 framebuffer yourself: program FB_R_CTRL/FB_R_SIZE/FB_R_SOF1 + SPG for 640x480 RGB565 at VRAM 0xA5000000, then write pixels (see the dc.h helper). With flycast_emulate_framebuffer on, a plain pixel-writing program presents — no TA list needed." },
96
+ },
88
97
  };
89
98
 
90
99
  /**
@@ -115,6 +124,9 @@ const PLATFORM_DEFAULT_LANGUAGE = {
115
124
  spc700: "asm",
116
125
  pce: "c",
117
126
  msx: "c",
127
+ ps1: "c",
128
+ n64: "c",
129
+ dreamcast: "c",
118
130
  };
119
131
 
120
132
  /**
@@ -467,6 +479,59 @@ export async function buildForPlatform(args) {
467
479
  // below (SDCC_PORTS["gb"] = sm83 port).
468
480
  }
469
481
 
482
+ if (args.platform === "ps1" || args.platform === "n64") {
483
+ // MIPS C: the bare gcc+newlib+libgcc path (no SDK yet). cc1→as→ld→objcopy
484
+ // through the mips-elf-gcc WASM toolchain; PS1 (R3000, little-endian) wraps the
485
+ // image in a PS-EXE the HLE BIOS loads; N64 (R4300, big-endian) emits a flat
486
+ // .bin (real N64 boot needs libdragon — forthcoming). language defaults to "c".
487
+ const { buildMipsC } = await import("./mips-c/mips-c.js");
488
+ const r = await buildMipsC({
489
+ source: args.source,
490
+ sources: args.sources,
491
+ headers: args.includes,
492
+ platform: args.platform,
493
+ cc1Options: args.options,
494
+ });
495
+ return {
496
+ ok: r.ok,
497
+ binary: r.binary,
498
+ listing: "",
499
+ symbols: r.symbols ?? "",
500
+ log: r.log,
501
+ issues: parseBuildLog(r.log),
502
+ exitCode: r.exitCode,
503
+ toolchain: "mips-elf-gcc",
504
+ stage: r.stage,
505
+ ...(r.crash ? { crash: r.crash } : {}),
506
+ };
507
+ }
508
+
509
+ if (args.platform === "dreamcast") {
510
+ // Dreamcast SH-4 C: the bare gcc+newlib+libgcc path through the sh-elf-gcc WASM
511
+ // toolchain (cc1→as→ld). The output is an ELF that Flycast's reios HLE BIOS boots
512
+ // directly (no GD-ROM/CDI image). Bring up the PowerVR2 framebuffer yourself (see
513
+ // the dc.h helper); language defaults to "c".
514
+ const { buildShC } = await import("./sh-c/sh-c.js");
515
+ const r = await buildShC({
516
+ source: args.source,
517
+ sources: args.sources,
518
+ headers: args.includes,
519
+ cc1Options: args.options,
520
+ });
521
+ return {
522
+ ok: r.ok,
523
+ binary: r.binary,
524
+ listing: "",
525
+ symbols: r.symbols ?? "",
526
+ log: r.log,
527
+ issues: parseBuildLog(r.log),
528
+ exitCode: r.exitCode,
529
+ toolchain: "sh-elf-gcc",
530
+ stage: r.stage,
531
+ ...(r.crash ? { crash: r.crash } : {}),
532
+ };
533
+ }
534
+
470
535
  if (args.platform === "gba") {
471
536
  // R24 + R28: language:"c" routes through the arm-none-eabi gcc +
472
537
  // binutils WASM toolchain (cc1-arm → as → ld → objcopy). Three
@@ -0,0 +1,21 @@
1
+ /* Minimal N64 (R4300) crt0 — bare stack + .bss clear + main(). NOTE: a real
2
+ bootable N64 ROM needs the IPL3 bootcode + a libdragon-style header; this
3
+ minimal crt0 exercises the mips-elf toolchain (big-endian) and produces a flat
4
+ code image. Full N64 boot = libdragon (STAGE 3). */
5
+ .set noreorder
6
+ .section .text.start, "ax"
7
+ .global _start
8
+ _start:
9
+ la $sp, __sp_top
10
+ la $t0, __bss_start
11
+ la $t1, __bss_end
12
+ 1: beq $t0, $t1, 2f
13
+ nop
14
+ sw $zero, 0($t0)
15
+ addiu $t0, $t0, 4
16
+ b 1b
17
+ nop
18
+ 2: jal main
19
+ nop
20
+ 3: b 3b
21
+ nop
@@ -0,0 +1,30 @@
1
+ /* Minimal N64 IPL3 (clean-room): runs in SP DMEM at 0xA4000040 after the HLE/real
2
+ PIF boot copies ROM[0x40..0xFFF] here. Its job: PI-DMA the game from cart
3
+ (0xB0001000) to RDRAM (0x80000400), then jump to the game entry. Assembled to
4
+ the 0xFC0-byte IPL3 slot. */
5
+ .set noreorder
6
+ .set noat
7
+ .global ipl3_start
8
+ ipl3_start:
9
+ /* PI DMA: copy GAME_SIZE bytes from cart 0x10001000 to RDRAM 0x00000400 */
10
+ lui $t0, 0xA460 /* PI base 0xA4600000 */
11
+ /* PI_DRAM_ADDR = 0x00000400 (RDRAM dest, phys) */
12
+ li $t1, 0x00000400
13
+ sw $t1, 0x00($t0) /* PI_DRAM_ADDR_REG */
14
+ /* PI_CART_ADDR = 0x10001000 (cart src, phys; game starts at ROM 0x1000) */
15
+ lui $t1, 0x1000
16
+ ori $t1, $t1, 0x1000
17
+ sw $t1, 0x04($t0) /* PI_CART_ADDR_REG */
18
+ /* PI_WR_LEN = GAME_SIZE-1 (write to RDRAM) */
19
+ li $t1, (GAME_SIZE - 1)
20
+ sw $t1, 0x0C($t0) /* PI_WR_LEN_REG starts the DMA */
21
+ 1: /* wait for PI not busy (PI_STATUS bit0/1) */
22
+ lw $t2, 0x10($t0)
23
+ andi $t2, $t2, 0x3
24
+ bnez $t2, 1b
25
+ nop
26
+ /* jump to the game entry in RDRAM (cached kseg0) */
27
+ lui $t3, 0x8000
28
+ ori $t3, $t3, 0x0400
29
+ jr $t3
30
+ nop
@@ -0,0 +1,15 @@
1
+ OUTPUT_FORMAT("elf32-bigmips")
2
+ OUTPUT_ARCH(mips)
3
+ ENTRY(_start)
4
+ MEMORY { ram (rwx) : ORIGIN = 0x80000400, LENGTH = 0x3FFC00 }
5
+ SECTIONS {
6
+ .text : { *(.text.start) *(.text*) *(.rodata*) } > ram
7
+ .data : { *(.data*) *(.sdata*) *(.lit8) *(.lit4) *(.scommon) } > ram
8
+ . = ALIGN(8);
9
+ __bss_start = .;
10
+ .bss : { *(.bss*) *(.sbss*) *(.scommon) *(COMMON) } > ram
11
+ . = ALIGN(8);
12
+ __bss_end = .;
13
+ __sp_top = 0x803FFFF0;
14
+ /DISCARD/ : { *(.MIPS.abiflags) *(.reginfo) *(.comment) }
15
+ }
@@ -0,0 +1,20 @@
1
+ /* Minimal PS1 (R3000) crt0 — sets the stack, clears .bss, calls main(), loops.
2
+ The PS-EXE header (set by the JS wrapper) points the BIOS entry here. */
3
+ .set noreorder
4
+ .section .text.start, "ax"
5
+ .global _start
6
+ _start:
7
+ la $sp, __sp_top /* stack top */
8
+ /* zero .bss */
9
+ la $t0, __bss_start
10
+ la $t1, __bss_end
11
+ 1: beq $t0, $t1, 2f
12
+ nop
13
+ sw $zero, 0($t0)
14
+ addiu $t0, $t0, 4
15
+ b 1b
16
+ nop
17
+ 2: jal main
18
+ nop
19
+ 3: b 3b /* main returned — hang */
20
+ nop
@@ -0,0 +1,15 @@
1
+ OUTPUT_FORMAT("elf32-littlemips")
2
+ OUTPUT_ARCH(mips)
3
+ ENTRY(_start)
4
+ MEMORY { ram (rwx) : ORIGIN = 0x80010000, LENGTH = 0x1F0000 }
5
+ SECTIONS {
6
+ .text : { *(.text.start) *(.text*) *(.rodata*) } > ram
7
+ .data : { *(.data*) *(.sdata*) *(.lit8) *(.lit4) *(.scommon) } > ram
8
+ . = ALIGN(4);
9
+ __bss_start = .;
10
+ .bss : { *(.bss*) *(.sbss*) *(.scommon) *(COMMON) } > ram
11
+ . = ALIGN(4);
12
+ __bss_end = .;
13
+ __sp_top = 0x801FFFF0;
14
+ /DISCARD/ : { *(.MIPS.abiflags) *(.reginfo) *(.comment) }
15
+ }
@@ -0,0 +1,37 @@
1
+ /* softint.c — the few libgcc helpers a fixed-point game needs, in plain C, so the
2
+ build doesn't depend on an endian-specific libgcc.a. Compiled per-build with the
3
+ target endian. Currently: 64-bit signed/unsigned divide + modulo (__divdi3 etc).
4
+ MIPS has native 32x32->64 multiply, so __muldi3 isn't needed. */
5
+ typedef long long DItype;
6
+ typedef unsigned long long UDItype;
7
+
8
+ static UDItype udivmod64(UDItype num, UDItype den, UDItype *rem) {
9
+ UDItype quot = 0, qbit = 1;
10
+ if (den == 0) { if (rem) *rem = 0; return 0; }
11
+ /* normalize: shift den up until > num or top bit set */
12
+ while ((DItype)den >= 0) { den <<= 1; qbit <<= 1; }
13
+ while (qbit) {
14
+ if (den <= num) { num -= den; quot += qbit; }
15
+ den >>= 1; qbit >>= 1;
16
+ }
17
+ if (rem) *rem = num;
18
+ return quot;
19
+ }
20
+
21
+ UDItype __udivdi3(UDItype a, UDItype b) { return udivmod64(a, b, 0); }
22
+ UDItype __umoddi3(UDItype a, UDItype b) { UDItype r; udivmod64(a, b, &r); return r; }
23
+
24
+ DItype __divdi3(DItype a, DItype b) {
25
+ int neg = 0; UDItype ua, ub, q;
26
+ if (a < 0) { a = -a; neg ^= 1; } if (b < 0) { b = -b; neg ^= 1; }
27
+ ua = (UDItype)a; ub = (UDItype)b;
28
+ q = udivmod64(ua, ub, 0);
29
+ return neg ? -(DItype)q : (DItype)q;
30
+ }
31
+ DItype __moddi3(DItype a, DItype b) {
32
+ int neg = 0; UDItype ua, ub, r;
33
+ if (a < 0) { a = -a; neg = 1; } if (b < 0) { b = -b; }
34
+ ua = (UDItype)a; ub = (UDItype)b;
35
+ udivmod64(ua, ub, &r);
36
+ return neg ? -(DItype)r : (DItype)r;
37
+ }