romdevtools 0.16.0 → 0.21.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 (110) hide show
  1. package/AGENTS.md +60 -12
  2. package/CHANGELOG.md +258 -0
  3. package/examples/README.md +2 -0
  4. package/examples/atari2600/templates/platformer.asm +460 -0
  5. package/examples/atari2600/templates/racing.asm +463 -0
  6. package/examples/atari2600/templates/shmup.asm +386 -0
  7. package/examples/atari2600/templates/sports.asm +362 -0
  8. package/examples/atari7800/templates/default.c +49 -5
  9. package/examples/atari7800/templates/platformer.c +43 -4
  10. package/examples/atari7800/templates/puzzle.c +39 -4
  11. package/examples/atari7800/templates/racing.c +39 -4
  12. package/examples/atari7800/templates/shmup.c +40 -2
  13. package/examples/atari7800/templates/sports.c +36 -5
  14. package/examples/c64/templates/platformer.c +19 -5
  15. package/examples/c64/templates/puzzle.c +32 -2
  16. package/examples/c64/templates/shmup.c +28 -2
  17. package/examples/c64/templates/sports.c +30 -2
  18. package/examples/gb/templates/default.c +110 -16
  19. package/examples/gb/templates/platformer.c +25 -4
  20. package/examples/gb/templates/puzzle.c +32 -2
  21. package/examples/gb/templates/racing.c +72 -8
  22. package/examples/gb/templates/shmup.c +38 -1
  23. package/examples/gb/templates/sports.c +48 -1
  24. package/examples/gba/templates/gba_hello.c +29 -11
  25. package/examples/gba/templates/puzzle.c +15 -3
  26. package/examples/gba/templates/racing.c +65 -3
  27. package/examples/gba/templates/shmup.c +41 -4
  28. package/examples/gba/templates/sports.c +36 -2
  29. package/examples/gba/templates/tonc_hello.c +41 -5
  30. package/examples/gbc/templates/default.c +103 -26
  31. package/examples/gbc/templates/platformer.c +25 -4
  32. package/examples/gbc/templates/puzzle.c +32 -2
  33. package/examples/gbc/templates/racing.c +85 -19
  34. package/examples/gbc/templates/shmup.c +34 -1
  35. package/examples/gbc/templates/sports.c +45 -1
  36. package/examples/genesis/templates/puzzle.c +37 -3
  37. package/examples/genesis/templates/racing.c +44 -11
  38. package/examples/genesis/templates/sgdk_hello.c +34 -1
  39. package/examples/genesis/templates/shmup.c +31 -1
  40. package/examples/gg/templates/default.c +56 -18
  41. package/examples/gg/templates/platformer.c +18 -12
  42. package/examples/gg/templates/puzzle.c +38 -7
  43. package/examples/gg/templates/racing.c +51 -5
  44. package/examples/gg/templates/shmup.c +47 -3
  45. package/examples/gg/templates/sports.c +46 -3
  46. package/examples/lynx/templates/default.c +39 -8
  47. package/examples/lynx/templates/puzzle.c +28 -1
  48. package/examples/lynx/templates/racing.c +34 -7
  49. package/examples/lynx/templates/shmup.c +42 -3
  50. package/examples/lynx/templates/sports.c +29 -2
  51. package/examples/msx/platformer/main.c +213 -0
  52. package/examples/msx/puzzle/main.c +250 -0
  53. package/examples/msx/racing/main.c +249 -0
  54. package/examples/msx/shmup/main.c +288 -0
  55. package/examples/msx/sports/main.c +182 -0
  56. package/examples/nes/templates/default.c +67 -19
  57. package/examples/nes/templates/platformer.c +65 -6
  58. package/examples/nes/templates/puzzle.c +67 -6
  59. package/examples/nes/templates/racing.c +45 -13
  60. package/examples/nes/templates/shmup.c +51 -2
  61. package/examples/nes/templates/sports.c +51 -6
  62. package/examples/pce/platformer/main.c +283 -0
  63. package/examples/pce/puzzle/main.c +304 -0
  64. package/examples/pce/racing/main.c +304 -0
  65. package/examples/pce/shmup/main.c +346 -0
  66. package/examples/pce/sports/main.c +254 -0
  67. package/examples/sms/main.c +35 -6
  68. package/examples/sms/templates/puzzle.c +34 -5
  69. package/examples/sms/templates/racing.c +39 -2
  70. package/examples/sms/templates/shmup.c +41 -2
  71. package/examples/sms/templates/sports.c +43 -2
  72. package/examples/snes/templates/default.c +50 -28
  73. package/examples/snes/templates/platformer-data.asm +22 -0
  74. package/examples/snes/templates/platformer.c +16 -1
  75. package/examples/snes/templates/puzzle-data.asm +22 -0
  76. package/examples/snes/templates/puzzle.c +17 -1
  77. package/examples/snes/templates/racing-data.asm +22 -0
  78. package/examples/snes/templates/racing.c +17 -1
  79. package/examples/snes/templates/shmup-data.asm +22 -0
  80. package/examples/snes/templates/shmup.c +20 -1
  81. package/examples/snes/templates/sports-data.asm +22 -0
  82. package/examples/snes/templates/sports.c +16 -1
  83. package/package.json +1 -1
  84. package/src/cores/wasm/vice_x64_libretro.js +1 -1
  85. package/src/cores/wasm/vice_x64_libretro.wasm +0 -0
  86. package/src/host/LibretroHost.js +122 -1
  87. package/src/host/callbacks.js +9 -1
  88. package/src/host/types.js +15 -8
  89. package/src/http/tool-registry.js +26 -1
  90. package/src/mcp/tools/cart-parts.js +75 -3
  91. package/src/mcp/tools/disasm-rebuild.js +507 -0
  92. package/src/mcp/tools/disasm.js +95 -6
  93. package/src/mcp/tools/frame.js +168 -3
  94. package/src/mcp/tools/lifecycle.js +4 -2
  95. package/src/mcp/tools/project.js +54 -9
  96. package/src/mcp/tools/state.js +201 -14
  97. package/src/mcp/tools/toolchain.js +76 -3
  98. package/src/mcp/tools/watch-memory.js +125 -14
  99. package/src/platforms/c64/MENTAL_MODEL.md +45 -1
  100. package/src/platforms/c64/d64.js +281 -0
  101. package/src/platforms/gb/MENTAL_MODEL.md +10 -0
  102. package/src/platforms/msx/MENTAL_MODEL.md +10 -6
  103. package/src/platforms/nes/MENTAL_MODEL.md +63 -2
  104. package/src/platforms/pce/MENTAL_MODEL.md +9 -4
  105. package/src/platforms/pce/lib/c/pce_video.c +1 -1
  106. package/src/rom-id/identifier.js +15 -0
  107. package/src/toolchains/cc65/ines.js +145 -0
  108. package/src/toolchains/cc65/presets/nes/chr-rom.cfg +83 -0
  109. package/src/toolchains/cc65/presets/nes/chr-rom.crt0.s +153 -0
  110. package/src/toolchains/common/reassemble.js +10 -2
