romdevtools 0.14.0 → 0.16.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 (104) hide show
  1. package/AGENTS.md +18 -11
  2. package/CHANGELOG.md +94 -0
  3. package/README.md +1 -1
  4. package/examples/atari2600/main.asm +1 -1
  5. package/examples/atari2600/templates/default.asm +1 -1
  6. package/examples/atari2600/templates/paddle.asm +59 -47
  7. package/examples/atari7800/main.c +1 -1
  8. package/examples/atari7800/templates/default.c +1 -1
  9. package/examples/atari7800/templates/music_demo.c +1 -1
  10. package/examples/c64/main.c +1 -1
  11. package/examples/c64/templates/platformer.c +2 -2
  12. package/examples/c64/templates/puzzle.c +1 -1
  13. package/examples/c64/templates/racing.c +3 -3
  14. package/examples/c64/templates/shmup.c +6 -5
  15. package/examples/c64/templates/sports.c +4 -4
  16. package/examples/gb/main.asm +1 -1
  17. package/examples/gb/main.c +1 -1
  18. package/examples/gb/templates/puzzle.c +1 -1
  19. package/examples/gb/templates/racing.c +1 -1
  20. package/examples/gb/templates/shmup.c +1 -1
  21. package/examples/gba/templates/gba_hello.c +1 -1
  22. package/examples/gba/templates/maxmod_demo.c +1 -1
  23. package/examples/gba/templates/puzzle.c +17 -3
  24. package/examples/gba/templates/racing.c +16 -2
  25. package/examples/gba/templates/shmup.c +23 -4
  26. package/examples/gba/templates/tonc_hello.c +6 -4
  27. package/examples/gbc/main.asm +1 -1
  28. package/examples/gbc/templates/puzzle.c +1 -1
  29. package/examples/gbc/templates/racing.c +1 -1
  30. package/examples/gbc/templates/shmup.c +1 -1
  31. package/examples/genesis/main.s +1 -1
  32. package/examples/genesis/templates/puzzle.c +1 -1
  33. package/examples/genesis/templates/racing.c +45 -1
  34. package/examples/genesis/templates/shmup.c +12 -3
  35. package/examples/genesis/templates/shmup_2p.c +2 -2
  36. package/examples/genesis/templates/sports.c +39 -0
  37. package/examples/gg/templates/hello_sprite.c +38 -23
  38. package/examples/gg/templates/music_demo.c +11 -8
  39. package/examples/gg/templates/platformer.c +37 -15
  40. package/examples/gg/templates/racing.c +25 -12
  41. package/examples/gg/templates/shmup.c +12 -6
  42. package/examples/gg/templates/sports.c +30 -16
  43. package/examples/gg/templates/tile_engine.c +24 -10
  44. package/examples/lynx/templates/platformer.c +7 -1
  45. package/examples/lynx/templates/puzzle.c +8 -2
  46. package/examples/lynx/templates/racing.c +7 -1
  47. package/examples/lynx/templates/sports.c +7 -1
  48. package/examples/nes/main.c +2 -2
  49. package/examples/nes/space-shooter/nes_runtime.h +1 -1
  50. package/examples/nes/templates/default.c +4 -1
  51. package/examples/nes/templates/racing.c +50 -1
  52. package/examples/pce/main.c +1 -1
  53. package/examples/sms/templates/hello_sprite.c +1 -1
  54. package/examples/sms/templates/music_demo.c +1 -1
  55. package/examples/sms/templates/puzzle.c +1 -1
  56. package/examples/sms/templates/racing.c +1 -1
  57. package/examples/sms/templates/shmup.c +1 -1
  58. package/examples/sms/templates/shmup_2p.c +2 -2
  59. package/examples/snes/main.asm +1 -1
  60. package/examples/snes/templates/c-hello-data.asm +309 -14
  61. package/examples/snes/templates/c-hello.c +13 -2
  62. package/examples/snes/templates/default.c +1 -1
  63. package/examples/snes/templates/hello_sprite-data.asm +300 -2
  64. package/examples/snes/templates/hello_sprite.c +10 -1
  65. package/examples/snes/templates/music_demo-data.asm +300 -2
  66. package/examples/snes/templates/music_demo.c +10 -1
  67. package/examples/snes/templates/platformer-data.asm +300 -2
  68. package/examples/snes/templates/platformer.c +10 -1
  69. package/examples/snes/templates/puzzle-data.asm +300 -2
  70. package/examples/snes/templates/puzzle.c +11 -1
  71. package/examples/snes/templates/racing-data.asm +300 -2
  72. package/examples/snes/templates/racing.c +40 -4
  73. package/examples/snes/templates/shmup-data.asm +299 -6
  74. package/examples/snes/templates/shmup.c +11 -7
  75. package/examples/snes/templates/sports-data.asm +300 -2
  76. package/examples/snes/templates/sports.c +40 -5
  77. package/package.json +1 -1
  78. package/src/http/skill-doc.js +1 -1
  79. package/src/http/tool-registry.js +1 -1
  80. package/src/mcp/tools/index.js +4 -4
  81. package/src/mcp/tools/project.js +33 -22
  82. package/src/mcp/tools/toolchain.js +196 -20
  83. package/src/observer/livestream.html +34 -4
  84. package/src/platforms/gg/MENTAL_MODEL.md +14 -13
  85. package/src/platforms/gg/lib/c/vdp_init.c +10 -8
  86. package/src/platforms/msx/MENTAL_MODEL.md +1 -1
  87. package/src/platforms/nes/TROUBLESHOOTING.md +1 -1
  88. package/src/platforms/nes/lib/c/nes_runtime.c +28 -6
  89. package/src/platforms/pce/MENTAL_MODEL.md +1 -1
  90. package/src/platforms/pce/lib/c/pce_hw.h +1 -0
  91. package/src/platforms/pce/lib/c/pce_video.c +26 -0
  92. package/src/platforms/sms/MENTAL_MODEL.md +12 -12
  93. package/src/platforms/sms/lib/c/vdp_init.c +10 -8
  94. package/src/platforms/sms/lib/vdp_init.s +1 -1
  95. package/src/toolchains/cc65/cc65.js +8 -1
  96. package/src/toolchains/cc65/presets/nes/chr-ram-runtime.cfg +1 -1
  97. package/src/toolchains/cc65/presets/nes/chr-ram.cfg +1 -1
  98. package/src/toolchains/cc65/presets/nes/chr-ram.crt0.s +1 -1
  99. package/src/toolchains/gba-c/gba-c.js +6 -1
  100. package/src/toolchains/genesis-c/README.md +1 -1
  101. package/src/toolchains/genesis-c/genesis-c.js +10 -2
  102. package/src/toolchains/parse-errors.js +67 -5
  103. package/src/toolchains/sdcc/preflight-lint.js +47 -7
  104. package/src/toolchains/snes-c/snes-c.js +3 -7
