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.
Files changed (65) hide show
  1. package/CHANGELOG.md +322 -0
  2. package/examples/n64/platformer/main.c +158 -0
  3. package/examples/n64/puzzle/main.c +117 -0
  4. package/examples/n64/racing/main.c +147 -0
  5. package/examples/n64/shmup/main.c +122 -0
  6. package/examples/n64/sports/main.c +127 -0
  7. package/examples/ps1/platformer/main.c +158 -0
  8. package/examples/ps1/puzzle/main.c +125 -0
  9. package/examples/ps1/racing/main.c +147 -0
  10. package/examples/ps1/shmup/main.c +192 -0
  11. package/examples/ps1/sports/main.c +127 -0
  12. package/examples/ps1/sprite_move/main.c +38 -0
  13. package/package.json +9 -2
  14. package/src/analysis/analyze.js +169 -29
  15. package/src/analysis/decompile.js +4 -0
  16. package/src/analysis/decompiler/sleigh/mips.ldefs +25 -0
  17. package/src/analysis/decompiler/sleigh/mips32.pspec +78 -0
  18. package/src/analysis/decompiler/sleigh/mips32be.cspec +107 -0
  19. package/src/analysis/decompiler/sleigh/mips32be.sla +211162 -0
  20. package/src/analysis/decompiler/sleigh/mips32le.cspec +106 -0
  21. package/src/analysis/decompiler/sleigh/mips32le.sla +210624 -0
  22. package/src/analysis/rizin.js +22 -1
  23. package/src/cores/capabilities.js +90 -2
  24. package/src/cores/registry.js +12 -0
  25. package/src/host/LibretroGL.js +270 -0
  26. package/src/host/LibretroGLBridge.js +836 -0
  27. package/src/host/LibretroHost.js +144 -3
  28. package/src/host/callbacks.js +18 -2
  29. package/src/host/coreLoader.js +49 -2
  30. package/src/host/cpu-state.js +27 -0
  31. package/src/host/framebuffer.js +14 -1
  32. package/src/host/glOptionalDep.js +60 -0
  33. package/src/host/n64-ai-state.js +43 -0
  34. package/src/host/ps1-spu-state.js +65 -0
  35. package/src/host/retroConstants.js +14 -0
  36. package/src/mcp/tools/cheats.js +73 -4
  37. package/src/mcp/tools/disasm.js +2 -0
  38. package/src/mcp/tools/frame.js +18 -9
  39. package/src/mcp/tools/index.js +12 -2
  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 +100 -0
  44. package/src/mcp/tools/rendering-context.js +2 -1
  45. package/src/mcp/tools/toolchain.js +1 -1
  46. package/src/mcp/tools/watch-memory.js +93 -20
  47. package/src/platforms/n64/lib/c/n64.c +196 -0
  48. package/src/platforms/n64/lib/c/n64.h +68 -0
  49. package/src/platforms/ps1/lib/c/psx.c +200 -0
  50. package/src/platforms/ps1/lib/c/psx.h +83 -0
  51. package/src/toolchains/cc65/cc65.js +11 -0
  52. package/src/toolchains/index.js +35 -0
  53. package/src/toolchains/mips-c/lib/be/libc.a +0 -0
  54. package/src/toolchains/mips-c/lib/be/libgcc.a +0 -0
  55. package/src/toolchains/mips-c/lib/be/libm.a +0 -0
  56. package/src/toolchains/mips-c/lib/el/libc.a +0 -0
  57. package/src/toolchains/mips-c/lib/el/libm.a +0 -0
  58. package/src/toolchains/mips-c/lib/n64-crt0.s +21 -0
  59. package/src/toolchains/mips-c/lib/n64-ipl3.s +30 -0
  60. package/src/toolchains/mips-c/lib/n64.ld +15 -0
  61. package/src/toolchains/mips-c/lib/ps1-crt0.s +20 -0
  62. package/src/toolchains/mips-c/lib/ps1.ld +15 -0
  63. package/src/toolchains/mips-c/lib/softint.c +37 -0
  64. package/src/toolchains/mips-c/mips-c.js +155 -0
  65. package/src/toolchains/mips-elf-gcc/gcc.js +130 -0