@@ -25,16 +25,19 @@ extern uint8_t sms_joypad_read(void);
25
25
  #define T_R 1
26
26
  #define T_G 2
27
27
  #define T_B 3
28
+ #define T_WALL 4 /* well border */
29
+ #define T_FIELD 5 /* empty well interior */
28
30
 
29
31
  static const uint8_t palette[32] = {
30
- /* BG palette: backdrop black, red, green, blue */
31
- 0x00,0x03,0x0C,0x30, 0x00,0x00,0x00,0x00,
32
+ /* BG palette: 0 backdrop navy, 1 red, 2 green, 3 blue, 4 wall grey,
33
+ * 5 dim field blue */
34
+ 0x10,0x03,0x0C,0x30, 0x15,0x14, 0x00,0x00,
32
35
  0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
33
36
  0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
34
37
  0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
35
38
  };
36
39
 
37
- static const uint8_t bg_tiles[32 * 4] = {
40
+ static const uint8_t bg_tiles[32 * 6] = {
38
41
  /* T_BLANK */
39
42
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
40
43
  0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
@@ -53,6 +56,16 @@ static const uint8_t bg_tiles[32 * 4] = {
53
56
  0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
54
57
  0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
55
58
  0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
59
+ /* T_WALL — colour 4 fill (plane 2 set) */
60
+ 0x00,0x00,0xFF,0x00, 0x00,0x00,0xFF,0x00,
61
+ 0x00,0x00,0xFF,0x00, 0x00,0x00,0xFF,0x00,
62
+ 0x00,0x00,0xFF,0x00, 0x00,0x00,0xFF,0x00,
63
+ 0x00,0x00,0xFF,0x00, 0x00,0x00,0xFF,0x00,
64
+ /* T_FIELD — colour 5 fill (planes 0+2 set) = dim field */
65
+ 0xFF,0x00,0xFF,0x00, 0xFF,0x00,0xFF,0x00,
66
+ 0xFF,0x00,0xFF,0x00, 0xFF,0x00,0xFF,0x00,
67
+ 0xFF,0x00,0xFF,0x00, 0xFF,0x00,0xFF,0x00,
68
+ 0xFF,0x00,0xFF,0x00, 0xFF,0x00,0xFF,0x00,
56
69
  };
57
70
 
58
71
  static uint8_t grid[ROWS][COLS];
@@ -76,7 +89,7 @@ static uint8_t tile_for(uint8_t c) {
76
89
  if (c == 1) return T_R;
77
90
  if (c == 2) return T_G;
78
91
  if (c == 3) return T_B;
79
- return T_BLANK;
92
+ return T_FIELD; /* empty cell shows the dim well interior, not backdrop */
80
93
  }
81
94
 
82
95
  static void draw_cell(int8_t col, int8_t row, uint8_t cell) {
@@ -85,6 +98,21 @@ static void draw_cell(int8_t col, int8_t row, uint8_t cell) {
85
98
  sms_set_tilemap_cell((uint8_t)(row + 1), (uint8_t)(col + 7), tile_for(cell), 0);
86
99
  }
87
100
 
101
+ /* Draw the well: a grey border frame around the 6x12 play field with a dim
102
+ * field interior, so the playfield is clearly visible even when empty.
103
+ * The grid maps cell (col,row) -> tilemap (row+1, col+7), i.e. rows 1..12
104
+ * cols 7..12. Frame the perimeter at rows 0..13, cols 6..13. */
105
+ static void draw_well(void) {
106
+ uint8_t r, c;
107
+ for (r = 0; r <= 13; r++) {
108
+ for (c = 6; c <= 13; c++) {
109
+ uint8_t t = T_FIELD;
110
+ if (r == 0 || r == 13 || c == 6 || c == 13) t = T_WALL;
111
+ sms_set_tilemap_cell(r, c, t, 0);
112
+ }
113
+ }
114
+ }
115
+
88
116
  static void draw_grid(void) {
89
117
  int8_t r, c;
90
118
  for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) draw_cell(c, r, grid[r][c]);
@@ -151,7 +179,7 @@ void main(void) {
151
179
 
152
180
  sms_vdp_init();
153
181
  sms_load_palette(palette);
154
- sms_load_tiles(0x0000, bg_tiles, 32 * 4);
182
+ sms_load_tiles(0x0000, bg_tiles, 32 * 6);
155
183
 
156
184
  for (r = 0; r < 24; r++) for (c = 0; c < 32; c++) sms_set_tilemap_cell(r, c, T_BLANK, 0);
157
185
  for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) grid[r][c] = 0;
@@ -159,6 +187,7 @@ void main(void) {
159
187
  score = 0;
160
188
  fall_timer = 0;
161
189
  new_piece();
190
+ draw_well();
162
191
  draw_grid();
163
192
 
164
193
  sfx_init();
@@ -18,6 +18,7 @@ extern void sms_vdp_init(void);
18
18
  extern void sms_vdp_display_on(void);
19
19
  extern void sms_load_palette(const uint8_t *palette);
20
20
  extern void sms_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
21
+ extern void sms_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
21
22
  extern void sms_vblank_wait(void);
22
23
  extern uint8_t sms_joypad_read(void);
23
24
  extern void sms_sprite_init(void);
@@ -31,13 +32,47 @@ extern void sms_sat_upload(void);
31
32
  #define MAX_OBSTACLES 4
32
33
 
33
34
  static const uint8_t palette[32] = {
34
- 0x10, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35
+ /* BG: 0 = dark navy backdrop, 1 = grass green, 2 = road grey */
36
+ 0x10, 0x08, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00,
35
37
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
36
38
  /* Sprite palette: white (1), red (2) */
37
39
  0x00, 0x3F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
38
40
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39
41
  };
40
42
 
43
+ /* Three BG tiles for the track, loaded into the BG tile bank at $0000:
44
+ * tile 0 = blank (backdrop), tile 1 = grass (colour 1), tile 2 = road
45
+ * (colour 2). The track fills the whole 32x24 SMS screen so the display
46
+ * is a clear road scene, not a flat backdrop. */
47
+ static const uint8_t bg_tiles[96] = {
48
+ /* BG tile 0 = blank */
49
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
50
+ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
51
+ /* BG tile 1 = grass (colour 1 -> plane 0 set) */
52
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
53
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
54
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
55
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
56
+ /* BG tile 2 = road (colour 2 -> plane 1 set) */
57
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
58
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
59
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
60
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
61
+ };
62
+
63
+ /* Paint the whole 32x24 SMS screen: grey road down the centre lanes,
64
+ * green grass on the shoulders. BG tile bank is $0000. The road spans the
65
+ * three lanes (player X 72..184 -> roughly cols 8..23). */
66
+ static void draw_track(void) {
67
+ uint8_t row, col;
68
+ for (row = 0; row < 24; row++) {
69
+ for (col = 0; col < 32; col++) {
70
+ uint8_t road = (col >= 8 && col <= 23);
71
+ sms_set_tilemap_cell(row, col, road ? 2 : 1, 0);
72
+ }
73
+ }
74
+ }
75
+
41
76
  /* Two sprite tiles — player (colour 1) + enemy (colour 2). */
42
77
  static const uint8_t tiles[64] = {
43
78
  /* Tile 0 = player car (colour 1 → plane 0 set) */
@@ -97,7 +132,9 @@ void main(void) {
97
132
  uint8_t i;
98
133
  sms_vdp_init();
99
134
  sms_load_palette(palette);
100
- sms_load_tiles(0x2000, tiles, 64);
135
+ sms_load_tiles(0x0000, bg_tiles, 96); /* BG tiles -> BG bank $0000 */
136
+ sms_load_tiles(0x2000, tiles, 64); /* sprite tiles -> sprite bank $2000 */
137
+ draw_track();
101
138
  sms_sprite_init();
102
139
  sfx_init();
103
140
  sms_vdp_display_on();
@@ -15,6 +15,7 @@ extern void sms_vdp_display_on(void);
15
15
  extern void sms_vdp_set_addr(uint16_t addr, uint8_t prefix);
16
16
  extern void sms_load_palette(const uint8_t *palette);
17
17
  extern void sms_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
18
+ extern void sms_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
18
19
  extern void sms_vblank_wait(void);
19
20
  extern uint8_t sms_joypad_read(void);
20
21
  extern void sms_sprite_init(void);
@@ -29,13 +30,49 @@ extern void sms_sat_upload(void);
29
30
  #define T_ENEMY 2
30
31
 
31
32
  static const uint8_t palette[32] = {
32
- 0x10,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
33
+ /* BG: 0 = backdrop, 1 = deep space blue, 2 = lighter space blue, 3 = star white */
34
+ 0x10,0x08,0x20,0x3F, 0x00,0x00,0x00,0x00,
33
35
  0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
34
36
  /* Sprite palette: white, yellow, red */
35
37
  0x00,0x3F,0x0F,0x03, 0x00,0x00,0x00,0x00,
36
38
  0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
37
39
  };
38
40
 
41
+ /* Three BG tiles for the starfield, loaded into the BG tile bank at $0000:
42
+ * tile 0 = deep space (solid colour 1)
43
+ * tile 1 = lighter space band (solid colour 2)
44
+ * tile 2 = space with a star (mostly colour 1, one colour-3 pixel) */
45
+ static const uint8_t bg_tiles[96] = {
46
+ /* tile 0 = deep space (colour 1 -> plane 0 set) */
47
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
48
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
49
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
50
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
51
+ /* tile 1 = lighter band (colour 2 -> plane 1 set) */
52
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
53
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
54
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
55
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
56
+ /* tile 2 = deep space + a star: row 3 col 3 = colour 3 (planes 0+1) */
57
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
58
+ 0xFF,0x00,0x00,0x00, 0xFF,0x10,0x00,0x00,
59
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
60
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
61
+ };
62
+
63
+ /* Paint the whole 32x24 SMS screen with a banded starfield so the screen
64
+ * is clearly space, not a flat backdrop. BG tile bank is $0000. */
65
+ static void draw_starfield(void) {
66
+ uint8_t row, col;
67
+ for (row = 0; row < 24; row++) {
68
+ for (col = 0; col < 32; col++) {
69
+ uint8_t t = (row & 2) ? 1 : 0; /* alternating depth bands */
70
+ if (((row * 7 + col * 5) & 7) == 0) t = 2; /* sparse stars */
71
+ sms_set_tilemap_cell(row, col, t, 0);
72
+ }
73
+ }
74
+ }
75
+
39
76
  static const uint8_t sprite_tiles[32 * 3] = {
40
77
  /* T_SHIP — diamond using colour 1 (white) */
41
78
  0x18,0x00,0x00,0x00, 0x3C,0x00,0x00,0x00,
@@ -96,7 +133,9 @@ void main(void) {
96
133
 
97
134
  sms_vdp_init();
98
135
  sms_load_palette(palette);
99
- sms_load_tiles(0x2000, sprite_tiles, 32 * 3);
136
+ sms_load_tiles(0x0000, bg_tiles, 96); /* BG tiles -> BG bank $0000 */
137
+ sms_load_tiles(0x2000, sprite_tiles, 32 * 3); /* sprite tiles -> $2000 */
138
+ draw_starfield();
100
139
 
101
140
  player.x = 120; player.y = 160; player.alive = 1;
102
141
  {
@@ -20,6 +20,7 @@ extern void sms_vdp_init(void);
20
20
  extern void sms_vdp_display_on(void);
21
21
  extern void sms_load_palette(const uint8_t *palette);
22
22
  extern void sms_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
23
+ extern void sms_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
23
24
  extern void sms_vblank_wait(void);
24
25
  extern uint8_t sms_joypad_read(void);
25
26
  extern uint8_t sms_joypad_read_p2(void);
@@ -35,7 +36,8 @@ extern void sms_sat_upload(void);
35
36
  #define PADDLE_X2 232
36
37
 
37
38
  static const uint8_t palette[32] = {
38
- 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39
+ /* BG: 0 = backdrop, 1 = court green, 2 = court line / net white */
40
+ 0x10, 0x08, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00,
39
41
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40
42
  /* Sprite palette: white at idx 1 */
41
43
  0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -49,6 +51,43 @@ static const uint8_t tile_solid[32] = {
49
51
  0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
50
52
  };
51
53
 
54
+ /* Three BG tiles for the court, loaded into the BG tile bank at $0000:
55
+ * tile 0 = court green (solid colour 1)
56
+ * tile 1 = court line / border (solid colour 2 = white)
57
+ * tile 2 = dashed net (colour 2 vertical stripe on green) */
58
+ static const uint8_t bg_tiles[96] = {
59
+ /* tile 0 = court green (colour 1 -> plane 0 set) */
60
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
61
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
62
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
63
+ 0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
64
+ /* tile 1 = court line / border (colour 2 -> plane 1 set) */
65
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
66
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
67
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
68
+ 0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
69
+ /* tile 2 = net: centre column colour 2, rest colour 1 (green) */
70
+ 0xFF,0x18,0x00,0x00, 0xFF,0x18,0x00,0x00,
71
+ 0xFF,0x18,0x00,0x00, 0xFF,0x18,0x00,0x00,
72
+ 0xFF,0x18,0x00,0x00, 0xFF,0x18,0x00,0x00,
73
+ 0xFF,0x18,0x00,0x00, 0xFF,0x18,0x00,0x00,
74
+ };
75
+
76
+ /* Paint the whole 32x24 court: green field, white top/bottom border lines,
77
+ * and a dashed net down the centre column. BG tile bank is $0000. */
78
+ static void draw_court(void) {
79
+ uint8_t row, col;
80
+ for (row = 0; row < 24; row++) {
81
+ for (col = 0; col < 32; col++) {
82
+ uint8_t t = 0; /* green field */
83
+ if (row <= 1 || row >= 22) t = 1; /* white top/bottom lines */
84
+ else if (col == 1 || col == 30) t = 1; /* white sidelines */
85
+ else if (col == 16) t = 2; /* solid centre net */
86
+ sms_set_tilemap_cell(row, col, t, 0);
87
+ }
88
+ }
89
+ }
90
+
52
91
  static int16_t p1y, p2y, bx, by;
53
92
  static int8_t bdx, bdy;
54
93
  static uint8_t score_p1, score_p2;
@@ -72,7 +111,9 @@ void main(void) {
72
111
  uint8_t i;
73
112
  sms_vdp_init();
74
113
  sms_load_palette(palette);
75
- sms_load_tiles(0x2000, tile_solid, 32);
114
+ sms_load_tiles(0x0000, bg_tiles, 96); /* BG court tiles -> BG bank $0000 */
115
+ sms_load_tiles(0x2000, tile_solid, 32); /* paddle/ball sprite tile -> $2000 */
116
+ draw_court();
76
117
  sms_sprite_init();
77
118
  sfx_init();
78
119
  sms_vdp_display_on();
@@ -1,61 +1,83 @@
1
1
  /* default.c — SNES PVSnesLib starter (C).
2
2
  *
3
- * Smallest "ROM that does something visible" — sets a blue backdrop
4
- * and parks a white diamond sprite in the centre of the screen.
5
- * Pressing the d-pad moves the sprite.
3
+ * Smallest "ROM that does something visible": a full-screen diamond-tile
4
+ * background (BG0) with a movable white sprite parked in the centre.
5
+ * Pressing the d-pad moves the sprite. The tiled backdrop means the very
6
+ * first build shows recognizable content, not a flat colour — the SNES
7
+ * equivalent of the "is anything even rendering?" smoke test.
6
8
  *
7
9
  * Picked as the default SNES template because:
8
- * - PVSnesLib gives a clean C API (oamSet, padsCurrent, etc.)
10
+ * - PVSnesLib gives a clean C API (bgInitTileSet, oamSet, padsCurrent)
9
11
  * instead of raw 65816 / direct register pokes.
10
12
  * - tcc-65816 builds it in <2s.
11
- * - You read this file once and know how every SNES C scaffold
12
- * is structured (hello_sprite, shmup, platformer all extend it).
13
+ * - You read this file once and know how every SNES C scaffold is
14
+ * structured (hello_sprite, shmup, platformer all extend it).
15
+ *
16
+ * The one footgun this guards against: on the SNES, NOTHING renders until
17
+ * setScreenOn() lifts forced blank (INIDISP $80) AND at least one layer
18
+ * (a BG or the sprite layer) actually has tiles + a non-black palette.
19
+ * Disabling every BG and showing one sprite (the obvious "minimal" move)
20
+ * leaves a near-blank screen — so here we light up BG0 too.
13
21
  *
14
- * For raw 65816 see template:"asm" (kept for cycle-accurate work).
15
22
  * For text-mode console output see template:"c_hello".
16
- * For genre-shaped scaffolds see createGame({genre:"shmup", ...}).
23
+ * For raw 65816 see template:"asm" (kept for cycle-accurate work).
24
+ * For genre-shaped scaffolds see scaffold({op:'game', genre:"shmup", ...}).
17
25
  *
18
- * Sibling `data.asm` provides the sprite tile + palette bytes.
19
- * Build:
20
- * build({ output: "rom",
21
- * platform:"snes", language:"c",
22
- * sourcesPaths:{"main.c":"...","data.asm":"..."},
23
- * })
26
+ * Sibling `data.asm` provides tilsprite (one 8×8 4bpp diamond tile) +
27
+ * palsprite (its 16-colour palette). We reuse that one tile for BOTH the
28
+ * sprite and the BG wallpaper — replace with real gfx4snes .pic/.pal art.
24
29
  */
25
30
  #include <snes.h>
26
31
 
27
32
  extern char tilsprite, palsprite;
28
33
 
34
+ /* A 32×32 BG tilemap (one screen), all pointing at tile 0 = the diamond.
35
+ * SNES map entries are 16-bit (tile# + palette/priority/flip bits); 0x0000
36
+ * = tile 0, palette 0, no flip. 32*32 = 1024 entries = 2048 bytes. We fill
37
+ * it at runtime so the source stays small. */
38
+ static u16 bg_map[32 * 32];
39
+
29
40
  int main(void) {
30
41
  u16 x = 120, y = 100;
42
+ u16 i;
31
43
 
32
- /* BG_MODE1 with all BG layers disabledwe render only the
33
- * sprite layer on top of the backdrop colour. */
44
+ /* BG_MODE1: BG0/BG1 are 16-colour, BG2 is 4-colour standard mix.
45
+ * We use BG0 for the wallpaper and the sprite layer on top. */
34
46
  setMode(BG_MODE1, 0);
35
- bgSetDisable(0);
36
47
  bgSetDisable(1);
37
48
  bgSetDisable(2);
38
49
 
39
- /* Backdrop colour (CGRAM entry 0). RGB5: red=0, green=0, blue=15
40
- * bright SNES blue. setPaletteColor packs to BGR555 for us. */
41
- setPaletteColor(0, RGB5(0, 0, 15));
50
+ /* Load the diamond tile + its palette as BG0's tileset. Tile gfx go to
51
+ * VRAM $2000, palette into BG palette 0. 32 bytes of tile (one 8×8 4bpp
52
+ * tile), 32 bytes of palette (16 colours × 2). */
53
+ bgInitTileSet(0, (u8 *)&tilsprite, (u8 *)&palsprite,
54
+ 0, /* palette entry 0 */
55
+ 32, /* tile data size */
56
+ 32, /* palette size */
57
+ BG_16COLORS, /* colour mode */
58
+ 0x2000); /* VRAM addr for BG0 tiles */
59
+
60
+ /* Fill the map with tile 0 and upload it to VRAM $6000. */
61
+ for (i = 0; i < 32 * 32; i++) bg_map[i] = 0x0000;
62
+ bgInitMapSet(0, (u8 *)bg_map, sizeof(bg_map), SC_32x32, 0x6000);
63
+
64
+ bgSetEnable(0);
42
65
 
43
- /* Upload 32 bytes of sprite tile + 32 bytes of sprite palette to
44
- * VRAM $0000 / OBJ palette slot 0. OBJ_SIZE8_L16 = small sprites
45
- * are 8×8, large 16×16 (we use small). */
66
+ /* Upload the same tile as a sprite (VRAM $0000, OBJ palette slot 0) so
67
+ * the movable sprite reads distinctly on top of the wallpaper. */
46
68
  oamInitGfxSet(&tilsprite, 32, &palsprite, 32,
47
69
  0, /* OBJ palette slot */
48
70
  0x0000, /* VRAM address for sprite tiles */
49
71
  OBJ_SIZE8_L16);
50
72
 
51
- /* Place sprite #0 at the centre. */
73
+ /* Place sprite #0 at the centre, highest priority so it sits over BG0. */
52
74
  oamSet(0, x, y,
53
- 3 /* highest priority */,
54
- 0 /* no flip */, 0 /* palette */, 0 /* tile index */, 0 /* objsize */);
75
+ 3 /* priority */, 0 /* no flip */, 0 /* palette */,
76
+ 0 /* tile index */, 0 /* objsize */);
55
77
  oamSetEx(0, OBJ_SMALL, OBJ_SHOW);
56
78
 
57
- /* Until setScreenOn() fires, the SNES holds forced blank
58
- * (INIDISP $80) and nothing renders. */
79
+ /* Until setScreenOn() fires, the SNES holds forced blank (INIDISP $80)
80
+ * and nothing renders. */
59
81
  setScreenOn();
60
82
 
61
83
  while (1) {
@@ -322,4 +322,26 @@ palsprite:
322
322
  .db $00, $00, $00, $00, $00, $00, $00, $00
323
323
  .db $00, $00, $00, $00, $00, $00, $00, $00
324
324
 
325
+ ; ── Background wallpaper (one 8x8 4bpp tile, 4 solid colour quadrants) ──
326
+ ; Tiled across BG1 it paints the whole screen in four muted colours so the
327
+ ; playfield never reads as a flat/blank backdrop. Quadrant->colour: TL=1,
328
+ ; TR=2, BL=3, BR=4. 4bpp plane order: bytes 0-15 = rows 0-7 plane0/plane1
329
+ ; pairs, bytes 16-31 = rows 0-7 plane2/plane3 pairs.
330
+ tilbg:
331
+ .db $F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F ; rows 0-3: p0=left p1=right
332
+ .db $F0, $F0, $F0, $F0, $F0, $F0, $F0, $F0 ; rows 4-7: p0+p1 = left
333
+ .db $00, $00, $00, $00, $00, $00, $00, $00 ; rows 0-3: p2/p3 = 0
334
+ .db $0F, $00, $0F, $00, $0F, $00, $0F, $00 ; rows 4-7: p2 = right
335
+
336
+ palbg:
337
+ ; 16-colour BG palette; only 1-4 used (the four wallpaper quadrant tones).
338
+ .db $00, $00 ; 0 unused (BG fully opaque)
339
+ .db $C4, $30 ; 1 dark blue
340
+ .db $42, $29 ; 2 dark teal
341
+ .db $88, $30 ; 3 dark purple
342
+ .db $C6, $24 ; 4 dark slate
343
+ .db $00, $00, $00, $00, $00, $00, $00, $00
344
+ .db $00, $00, $00, $00, $00, $00, $00, $00
345
+ .db $00, $00, $00, $00, $00, $00
346
+
325
347
  .ends
@@ -23,11 +23,16 @@
23
23
 
24
24
  extern char tilfont, palfont;
25
25
  extern char tilsprite, palsprite;
26
+ extern char tilbg, palbg; /* wallpaper tile + palette (data.asm) */
26
27
 
27
28
  /* consoleVblank() copies the dirty text tilemap to VRAM during VBlank.
28
29
  * No public prototype in console.h, so declare it; call once per frame. */
29
30
  extern void consoleVblank(void);
30
31
 
32
+ /* BG1 wallpaper map: a full 32x32 screen of the 4-colour tile so the
33
+ * playfield reads as a real backdrop, not flat blank. Filled at runtime. */
34
+ static u16 bg_map[32 * 32];
35
+
31
36
  typedef struct { s16 x, y, w, h; } Rect;
32
37
 
33
38
  #define WORLD_W 512
@@ -80,7 +85,17 @@ int main(void) {
80
85
  * registers — point BG0 at the same font ($3000) + map ($6800). */
81
86
  bgSetGfxPtr(0, 0x3000);
82
87
  bgSetMapPtr(0, 0x6800, SC_32x32);
83
- bgSetDisable(1);
88
+
89
+ /* BG1 = full-screen wallpaper so the playfield never reads as blank.
90
+ * Tiles -> VRAM $2000, map -> VRAM $4000 (clear of sprites $0000 and
91
+ * the console gfx $3000 / map $6800). Map entries use palette block 1
92
+ * (0x0400) so the wallpaper palette doesn't disturb the console font
93
+ * palette in block 0 (HUD text stays legible). */
94
+ bgInitTileSet(1, (u8 *)&tilbg, (u8 *)&palbg, 1,
95
+ 32, 32, BG_16COLORS, 0x2000);
96
+ for (i = 0; i < 32 * 32; i++) bg_map[i] = 0x0400;
97
+ bgInitMapSet(1, (u8 *)bg_map, sizeof(bg_map), SC_32x32, 0x4000);
98
+ bgSetEnable(1);
84
99
  bgSetDisable(2);
85
100
 
86
101
  oamInitGfxSet(&tilsprite, 32, &palsprite, 32, 0, 0x0000, OBJ_SIZE8_L16);
@@ -309,4 +309,26 @@ palfont:
309
309
  .db $00, $00, $00, $00, $00, $00, $00, $00
310
310
  .db $00, $00
311
311
 
312
+ ; ── Background wallpaper (one 8x8 4bpp tile, 4 solid colour quadrants) ──
313
+ ; Tiled across BG1 it paints the whole screen in four muted colours so the
314
+ ; playfield never reads as a flat/blank backdrop. Quadrant->colour: TL=1,
315
+ ; TR=2, BL=3, BR=4. 4bpp plane order: bytes 0-15 = rows 0-7 plane0/plane1
316
+ ; pairs, bytes 16-31 = rows 0-7 plane2/plane3 pairs.
317
+ tilbg:
318
+ .db $F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F ; rows 0-3: p0=left p1=right
319
+ .db $F0, $F0, $F0, $F0, $F0, $F0, $F0, $F0 ; rows 4-7: p0+p1 = left
320
+ .db $00, $00, $00, $00, $00, $00, $00, $00 ; rows 0-3: p2/p3 = 0
321
+ .db $0F, $00, $0F, $00, $0F, $00, $0F, $00 ; rows 4-7: p2 = right
322
+
323
+ palbg:
324
+ ; 16-colour BG palette; only 1-4 used (the four wallpaper quadrant tones).
325
+ .db $00, $00 ; 0 unused (BG fully opaque)
326
+ .db $C4, $30 ; 1 dark blue
327
+ .db $42, $29 ; 2 dark teal
328
+ .db $88, $30 ; 3 dark purple
329
+ .db $C6, $24 ; 4 dark slate
330
+ .db $00, $00, $00, $00, $00, $00, $00, $00
331
+ .db $00, $00, $00, $00, $00, $00, $00, $00
332
+ .db $00, $00, $00, $00, $00, $00
333
+
312
334
  .ends
@@ -15,11 +15,16 @@
15
15
  #include "snes_sfx.c"
16
16
 
17
17
  extern char tilfont, palfont;
18
+ extern char tilbg, palbg; /* wallpaper tile + palette (data.asm) */
18
19
 
19
20
  /* consoleVblank() copies the dirty text tilemap to VRAM during VBlank.
20
21
  * No public prototype in console.h, so declare it; call once per frame. */
21
22
  extern void consoleVblank(void);
22
23
 
24
+ /* BG1 wallpaper map: a full 32x32 screen of the 4-colour tile so the
25
+ * playfield reads as a real backdrop, not flat blank. Filled at runtime. */
26
+ static u16 bg_map[32 * 32];
27
+
23
28
  #define COLS 6
24
29
  #define ROWS 12
25
30
 
@@ -136,6 +141,7 @@ static void render_score(void) {
136
141
  int main(void) {
137
142
  s16 r, c;
138
143
  u16 pad, prev = 0, fall_rate;
144
+ u16 i;
139
145
  u8 t;
140
146
 
141
147
  consoleSetTextMapPtr(0x6800);
@@ -147,7 +153,17 @@ int main(void) {
147
153
  * registers — point BG0 at the same font ($3000) + map ($6800). */
148
154
  bgSetGfxPtr(0, 0x3000);
149
155
  bgSetMapPtr(0, 0x6800, SC_32x32);
150
- bgSetDisable(1);
156
+
157
+ /* BG1 = full-screen wallpaper so the playfield never reads as blank.
158
+ * Tiles -> VRAM $2000, map -> VRAM $4000 (clear of sprites $0000 and
159
+ * the console gfx $3000 / map $6800). Map entries use palette block 1
160
+ * (0x0400) so the wallpaper palette doesn't disturb the console font
161
+ * palette in block 0 (HUD/grid text stays legible). */
162
+ bgInitTileSet(1, (u8 *)&tilbg, (u8 *)&palbg, 1,
163
+ 32, 32, BG_16COLORS, 0x2000);
164
+ for (i = 0; i < 32 * 32; i++) bg_map[i] = 0x0400;
165
+ bgInitMapSet(1, (u8 *)bg_map, sizeof(bg_map), SC_32x32, 0x4000);
166
+ bgSetEnable(1);
151
167
  bgSetDisable(2);
152
168
 
153
169
  for (r = 0; r < ROWS; r++)
@@ -328,4 +328,26 @@ palsprite:
328
328
  .db $00, $00, $00, $00, $00, $00, $00, $00
329
329
  .db $00, $00, $00, $00, $00, $00, $00, $00
330
330
 
331
+ ; ── Background wallpaper (one 8x8 4bpp tile, 4 solid colour quadrants) ──
332
+ ; Tiled across BG1 it paints the whole screen in four muted colours so the
333
+ ; playfield never reads as a flat/blank backdrop. Quadrant->colour: TL=1,
334
+ ; TR=2, BL=3, BR=4. 4bpp plane order: bytes 0-15 = rows 0-7 plane0/plane1
335
+ ; pairs, bytes 16-31 = rows 0-7 plane2/plane3 pairs.
336
+ tilbg:
337
+ .db $F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F ; rows 0-3: p0=left p1=right
338
+ .db $F0, $F0, $F0, $F0, $F0, $F0, $F0, $F0 ; rows 4-7: p0+p1 = left
339
+ .db $00, $00, $00, $00, $00, $00, $00, $00 ; rows 0-3: p2/p3 = 0
340
+ .db $0F, $00, $0F, $00, $0F, $00, $0F, $00 ; rows 4-7: p2 = right
341
+
342
+ palbg:
343
+ ; 16-colour BG palette; only 1-4 used (the four wallpaper quadrant tones).
344
+ .db $00, $00 ; 0 unused (BG fully opaque)
345
+ .db $C4, $30 ; 1 dark blue
346
+ .db $42, $29 ; 2 dark teal
347
+ .db $88, $30 ; 3 dark purple
348
+ .db $C6, $24 ; 4 dark slate
349
+ .db $00, $00, $00, $00, $00, $00, $00, $00
350
+ .db $00, $00, $00, $00, $00, $00, $00, $00
351
+ .db $00, $00, $00, $00, $00, $00
352
+
331
353
  .ends
@@ -12,11 +12,16 @@
12
12
 
13
13
  extern char tilfont, palfont;
14
14
  extern char tilsprite, palsprite;
15
+ extern char tilbg, palbg; /* wallpaper tile + palette (data.asm) */
15
16
 
16
17
  /* consoleVblank() copies the dirty text tilemap to VRAM during VBlank.
17
18
  * No public prototype in console.h, so declare it; call once per frame. */
18
19
  extern void consoleVblank(void);
19
20
 
21
+ /* BG1 wallpaper map: a full 32x32 screen of the 4-colour tile so the
22
+ * playfield reads as a real backdrop, not flat blank. Filled at runtime. */
23
+ static u16 bg_map[32 * 32];
24
+
20
25
  #define LANE_LEFT_X 72
21
26
  #define LANE_MID_X 124
22
27
  #define LANE_RIGHT_X 176
@@ -103,6 +108,7 @@ static void draw_road(void) {
103
108
  int main(void) {
104
109
  u16 pad;
105
110
  u8 i;
111
+ u16 bi;
106
112
  s16 step;
107
113
 
108
114
  consoleSetTextMapPtr(0x6800);
@@ -114,7 +120,17 @@ int main(void) {
114
120
  * registers — point BG0 at the same font ($3000) + map ($6800). */
115
121
  bgSetGfxPtr(0, 0x3000);
116
122
  bgSetMapPtr(0, 0x6800, SC_32x32);
117
- bgSetDisable(1);
123
+
124
+ /* BG1 = full-screen wallpaper so the playfield never reads as blank.
125
+ * Tiles -> VRAM $2000, map -> VRAM $4000 (clear of sprites $0000 and
126
+ * the console gfx $3000 / map $6800). Map entries use palette block 1
127
+ * (0x0400) so the wallpaper palette doesn't disturb the console font
128
+ * palette in block 0 (HUD/road text stays legible). */
129
+ bgInitTileSet(1, (u8 *)&tilbg, (u8 *)&palbg, 1,
130
+ 32, 32, BG_16COLORS, 0x2000);
131
+ for (bi = 0; bi < 32 * 32; bi++) bg_map[bi] = 0x0400;
132
+ bgInitMapSet(1, (u8 *)bg_map, sizeof(bg_map), SC_32x32, 0x4000);
133
+ bgSetEnable(1);
118
134
  bgSetDisable(2);
119
135
 
120
136
  /* 2 sprite tiles × 32 bytes = 64 bytes */
@@ -351,4 +351,26 @@ palsprite:
351
351
  .db $00, $00, $00, $00, $00, $00, $00, $00
352
352
  .db $00, $00, $00, $00, $00, $00, $00, $00
353
353
 
354
+ ; ── Background wallpaper (one 8×8 4bpp tile, 4 solid colour quadrants) ──
355
+ ; Tiled across BG1 it paints the whole screen in four muted colours so the
356
+ ; playfield never reads as a flat/blank backdrop. Quadrant→colour: TL=1,
357
+ ; TR=2, BL=3, BR=4. 4bpp plane order: bytes 0-15 = rows 0-7 plane0/plane1
358
+ ; pairs, bytes 16-31 = rows 0-7 plane2/plane3 pairs.
359
+ tilbg:
360
+ .db $F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F ; rows 0-3: p0=left p1=right
361
+ .db $F0, $F0, $F0, $F0, $F0, $F0, $F0, $F0 ; rows 4-7: p0+p1 = left
362
+ .db $00, $00, $00, $00, $00, $00, $00, $00 ; rows 0-3: p2/p3 = 0
363
+ .db $0F, $00, $0F, $00, $0F, $00, $0F, $00 ; rows 4-7: p2 = right
364
+
365
+ palbg:
366
+ ; 16-colour BG palette; only 1-4 used (the four wallpaper quadrant tones).
367
+ .db $00, $00 ; 0 unused (BG fully opaque)
368
+ .db $C4, $30 ; 1 dark blue
369
+ .db $42, $29 ; 2 dark teal
370
+ .db $88, $30 ; 3 dark purple
371
+ .db $C6, $24 ; 4 dark slate
372
+ .db $00, $00, $00, $00, $00, $00, $00, $00
373
+ .db $00, $00, $00, $00, $00, $00, $00, $00
374
+ .db $00, $00, $00, $00, $00, $00
375
+
354
376
  .ends