@@ -46,12 +46,18 @@ extern void gg_sat_upload(void);
46
46
  #define T_BULLET 1
47
47
  #define T_ENEMY 2
48
48
 
49
- static const uint8_t palette[32] = {
50
- 0x10,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
51
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
52
- /* Sprite palette: white, yellow, red */
53
- 0x00,0x3F,0x0F,0x03, 0x00,0x00,0x00,0x00,
54
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
49
+ /* GG palette = 32 entries × 2 bytes (4-4-4 BGR LE): low=(g<<4)|r, high=b.
50
+ * Entries 0-15 = BG, 16-31 = SPRITE. (The earlier 32-byte SMS-style array was
51
+ * the GG #1 invisible-sprite bug: gg_load_palette reads 64 bytes, so a 32-byte
52
+ * array left the sprite palette (entries 16-31) reading past the array = garbage
53
+ * = invisible sprites.) */
54
+ static const uint8_t palette[64] = {
55
+ /* BG 0-15: entry 0 = dark navy backdrop */
56
+ 0x20,0x02, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
57
+ 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
58
+ /* SPRITE 16-31: 16=transparent, 17=white, 18=yellow, 19=red */
59
+ 0,0, 0xFF,0x0F, 0xFF,0x00, 0x0F,0x00, 0,0, 0,0, 0,0, 0,0,
60
+ 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
55
61
  };
56
62
 
57
63
  static const uint8_t sprite_tiles[32 * 3] = {
@@ -17,19 +17,32 @@ extern void gg_sprite_init(void);
17
17
  extern void gg_sprite_set(uint8_t slot, uint8_t x, uint8_t y, uint8_t tile);
18
18
  extern void gg_sat_upload(void);
19
19
 
20
- #define COURT_TOP 8
21
- #define COURT_BOT 184
20
+ /* ── Game Gear visible viewport ──────────────────────────────────────
21
+ * Only the centered 160x144 of the 256x192 frame shows. Keep the whole
22
+ * court inside [VIS_X0..VIS_X1] x [VIS_Y0..VIS_Y1] or it's off-screen. */
23
+ #define VIS_X0 48
24
+ #define VIS_Y0 24
25
+ #define VIS_X1 207 /* 48 + 160 - 1 */
26
+ #define VIS_Y1 167 /* 24 + 144 - 1 */
27
+
28
+ #define COURT_TOP VIS_Y0
29
+ #define COURT_BOT VIS_Y1
22
30
  #define PADDLE_H 24
23
31
  #define BALL_SIZE 8
24
- #define PADDLE_X1 16
25
- #define PADDLE_X2 232
26
-
27
- static const uint8_t palette[32] = {
28
- 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30
- /* Sprite palette: white at idx 1 */
31
- 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32
+ #define PADDLE_X1 (VIS_X0 + 8) /* near the visible left edge */
33
+ #define PADDLE_X2 (VIS_X1 - 16) /* near the visible right edge */
34
+
35
+ /* GG palette = 32 entries × 2 bytes (4-4-4 BGR LE): low=(g<<4)|r, high=b.
36
+ * gg_load_palette reads 64 bytes; a 32-byte array leaves the sprite palette
37
+ * (entries 16-31) reading garbage = invisible sprites. Sprite colour 1 = entry
38
+ * 17 (white). */
39
+ static const uint8_t palette[64] = {
40
+ /* BG 0-15: entry 0 = dark navy backdrop */
41
+ 0x20,0x02, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
42
+ 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
43
+ /* SPRITE 16-31: 16=transparent, 17=white */
44
+ 0,0, 0xFF,0x0F, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
45
+ 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
33
46
  };
34
47
 
35
48
  static const uint8_t tile_solid[32] = {
@@ -45,15 +58,16 @@ static uint8_t score_p1, score_p2;
45
58
  static uint8_t serve_timer;
46
59
 
47
60
  static void serve_ball(uint8_t to_left) {
48
- bx = 124;
49
- by = 90;
61
+ bx = (VIS_X0 + VIS_X1) / 2;
62
+ by = (VIS_Y0 + VIS_Y1) / 2;
50
63
  bdx = to_left ? -2 : 2;
51
64
  bdy = ((score_p1 + score_p2) & 1) ? -1 : 1;
52
65
  serve_timer = 30;
53
66
  }
54
67
 
55
68
  static void reset_match(void) {
56
- p1y = 84; p2y = 84;
69
+ p1y = (VIS_Y0 + VIS_Y1) / 2 - PADDLE_H / 2;
70
+ p2y = p1y;
57
71
  score_p1 = 0; score_p2 = 0;
58
72
  serve_ball(0);
59
73
  }
@@ -130,8 +144,8 @@ void main(void) {
130
144
  sfx_tone(0, 250, 3);
131
145
  }
132
146
 
133
- if (bx < 4) { if (score_p2 < 9) score_p2++; sfx_noise(20); serve_ball(0); }
134
- if (bx > 252) { if (score_p1 < 9) score_p1++; sfx_tone(0, 180, 16); serve_ball(1); }
147
+ if (bx < VIS_X0) { if (score_p2 < 9) score_p2++; sfx_noise(20); serve_ball(0); }
148
+ if (bx > VIS_X1 - BALL_SIZE) { if (score_p1 < 9) score_p1++; sfx_tone(0, 180, 16); serve_ball(1); }
135
149
  }
136
150
  } while (1);
137
151
  }
@@ -30,15 +30,28 @@ extern void gg_sat_upload(void);
30
30
 
31
31
  #define T_OPEN 0
32
32
  #define T_WALL 1
33
- #define T_SPR 0 /* sprite tile uses sprite-tile-bank (R6=0xFB) */
34
-
35
- static const uint8_t palette[32] = {
36
- /* BG palette: backdrop blue, wall colour 1 dark grey, colour 2 light grey */
37
- 0x10,0x14,0x2A,0x00, 0x00,0x00,0x00,0x00,
38
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
39
- /* Sprite palette: white at idx 1 */
40
- 0x00,0x3F,0x00,0x00, 0x00,0x00,0x00,0x00,
41
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
33
+ #define T_SPR 0 /* sprite tile uses sprite-tile-bank (R6=0xFF → $2000) */
34
+
35
+ /* ── Game Gear visible viewport ──────────────────────────────────────
36
+ * The 32x24 name table fills the whole 256x192 frame, but only the
37
+ * centered 160x144 SHOWS: fetch pixels [VIS_X0..VIS_X1] x [VIS_Y0..VIS_Y1]
38
+ * (tile columns 6..25, rows 3..20). Place the player sprite inside it. */
39
+ #define VIS_X0 48
40
+ #define VIS_Y0 24
41
+ #define VIS_X1 207 /* 48 + 160 - 1 */
42
+ #define VIS_Y1 167 /* 24 + 144 - 1 */
43
+
44
+ /* GG palette = 32 entries × 2 bytes (4-4-4 BGR LE): low=(g<<4)|r, high=b.
45
+ * gg_load_palette reads 64 bytes; a 32-byte array leaves the sprite palette
46
+ * (entries 16-31) reading garbage = invisible sprites. BG colour 1 = entry 1
47
+ * (dark grey wall); sprite colour 1 = entry 17 (white player). */
48
+ static const uint8_t palette[64] = {
49
+ /* BG 0-15: entry 0 = dark navy backdrop, entry 1 = dark grey wall */
50
+ 0x20,0x02, 0x66,0x06, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
51
+ 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
52
+ /* SPRITE 16-31: 16=transparent, 17=white player */
53
+ 0,0, 0xFF,0x0F, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
54
+ 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
42
55
  };
43
56
 
44
57
  static const uint8_t bg_tiles[32 * 2] = {
@@ -92,7 +105,8 @@ static uint8_t solid_at(int16_t px, int16_t py) {
92
105
  }
93
106
 
94
107
  void main(void) {
95
- int16_t px = 16, py = 16;
108
+ /* Start the player inside the visible window (tile ~10,10 = pixel 80,80). */
109
+ int16_t px = 80, py = 80;
96
110
 
97
111
  gg_vdp_init();
98
112
  gg_load_palette(palette);
@@ -43,7 +43,13 @@ void main(void) {
43
43
  sfx_init();
44
44
 
45
45
  for (;;) {
46
- tgi_clear();
46
+ /* Lynx frame loop: WAIT for the blitter, then clear with a full-screen
47
+ * tgi_bar (NOT tgi_clear, which leaves the back page stale on this core)
48
+ * — drawing while the blitter is mid-flight loses the frame → black.
49
+ * (Copied from the shmup scaffold, the LYNX-1 fix.) */
50
+ while (tgi_busy()) { }
51
+ tgi_setcolor(COLOR_BLACK);
52
+ tgi_bar(0, 0, tgi_getmaxx(), tgi_getmaxy());
47
53
  tgi_setcolor(COLOR_GREY);
48
54
  for (i = 0; i < N_PLATFORMS; i++) {
49
55
  tgi_bar(platforms[i].x, platforms[i].y, platforms[i].x + platforms[i].w - 1, platforms[i].y + platforms[i].h - 1);
@@ -62,7 +62,7 @@ static void lock_piece(void) {
62
62
  a = grid[r][c]; b = grid[r][c+1]; d = grid[r][c+2];
63
63
  if (a != 0 && a == b && b == d) {
64
64
  grid[r][c] = 0; grid[r][c+1] = 0; grid[r][c+2] = 0;
65
- if (score < 65500) score += 30;
65
+ if (score < 65500u) score += 30;
66
66
  sfx_tone(0, 60, 10);
67
67
  }
68
68
  }
@@ -93,7 +93,13 @@ void main(void) {
93
93
  new_piece();
94
94
 
95
95
  for (;;) {
96
- tgi_clear();
96
+ /* Lynx frame loop: WAIT for the blitter, then clear with a full-screen
97
+ * tgi_bar (NOT tgi_clear, which leaves the back page stale on this core)
98
+ * — drawing while the blitter is mid-flight loses the frame → black.
99
+ * (Copied from the shmup scaffold, the LYNX-1 fix.) */
100
+ while (tgi_busy()) { }
101
+ tgi_setcolor(COLOR_BLACK);
102
+ tgi_bar(0, 0, tgi_getmaxx(), tgi_getmaxy());
97
103
  /* grid */
98
104
  for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) {
99
105
  if (grid[r][c] != 0) {
@@ -31,7 +31,13 @@ void main(void) {
31
31
  for (i = 0; i < MAX_OBS; i++) obs[i].alive = 0;
32
32
 
33
33
  for (;;) {
34
- tgi_clear();
34
+ /* Lynx frame loop: WAIT for the blitter, then clear with a full-screen
35
+ * tgi_bar (NOT tgi_clear, which leaves the back page stale on this core)
36
+ * — drawing while the blitter is mid-flight loses the frame → black.
37
+ * (Copied from the shmup scaffold, the LYNX-1 fix.) */
38
+ while (tgi_busy()) { }
39
+ tgi_setcolor(COLOR_BLACK);
40
+ tgi_bar(0, 0, tgi_getmaxx(), tgi_getmaxy());
35
41
  /* lane lines */
36
42
  tgi_setcolor(COLOR_DARKGREY);
37
43
  tgi_line(28, 0, 28, 101);
@@ -28,7 +28,13 @@ void main(void) {
28
28
  sfx_init();
29
29
 
30
30
  for (;;) {
31
- tgi_clear();
31
+ /* Lynx frame loop: WAIT for the blitter, then clear with a full-screen
32
+ * tgi_bar (NOT tgi_clear, which leaves the back page stale on this core)
33
+ * — drawing while the blitter is mid-flight loses the frame → black.
34
+ * (Copied from the shmup scaffold, the LYNX-1 fix.) */
35
+ while (tgi_busy()) { }
36
+ tgi_setcolor(COLOR_BLACK);
37
+ tgi_bar(0, 0, tgi_getmaxx(), tgi_getmaxy());
32
38
  tgi_setcolor(COLOR_WHITE);
33
39
  tgi_bar(PADDLE_X1, (unsigned)p1y, PADDLE_X1 + PADDLE_W - 1, (unsigned)(p1y + PADDLE_H - 1));
34
40
  tgi_bar(PADDLE_X2, (unsigned)p2y, PADDLE_X2 + PADDLE_W - 1, (unsigned)(p2y + PADDLE_H - 1));
@@ -9,9 +9,9 @@
9
9
  * 5. Enables the PPU and loops forever (NMI does all the real work).
10
10
  *
11
11
  * BUILD: this file expects linkerConfig: "chr-ram" so the CHR-RAM
12
- * region is writable at runtime. Pass it to buildSource:
12
+ * region is writable at runtime. Pass it to build:
13
13
  *
14
- * buildSource({
14
+ * build({ output: "rom",
15
15
  * platform: "nes",
16
16
  * language: "c",
17
17
  * linkerConfig: "chr-ram",
@@ -1,5 +1,5 @@
1
1
  /* ── nes_runtime.h — neslib-shaped runtime for cc65 NES builds ───
2
- * Auto-included on every `buildSource({platform:"nes", language:"c"})`.
2
+ * Auto-included on every `build({ output: "rom", platform:"nes", language:"c"})`.
3
3
  *
4
4
  * API mirrors Shiru's neslib so existing tutorials port cleanly:
5
5
  *
@@ -13,7 +13,10 @@
13
13
 
14
14
  #include "nes_runtime.h"
15
15
 
16
- static const uint8_t bg_colors[4] = { 0x0F, 0x01, 0x21, 0x31 };
16
+ /* 4 VISIBLE hues never 0x0F (black), so a screenshot on ANY phase of the
17
+ * cycle shows a non-black backdrop (NES-2: the old cycle started on + passed
18
+ * through black, so a fresh agent's screenshot could land on a "blank" frame). */
19
+ static const uint8_t bg_colors[4] = { 0x21, 0x01, 0x11, 0x31 };
17
20
 
18
21
  void main(void) {
19
22
  uint8_t palette[32];
@@ -56,6 +56,26 @@ static const uint8_t tile_digits[10 * 16] = {
56
56
  #define T_CAR_ENEMY 2
57
57
  #define T_DIGIT0 3
58
58
 
59
+ /* ── Background road tiles ───────────────────────────────────────────
60
+ * Default PPUCTRL ($90) reads BG patterns from pattern table 1 ($1000),
61
+ * so these go to CHR $1000+ and are indexed independently of the sprite
62
+ * tiles above. Colour 1 (white, BG palette 0) draws the markings; the
63
+ * grey backdrop (colour 0) is the road surface.
64
+ *
65
+ * BG_T_EDGE: a solid 2px vertical stripe — the road shoulder line.
66
+ * BG_T_LANE: a 2px vertical dash (on for 4 rows, off for 4) — the
67
+ * dashed centre lane marking when stacked down a column. */
68
+ #define BG_T_EDGE 1
69
+ #define BG_T_LANE 2
70
+ static const uint8_t bg_tile_edge[16] = {
71
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* plane 0 (colour bit 0) */
72
+ 0, 0, 0, 0, 0, 0, 0, 0,
73
+ };
74
+ static const uint8_t bg_tile_lane[16] = {
75
+ 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* dash: 4 on, 4 off */
76
+ 0, 0, 0, 0, 0, 0, 0, 0,
77
+ };
78
+
59
79
  static const uint8_t palette[32] = {
60
80
  /* BG palettes — light grey backdrop simulates road */
61
81
  0x10, 0x30, 0x16, 0x12,
@@ -93,6 +113,29 @@ static uint8_t aabb(Car *a, Car *b) {
93
113
  && a->y < b->y + 8 && a->y + 8 > b->y;
94
114
  }
95
115
 
116
+ /* Draw the static road into nametable 0 ($2000): solid shoulder lines on
117
+ * the outside of the outer lanes and dashed dividers between the three
118
+ * lanes. PPU must be OFF — call from init (uses vram_unsafe_set). Tile
119
+ * columns: lanes sit at 11/15/19, so dividers go at 13/17 and shoulders
120
+ * just outside at 9/21. */
121
+ #define ROAD_TOP_ROW 2
122
+ #define ROAD_BOT_ROW 27
123
+ #define ROAD_EDGE_L 9
124
+ #define ROAD_EDGE_R 21
125
+ #define ROAD_DIV_1 13
126
+ #define ROAD_DIV_2 17
127
+ static void draw_road(void) {
128
+ uint8_t row;
129
+ uint16_t base;
130
+ for (row = ROAD_TOP_ROW; row <= ROAD_BOT_ROW; row++) {
131
+ base = (uint16_t)(0x2000 + (uint16_t)row * 32);
132
+ vram_unsafe_set((uint16_t)(base + ROAD_EDGE_L), BG_T_EDGE);
133
+ vram_unsafe_set((uint16_t)(base + ROAD_EDGE_R), BG_T_EDGE);
134
+ vram_unsafe_set((uint16_t)(base + ROAD_DIV_1), BG_T_LANE);
135
+ vram_unsafe_set((uint16_t)(base + ROAD_DIV_2), BG_T_LANE);
136
+ }
137
+ }
138
+
96
139
  static void reset_run(void) {
97
140
  uint8_t i;
98
141
  player_lane = 1;
@@ -138,7 +181,13 @@ void main(void) {
138
181
  chr_ram_upload(T_CAR_ENEMY * 16, tile_car_enemy, 16);
139
182
  chr_ram_upload(T_DIGIT0 * 16, tile_digits, sizeof(tile_digits));
140
183
 
184
+ /* BG road tiles live in pattern table 1 ($1000) — that's where the
185
+ * default PPUCTRL ($90) tells the PPU to read background patterns. */
186
+ chr_ram_upload((uint16_t)(0x1000 + BG_T_EDGE * 16), bg_tile_edge, 16);
187
+ chr_ram_upload((uint16_t)(0x1000 + BG_T_LANE * 16), bg_tile_lane, 16);
188
+
141
189
  palette_load(palette);
190
+ draw_road(); /* paint the static road while the PPU is off */
142
191
  oam_clear();
143
192
  ppu_on_all();
144
193
  sound_init();
@@ -198,6 +247,6 @@ void main(void) {
198
247
  }
199
248
  }
200
249
 
201
- if (score < 65500) score++;
250
+ if (score < 65500u) score++;
202
251
  }
203
252
  }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Boots to a guaranteed-VISIBLE screen using cc65's conio (text) library, which
5
5
  * initializes the HuC6270 VDC + HuC6260 VCE for you and uploads a font to VRAM.
6
- * Build with: buildSource({ platform: "pce" }) (language defaults to C).
6
+ * Build with: build({ output: "rom", platform: "pce" }) (language defaults to C).
7
7
  *
8
8
  * FOOTGUN — the empty-BSS crt0 trap (cc65 pce/crt0.s line 84):
9
9
  * The PCE crt0 clears .bss with `tii __BSS_RUN__, __BSS_RUN__+1, __BSS_SIZE__-1`.
@@ -11,7 +11,7 @@
11
11
  * - 64 sprite slots × 4 bytes (Y / X / tile / unused)
12
12
  *
13
13
  * Multi-file project — main.c plus the runtime .c files. Build with:
14
- * buildSource({platform:"sms", language:"c",
14
+ * build({ output: "rom", platform:"sms", language:"c",
15
15
  * sources: { "main.c": ..., "vdp_init.c": ..., ... },
16
16
  * includes: { "sms_hw.h": ... }})
17
17
  *
@@ -12,7 +12,7 @@
12
12
  * them by name-table index).
13
13
  *
14
14
  * Build via createProject({platform:"sms", template:"music_demo"}) or
15
- * buildSource({platform:"sms", language:"c", sources:{ "main.c": ...,
15
+ * build({ output: "rom", platform:"sms", language:"c", sources:{ "main.c": ...,
16
16
  * "sms_music.c": ... , ...runtime... }}).
17
17
  */
18
18
 
@@ -126,7 +126,7 @@ static void lock_piece(void) {
126
126
  a = grid[r][c]; b = grid[r][c + 1]; d = grid[r][c + 2];
127
127
  if (a != 0 && a == b && b == d) {
128
128
  grid[r][c] = 0; grid[r][c + 1] = 0; grid[r][c + 2] = 0;
129
- if (score < 65500) score = (uint16_t)(score + 30);
129
+ if (score < 65500u) score = (uint16_t)(score + 30);
130
130
  sfx_tone(0, 200, 10); /* triple-clear chime */
131
131
  }
132
132
  }
@@ -156,6 +156,6 @@ void main(void) {
156
156
  }
157
157
  }
158
158
 
159
- if (score < 65500) score = (uint16_t)(score + 1);
159
+ if (score < 65500u) score = (uint16_t)(score + 1);
160
160
  } while (1);
161
161
  }
@@ -157,7 +157,7 @@ void main(void) {
157
157
  if (aabb(&bullets[i], &enemies[j])) {
158
158
  bullets[i].alive = 0;
159
159
  enemies[j].alive = 0;
160
- if (score < 65500) score = (uint16_t)(score + 10);
160
+ if (score < 65500u) score = (uint16_t)(score + 10);
161
161
  sfx_noise(8);
162
162
  break;
163
163
  }
@@ -199,7 +199,7 @@ void main(void) {
199
199
  if (p1_bullets[i].alive && aabb(&p1_bullets[i], &enemies[j])) {
200
200
  p1_bullets[i].alive = 0;
201
201
  enemies[j].alive = 0;
202
- if (score_p1 < 65500) score_p1 = (uint16_t)(score_p1 + 10);
202
+ if (score_p1 < 65500u) score_p1 = (uint16_t)(score_p1 + 10);
203
203
  sfx_noise(8);
204
204
  break;
205
205
  }
@@ -209,7 +209,7 @@ void main(void) {
209
209
  if (p2_bullets[i].alive && aabb(&p2_bullets[i], &enemies[j])) {
210
210
  p2_bullets[i].alive = 0;
211
211
  enemies[j].alive = 0;
212
- if (score_p2 < 65500) score_p2 = (uint16_t)(score_p2 + 10);
212
+ if (score_p2 < 65500u) score_p2 = (uint16_t)(score_p2 + 10);
213
213
  sfx_noise(8);
214
214
  break;
215
215
  }
@@ -20,7 +20,7 @@
20
20
  ; - nmi_safe.asm: vblank handler skeleton
21
21
  ;
22
22
  ; BUILD: complete LoROM image, no extra options needed.
23
- ; buildSource({ platform: "snes", source: /* this file */ });
23
+ ; build({ output: "rom", platform: "snes", source: /* this file */ });
24
24
 
25
25
  lorom
26
26