@@ -0,0 +1,147 @@
1
+ /*
2
+ * racing/main.c — POLE BENDER: a 3D PlayStation racer.
3
+ *
4
+ * The road is a ribbon of quads receding to the horizon (real perspective, drawn
5
+ * far-to-near). You steer a car near the camera between the verges; the world
6
+ * scrolls toward you and the track curves. Rival cars (cubes) sit further up the
7
+ * road and grow as you close on them — collide and you spin out (lose time/score).
8
+ * Title -> race -> results state machine, distance score, a lap timer.
9
+ *
10
+ * Build: build({ platform:"ps1", language:"c" }). Controls: LEFT/RIGHT steer,
11
+ * UP accelerate, DOWN brake, START begins/restarts.
12
+ *
13
+ * 3D technique: the track is N segments ahead; each segment is a quad between two
14
+ * road-edge pairs, its centerline x driven by a curve value so the road bends. The
15
+ * camera sits just behind/above the car. All 16.16 fixed point.
16
+ */
17
+ #include "psx.h"
18
+
19
+ #define SEGS 14
20
+ enum { TITLE, RACE, DONE };
21
+
22
+ static fix car_x; /* lateral position of the car */
23
+ static fix scroll; /* how far we've travelled (for segment phase) */
24
+ static fix curve; /* current road curvature */
25
+ static fix speed;
26
+ static int state, score, best;
27
+ static int rivals_z[3], rival_x[3];
28
+ static unsigned int prev_pad;
29
+
30
+ static void reset_race(void)
31
+ {
32
+ int i;
33
+ car_x = 0; scroll = 0; curve = 0; speed = FIXF(0.4f); score = 0;
34
+ for (i = 0; i < 3; i++) { rivals_z[i] = 8 + i * 5; rival_x[i] = ((i * 97) % 5) - 2; }
35
+ }
36
+
37
+ /* a flat ground-plane quad at depth z0..z1, centered at lateral cx with half-width hw. */
38
+ static void road_quad(fix cx0, fix z0, fix cx1, fix z1, fix hw, unsigned int col)
39
+ {
40
+ Vec3 a, b, c, d;
41
+ a.x = cx0 - hw; a.y = FIXF(-2.0f); a.z = z0;
42
+ b.x = cx0 + hw; b.y = FIXF(-2.0f); b.z = z0;
43
+ c.x = cx1 + hw; c.y = FIXF(-2.0f); c.z = z1;
44
+ d.x = cx1 - hw; d.y = FIXF(-2.0f); d.z = z1;
45
+ /* ground plane: no back-face cull (a flat floor's winding is ambiguous). */
46
+ psx_quad3d_nc(a, b, c, d, col);
47
+ }
48
+
49
+ static void draw_cube_at(fix x, fix y, fix z, fix s, unsigned int col)
50
+ {
51
+ Vec3 v[8]; int i;
52
+ 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};
53
+ psx_model(x, y, z, 0);
54
+ for (i = 0; i < 8; i++){ v[i].x=sx[i]*s; v[i].y=sy[i]*s; v[i].z=sz[i]*s; }
55
+ psx_quad3d(v[0],v[1],v[2],v[3],col); psx_quad3d(v[5],v[4],v[7],v[6],col);
56
+ psx_quad3d(v[4],v[0],v[3],v[7],col); psx_quad3d(v[1],v[5],v[6],v[2],col);
57
+ psx_quad3d(v[4],v[5],v[1],v[0],col); psx_quad3d(v[3],v[2],v[6],v[7],col);
58
+ }
59
+
60
+ static void update(void)
61
+ {
62
+ unsigned int pad = psx_pad();
63
+ int i;
64
+ if (pad & PAD_LEFT) car_x -= FMUL(speed, FIXF(0.4f));
65
+ if (pad & PAD_RIGHT) car_x += FMUL(speed, FIXF(0.4f));
66
+ if (pad & PAD_UP) { speed += FIXF(0.01f); if (speed > FIX(1)) speed = FIX(1); }
67
+ if (pad & PAD_DOWN) { speed -= FIXF(0.03f); if (speed < FIXF(0.1f)) speed = FIXF(0.1f); }
68
+ if (car_x < FIX(-4)) car_x = FIX(-4); if (car_x > FIX(4)) car_x = FIX(4);
69
+
70
+ /* advance the world + wander the curve */
71
+ scroll += speed;
72
+ curve = psx_sin(scroll >> 2); /* the road bends with a sine */
73
+ curve = FMUL(curve, FIX(3));
74
+ score += F2I(speed << 2);
75
+
76
+ /* rivals roll toward us; collide = spin (reset speed + small score penalty) */
77
+ for (i = 0; i < 3; i++) {
78
+ rivals_z[i] -= F2I(speed << 1) + 1;
79
+ if (rivals_z[i] < 2) { rivals_z[i] = 30 + (psx_rand() % 12); rival_x[i] = (int)(psx_rand() % 5) - 2; }
80
+ if (rivals_z[i] < 4) {
81
+ fix rx = (fix)rival_x[i] << 16;
82
+ fix dx = rx - car_x; if (dx < 0) dx = -dx;
83
+ if (dx < FIX(1)) { speed = FIXF(0.15f); if (score > 30) score -= 30; }
84
+ }
85
+ }
86
+ }
87
+
88
+ static void render(void)
89
+ {
90
+ int s, i;
91
+ psx_clear(RGB(70, 130, 200)); /* sky */
92
+ /* far ground band */
93
+ psx_rect(0, 120, 320, 120, RGB(40, 90, 40));
94
+
95
+ /* draw road segments far -> near; centerline curves with depth */
96
+ for (s = SEGS - 1; s >= 0; s--) {
97
+ fix z0 = FIX(2 + s * 3);
98
+ fix z1 = FIX(2 + (s + 1) * 3);
99
+ /* centerline x grows with depth*curve for the bend */
100
+ fix cx0 = FMUL(curve, FMUL(z0, z0)) >> 6;
101
+ fix cx1 = FMUL(curve, FMUL(z1, z1)) >> 6;
102
+ unsigned int col = (s & 1) ? RGB(60, 60, 70) : RGB(80, 80, 90);
103
+ road_quad(cx0 - car_x, z0, cx1 - car_x, z1, FIX(3), col);
104
+ /* verges */
105
+ road_quad(cx0 - car_x - FIX(3), z0, cx1 - car_x - FIX(3), z1, FIXF(0.4f), RGB(220,220,220));
106
+ road_quad(cx0 - car_x + FIX(3), z0, cx1 - car_x + FIX(3), z1, FIXF(0.4f), RGB(220,60,60));
107
+ }
108
+ /* rivals on the road */
109
+ for (i = 0; i < 3; i++) if (rivals_z[i] >= 2 && rivals_z[i] < 40) {
110
+ fix z = (fix)rivals_z[i] << 16;
111
+ fix cx = FMUL(curve, FMUL(z, z)) >> 6;
112
+ draw_cube_at(((fix)rival_x[i] << 16) + cx - car_x, FIXF(-1.4f), z, FIXF(0.5f), RGB(230, 200, 40));
113
+ }
114
+ /* the player's car, near the camera */
115
+ draw_cube_at(0, FIXF(-1.4f), FIX(3), FIXF(0.6f), RGB(220, 40, 40));
116
+
117
+ psx_number(8, 6, (unsigned)score, RGB(255, 255, 255));
118
+ }
119
+
120
+ int main(void)
121
+ {
122
+ psx_init();
123
+ psx_srand(0x1337);
124
+ psx_camera(0, FIXF(-0.5f), FIX(-1), 0, FIXF(-0.18f)); /* low chase cam, looking down */
125
+ state = TITLE; prev_pad = 0;
126
+
127
+ for (;;) {
128
+ unsigned int pad = psx_pad();
129
+ if (state == TITLE) {
130
+ static fix t; t += FIX(3);
131
+ psx_clear(RGB(20, 20, 50));
132
+ draw_cube_at(0, 0, FIX(6), FIX(1), RGB(220, 40, 40));
133
+ if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_race(); state = RACE; }
134
+ } else if (state == RACE) {
135
+ update(); render();
136
+ if (score > 4000) { if (score > best) best = score; state = DONE; }
137
+ } else {
138
+ psx_clear(RGB(8, 30, 8));
139
+ psx_number(110, 100, (unsigned)score, RGB(120, 255, 120));
140
+ psx_number(120, 130, (unsigned)best, RGB(255, 255, 120));
141
+ if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_race(); state = RACE; }
142
+ }
143
+ prev_pad = pad;
144
+ psx_vsync();
145
+ }
146
+ return 0;
147
+ }
@@ -0,0 +1,192 @@
1
+ /*
2
+ * shmup/main.c — STARFALL: a 3D PlayStation vertical shooter.
3
+ *
4
+ * Idiomatic PS1 3D: the playfield recedes into the screen. You pilot a ship near
5
+ * the camera; enemy cubes fly IN from the far distance toward you, growing as the
6
+ * perspective projection scales them up. Shoot them with a stream of bullets that
7
+ * fire away into Z. Title -> play -> game-over state machine, score + lives,
8
+ * AABB-in-screen-space collision, an xorshift wave spawner, a parallax starfield.
9
+ *
10
+ * Build: build({ platform:"ps1", language:"c" }). Controls: d-pad/stick move,
11
+ * CROSS fires, START begins / restarts. Runs on the romdev pcsx_rearmed core.
12
+ *
13
+ * Pipeline: psx_camera fixes the eye; each entity is a small cube drawn via
14
+ * psx_model (translate to its world pos) + psx_quad3d faces. Depth sort is implicit
15
+ * (we draw far enemies first by iterating; the painter order is good enough for
16
+ * non-overlapping cubes). All math is 16.16 fixed point.
17
+ */
18
+ #include "psx.h"
19
+
20
+ #define MAX_E 8
21
+ #define MAX_B 6
22
+
23
+ enum { TITLE, PLAY, OVER };
24
+
25
+ typedef struct { int alive; fix x, y, z; } Ent;
26
+
27
+ static Ent enemy[MAX_E];
28
+ static Ent bullet[MAX_B];
29
+ static fix px, py; /* player position (x,y); z fixed near camera */
30
+ static int state, score, lives, hi;
31
+ static unsigned int prev_pad;
32
+
33
+ /* a unit cube's 6 faces, drawn at the current psx_model origin, scaled by `s`. */
34
+ static void draw_cube(fix s, unsigned int col)
35
+ {
36
+ Vec3 v[8];
37
+ int i;
38
+ static const int sx[8] = {-1, 1, 1,-1,-1, 1, 1,-1};
39
+ static const int sy[8] = {-1,-1, 1, 1,-1,-1, 1, 1};
40
+ static const int sz[8] = {-1,-1,-1,-1, 1, 1, 1, 1};
41
+ for (i = 0; i < 8; i++) { v[i].x = sx[i] * s; v[i].y = sy[i] * s; v[i].z = sz[i] * s; }
42
+ psx_quad3d(v[0],v[1],v[2],v[3], col);
43
+ psx_quad3d(v[5],v[4],v[7],v[6], col);
44
+ psx_quad3d(v[4],v[0],v[3],v[7], col);
45
+ psx_quad3d(v[1],v[5],v[6],v[2], col);
46
+ psx_quad3d(v[4],v[5],v[1],v[0], col);
47
+ psx_quad3d(v[3],v[2],v[6],v[7], col);
48
+ }
49
+
50
+ static void reset_game(void)
51
+ {
52
+ int i;
53
+ for (i = 0; i < MAX_E; i++) enemy[i].alive = 0;
54
+ for (i = 0; i < MAX_B; i++) bullet[i].alive = 0;
55
+ px = 0; py = FIXF(-1.5f);
56
+ score = 0; lives = 3;
57
+ }
58
+
59
+ static void spawn_enemy(void)
60
+ {
61
+ int i;
62
+ for (i = 0; i < MAX_E; i++) if (!enemy[i].alive) {
63
+ enemy[i].alive = 1;
64
+ enemy[i].x = (fix)((psx_rand() % 7) - 3) << 16; /* -3..3 */
65
+ enemy[i].y = (fix)((psx_rand() % 5) - 1) << 16; /* -1..3 */
66
+ enemy[i].z = FIX(40); /* far away */
67
+ return;
68
+ }
69
+ }
70
+
71
+ static void fire(void)
72
+ {
73
+ int i;
74
+ for (i = 0; i < MAX_B; i++) if (!bullet[i].alive) {
75
+ bullet[i].alive = 1; bullet[i].x = px; bullet[i].y = py; bullet[i].z = FIX(4);
76
+ return;
77
+ }
78
+ }
79
+
80
+ /* AABB overlap of two world points in the XY plane within `r`. */
81
+ static int hit(fix ax, fix ay, fix bx, fix by, fix r)
82
+ {
83
+ fix dx = ax - bx, dy = ay - by;
84
+ if (dx < 0) dx = -dx; if (dy < 0) dy = -dy;
85
+ return dx < r && dy < r;
86
+ }
87
+
88
+ static void update(void)
89
+ {
90
+ unsigned int pad = psx_pad();
91
+ int i, j;
92
+
93
+ /* move ship */
94
+ if (pad & PAD_LEFT) px -= FIXF(0.18f);
95
+ if (pad & PAD_RIGHT) px += FIXF(0.18f);
96
+ if (pad & PAD_UP) py += FIXF(0.14f);
97
+ if (pad & PAD_DOWN) py -= FIXF(0.14f);
98
+ if (px < FIX(-4)) px = FIX(-4); if (px > FIX(4)) px = FIX(4);
99
+ if (py < FIX(-3)) py = FIX(-3); if (py > FIX(2)) py = FIX(2);
100
+
101
+ /* fire on CROSS edge */
102
+ if ((pad & PAD_CROSS) && !(prev_pad & PAD_CROSS)) fire();
103
+
104
+ /* bullets fly into Z */
105
+ for (i = 0; i < MAX_B; i++) if (bullet[i].alive) {
106
+ bullet[i].z += FIX(2);
107
+ if (bullet[i].z > FIX(45)) bullet[i].alive = 0;
108
+ }
109
+
110
+ /* enemies approach */
111
+ if ((psx_rand() & 31) == 0) spawn_enemy();
112
+ for (i = 0; i < MAX_E; i++) if (enemy[i].alive) {
113
+ enemy[i].z -= FIXF(0.5f);
114
+ if (enemy[i].z < FIX(3)) { /* reached the ship plane */
115
+ enemy[i].alive = 0;
116
+ if (--lives <= 0) { if (score > hi) hi = score; state = OVER; }
117
+ }
118
+ /* bullet collisions */
119
+ for (j = 0; j < MAX_B; j++) if (bullet[j].alive) {
120
+ fix dz = enemy[i].z - bullet[j].z; if (dz < 0) dz = -dz;
121
+ if (dz < FIX(2) && hit(enemy[i].x, enemy[i].y, bullet[j].x, bullet[j].y, FIX(1))) {
122
+ enemy[i].alive = 0; bullet[j].alive = 0; score += 10;
123
+ }
124
+ }
125
+ }
126
+ prev_pad = pad;
127
+ }
128
+
129
+ static void render(void)
130
+ {
131
+ int i;
132
+ psx_clear(RGB(6, 8, 24));
133
+
134
+ /* starfield: a few far cubes as backdrop sparkle */
135
+ for (i = 0; i < 12; i++) {
136
+ fix sx = (fix)(((i * 53) % 9) - 4) << 16;
137
+ fix sy = (fix)(((i * 37) % 7) - 3) << 16;
138
+ psx_model(sx, sy, FIX(50), 0);
139
+ draw_cube(FIXF(0.15f), RGB(40, 40, 70));
140
+ }
141
+ /* enemies (draw far -> near for painter order) */
142
+ for (i = 0; i < MAX_E; i++) if (enemy[i].alive) {
143
+ psx_model(enemy[i].x, enemy[i].y, enemy[i].z, enemy[i].z << 2);
144
+ draw_cube(FIXF(0.7f), RGB(230, 60, 60));
145
+ }
146
+ /* bullets */
147
+ for (i = 0; i < MAX_B; i++) if (bullet[i].alive) {
148
+ psx_model(bullet[i].x, bullet[i].y, bullet[i].z, 0);
149
+ draw_cube(FIXF(0.18f), RGB(255, 240, 80));
150
+ }
151
+ /* player ship near the camera */
152
+ psx_model(px, py, FIX(3), 0);
153
+ draw_cube(FIXF(0.5f), RGB(80, 200, 255));
154
+
155
+ /* HUD: score + lives */
156
+ psx_number(8, 6, (unsigned)score, RGB(255, 255, 255));
157
+ for (i = 0; i < lives; i++) psx_rect(290 - i*10, 8, 6, 6, RGB(80, 200, 255));
158
+ }
159
+
160
+ int main(void)
161
+ {
162
+ psx_init();
163
+ psx_srand(0xC0FFEE);
164
+ psx_camera(0, 0, FIX(-2), 0, FIXF(-0.08f)); /* slight downward tilt over the field */
165
+ state = TITLE;
166
+ prev_pad = 0;
167
+
168
+ for (;;) {
169
+ unsigned int pad = psx_pad();
170
+ if (state == TITLE) {
171
+ psx_clear(RGB(10, 10, 40));
172
+ /* a spinning hero cube as the title art */
173
+ static fix t; t += FIX(2);
174
+ psx_model(0, 0, FIX(6), t);
175
+ draw_cube(FIX(1), RGB(80, 200, 255));
176
+ psx_number(120, 200, 0, RGB(255, 255, 255)); /* press start cue: shows "0" */
177
+ if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
178
+ prev_pad = pad;
179
+ } else if (state == PLAY) {
180
+ update();
181
+ render();
182
+ } else { /* OVER */
183
+ psx_clear(RGB(40, 8, 8));
184
+ psx_number(110, 100, (unsigned)score, RGB(255, 80, 80));
185
+ psx_number(120, 130, (unsigned)hi, RGB(255, 255, 120));
186
+ if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
187
+ prev_pad = pad;
188
+ }
189
+ psx_vsync();
190
+ }
191
+ return 0;
192
+ }
@@ -0,0 +1,127 @@
1
+ /*
2
+ * sports/main.c — SLAM COURT: a 3D PlayStation sports game (air-hockey / pong).
3
+ *
4
+ * A 3D playfield seen in perspective: you control the near paddle, the CPU the far
5
+ * one. A ball bounces down the court in 3D (X across, Z into the screen); rally it
6
+ * past the CPU to score, miss and the CPU scores. First to 7. Title -> match ->
7
+ * game-over. The whole court is drawn with the perspective pipeline; the ball's
8
+ * size changes with depth, selling the 3D.
9
+ *
10
+ * Build: build({ platform:"ps1", language:"c" }). Controls: LEFT/RIGHT move your
11
+ * paddle, START begin/restart.
12
+ *
13
+ * 3D technique: the court floor is a big no-cull quad; paddles + ball are cubes at
14
+ * world positions. The CPU paddle tracks the ball's X with a capped speed. 16.16 fp.
15
+ */
16
+ #include "psx.h"
17
+
18
+ enum { TITLE, PLAY, OVER };
19
+
20
+ static fix px; /* player paddle X (near, z = small) */
21
+ static fix cx; /* cpu paddle X (far, z = big) */
22
+ static fix bx, bz, bvx, bvz; /* ball position + velocity */
23
+ static int you, cpu, state;
24
+ static unsigned int prev_pad;
25
+
26
+ #define COURT_HW FIX(5) /* court half-width */
27
+ #define NEAR_Z FIX(3)
28
+ #define FAR_Z FIX(22)
29
+
30
+ static void draw_cube_at(fix x, fix y, fix z, fix s, unsigned int col)
31
+ {
32
+ Vec3 v[8]; int i;
33
+ 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};
34
+ psx_model(x,y,z,0);
35
+ for(i=0;i<8;i++){v[i].x=sx[i]*s;v[i].y=sy[i]*s;v[i].z=sz[i]*s;}
36
+ psx_quad3d(v[0],v[1],v[2],v[3],col); psx_quad3d(v[5],v[4],v[7],v[6],col);
37
+ psx_quad3d(v[4],v[0],v[3],v[7],col); psx_quad3d(v[1],v[5],v[6],v[2],col);
38
+ psx_quad3d(v[4],v[5],v[1],v[0],col); psx_quad3d(v[3],v[2],v[6],v[7],col);
39
+ }
40
+
41
+ static void serve(int toward_cpu)
42
+ {
43
+ bx = 0; bz = (NEAR_Z + FAR_Z) >> 1;
44
+ bvx = (psx_rand() & 1) ? FIXF(0.12f) : FIXF(-0.12f);
45
+ bvz = toward_cpu ? FIXF(0.45f) : FIXF(-0.45f);
46
+ }
47
+
48
+ static void reset_game(void) { px = 0; cx = 0; you = 0; cpu = 0; serve(1); }
49
+
50
+ static void update(void)
51
+ {
52
+ unsigned int pad = psx_pad();
53
+ if (pad & PAD_LEFT) px -= FIXF(0.22f);
54
+ if (pad & PAD_RIGHT) px += FIXF(0.22f);
55
+ if (px < -COURT_HW) px = -COURT_HW; if (px > COURT_HW) px = COURT_HW;
56
+
57
+ /* cpu tracks the ball, capped */
58
+ { fix d = bx - cx; if (d > FIXF(0.16f)) d = FIXF(0.16f); if (d < -FIXF(0.16f)) d = -FIXF(0.16f); cx += d; }
59
+
60
+ bx += bvx; bz += bvz;
61
+ if (bx < -COURT_HW || bx > COURT_HW) bvx = -bvx; /* side walls */
62
+
63
+ /* near paddle (player) */
64
+ if (bz <= NEAR_Z) {
65
+ fix d = bx - px; if (d < 0) d = -d;
66
+ if (d < FIX(2)) { bvz = -bvz; bz = NEAR_Z; bvx += (bx - px) >> 4; }
67
+ else { cpu++; if (cpu >= 7) { state = OVER; } else serve(1); }
68
+ }
69
+ /* far paddle (cpu) */
70
+ if (bz >= FAR_Z) {
71
+ fix d = bx - cx; if (d < 0) d = -d;
72
+ if (d < FIX(2)) { bvz = -bvz; bz = FAR_Z; }
73
+ else { you++; if (you >= 7) { state = OVER; } else serve(0); }
74
+ }
75
+ prev_pad = pad;
76
+ }
77
+
78
+ static void render(void)
79
+ {
80
+ Vec3 a,b,c,d;
81
+ psx_clear(RGB(20, 40, 30));
82
+ psx_camera(0, FIX(4), FIX(-1), 0, FIXF(-0.30f)); /* look down the court */
83
+
84
+ /* court floor */
85
+ a.x=-COURT_HW; a.y=0; a.z=NEAR_Z; b.x=COURT_HW; b.y=0; b.z=NEAR_Z;
86
+ c.x=COURT_HW; c.y=0; c.z=FAR_Z; d.x=-COURT_HW; d.y=0; d.z=FAR_Z;
87
+ psx_quad3d_nc(a,b,c,d, RGB(40, 90, 60));
88
+ /* center line */
89
+ a.x=-COURT_HW; a.z=(NEAR_Z+FAR_Z)>>1; b.x=COURT_HW; b.z=a.z;
90
+ c.x=COURT_HW; c.z=a.z+FIXF(0.3f); d.x=-COURT_HW; d.z=c.z;
91
+ a.y=FIXF(0.02f);b.y=a.y;c.y=a.y;d.y=a.y;
92
+ psx_quad3d_nc(a,b,c,d, RGB(220,220,220));
93
+
94
+ draw_cube_at(px, FIXF(0.5f), NEAR_Z, FIXF(0.9f), RGB(80, 180, 255)); /* you */
95
+ draw_cube_at(cx, FIXF(0.5f), FAR_Z, FIXF(0.9f), RGB(255, 100, 80)); /* cpu */
96
+ draw_cube_at(bx, FIXF(0.5f), bz, FIXF(0.4f), RGB(255, 240, 120)); /* ball */
97
+
98
+ psx_number(40, 8, (unsigned)you, RGB(80, 180, 255));
99
+ psx_number(240, 8, (unsigned)cpu, RGB(255, 100, 80));
100
+ }
101
+
102
+ int main(void)
103
+ {
104
+ psx_init();
105
+ psx_srand(0x5A11);
106
+ state = TITLE; prev_pad = 0;
107
+ for (;;) {
108
+ unsigned int pad = psx_pad();
109
+ if (state == TITLE) {
110
+ static fix t; t += FIX(3);
111
+ psx_clear(RGB(15, 30, 25));
112
+ psx_camera(0, 0, FIX(-1), 0, 0);
113
+ draw_cube_at(0, 0, FIX(6), FIX(1), RGB(80, 180, 255));
114
+ if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
115
+ prev_pad = pad;
116
+ } else if (state == PLAY) { update(); render(); }
117
+ else {
118
+ psx_clear(RGB(8, 20, 16));
119
+ psx_number(120, 100, (unsigned)you, RGB(80, 180, 255));
120
+ psx_number(160, 100, (unsigned)cpu, RGB(255, 100, 80));
121
+ if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
122
+ prev_pad = pad;
123
+ }
124
+ psx_vsync();
125
+ }
126
+ return 0;
127
+ }
@@ -0,0 +1,38 @@
1
+ /*
2
+ * sprite_move/main.c — PS1 starter (mips-elf-gcc + the bundled psx.h helper).
3
+ *
4
+ * Boots a visible 320x240 display and animates a colored box bouncing around the
5
+ * screen — the canonical "GPU is alive, here's a moving primitive" PS1 starter.
6
+ * Exercises the whole helper lib: psx_init (GPU bring-up), psx_clear + psx_rect
7
+ * (flat polygons), psx_vsync (pacing).
8
+ *
9
+ * Build with: build({ platform:"ps1", language:"c" }) — language defaults to C.
10
+ * Output is a PS-EXE the HLE BIOS loads at 0x80010000; main() loops forever
11
+ * (no OS to return to).
12
+ *
13
+ * PS1 NOTE: there is no tile/sprite/nametable hardware — the GPU draws polygons
14
+ * into a framebuffer. A "sprite" here is a textured/flat quad you draw each frame.
15
+ */
16
+ #include "psx.h"
17
+
18
+ int main(void)
19
+ {
20
+ int x = 40, y = 40, dx = 2, dy = 2;
21
+ const int W = 32, H = 32;
22
+
23
+ psx_init();
24
+
25
+ for (;;) {
26
+ /* move + bounce */
27
+ x += dx; y += dy;
28
+ if (x < 0 || x + W > 320) dx = -dx;
29
+ if (y < 0 || y + H > 240) dy = -dy;
30
+ if (x < 0) x = 0; if (x + W > 320) x = 320 - W;
31
+ if (y < 0) y = 0; if (y + H > 240) y = 240 - H;
32
+
33
+ psx_clear(RGB(20, 30, 60)); /* deep-blue background */
34
+ psx_rect(x, y, W, H, RGB(255, 180, 40)); /* the bouncing box */
35
+ psx_vsync();
36
+ }
37
+ return 0;
38
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "romdevtools",
3
- "version": "0.43.0",
4
- "description": "Tool server giving coding agents full control of homebrew ROM development AND reverse-engineering/romhacking across 14 retro platforms (NES, SNES, GB, Genesis, Atari, C64, PC Engine, MSX, ...) via WASM toolchains + emulator cores. Use over plain HTTP, as an Agent Skill, or as an MCP server.",
3
+ "version": "0.56.1",
4
+ "description": "Tool server giving coding agents full control of homebrew ROM development AND reverse-engineering/romhacking across 16 retro platforms (NES, SNES, GB, Genesis, Atari, C64, PC Engine, MSX, PlayStation, N64, ...) via WASM toolchains + emulator cores. Use over plain HTTP, as an Agent Skill, or as an MCP server.",
5
5
  "type": "module",
6
6
  "main": "src/mcp/server.js",
7
7
  "bin": {
@@ -58,6 +58,8 @@
58
58
  "romdev-core-geargrafx": "0.7.0",
59
59
  "romdev-core-gpgx": "0.12.0",
60
60
  "romdev-core-handy": "0.7.0",
61
+ "romdev-core-parallel-n64": "0.1.0",
62
+ "romdev-core-pcsx-rearmed": "0.1.0",
61
63
  "romdev-core-prosystem": "0.8.0",
62
64
  "romdev-core-vice": "0.9.0",
63
65
  "romdev-famitone": "0.1.0",
@@ -67,6 +69,7 @@
67
69
  "romdev-platform-snes": "0.8.0",
68
70
  "romdev-toolchain-cc65": "0.1.1",
69
71
  "romdev-toolchain-m68k-gcc": "0.2.0",
72
+ "romdev-toolchain-mips-gcc": "0.1.0",
70
73
  "romdev-toolchain-rgbds": "0.1.0",
71
74
  "romdev-toolchain-sdcc": "0.2.0",
72
75
  "romdev-toolchain-vasm": "0.1.0",
@@ -77,6 +80,10 @@
77
80
  "yauzl": "^3.3.1",
78
81
  "zod": "^4.4.3"
79
82
  },
83
+ "optionalDependencies": {
84
+ "native-gles": "^0.5.0",
85
+ "webgl-node": "^1.2.0"
86
+ },
80
87
  "files": [
81
88
  "src",
82
89
  "!src/**/*.test.js",