romdevtools 0.16.0 → 0.22.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 (209) hide show
  1. package/AGENTS.md +75 -16
  2. package/CHANGELOG.md +316 -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/hello_sprite.c +48 -4
  10. package/examples/atari7800/templates/music_demo.c +47 -2
  11. package/examples/atari7800/templates/platformer.c +43 -4
  12. package/examples/atari7800/templates/puzzle.c +39 -4
  13. package/examples/atari7800/templates/racing.c +39 -4
  14. package/examples/atari7800/templates/shmup.c +40 -2
  15. package/examples/atari7800/templates/sports.c +36 -5
  16. package/examples/c64/templates/platformer.c +19 -5
  17. package/examples/c64/templates/puzzle.c +32 -2
  18. package/examples/c64/templates/shmup.c +28 -2
  19. package/examples/c64/templates/sports.c +30 -2
  20. package/examples/c64/templates/tile_engine.c +77 -27
  21. package/examples/gb/templates/default.c +110 -16
  22. package/examples/gb/templates/hello_sprite.c +15 -6
  23. package/examples/gb/templates/music_demo.c +36 -0
  24. package/examples/gb/templates/platformer.c +28 -6
  25. package/examples/gb/templates/puzzle.c +35 -4
  26. package/examples/gb/templates/racing.c +75 -10
  27. package/examples/gb/templates/shmup.c +41 -3
  28. package/examples/gb/templates/sports.c +51 -3
  29. package/examples/gb/templates/tile_engine.c +3 -2
  30. package/examples/gba/templates/gba_hello.c +29 -11
  31. package/examples/gba/templates/maxmod_demo.c +36 -2
  32. package/examples/gba/templates/platformer.c +3 -1
  33. package/examples/gba/templates/puzzle.c +15 -3
  34. package/examples/gba/templates/racing.c +65 -3
  35. package/examples/gba/templates/shmup.c +41 -4
  36. package/examples/gba/templates/sports.c +36 -2
  37. package/examples/gba/templates/tonc_hello.c +41 -5
  38. package/examples/gba/templates/tonc_hello_sprite.c +35 -1
  39. package/examples/gbc/templates/default.c +103 -26
  40. package/examples/gbc/templates/hello_sprite.c +12 -3
  41. package/examples/gbc/templates/music_demo.c +56 -12
  42. package/examples/gbc/templates/platformer.c +28 -6
  43. package/examples/gbc/templates/puzzle.c +35 -4
  44. package/examples/gbc/templates/racing.c +88 -21
  45. package/examples/gbc/templates/shmup.c +37 -3
  46. package/examples/gbc/templates/sports.c +48 -3
  47. package/examples/gbc/templates/tile_engine.c +3 -2
  48. package/examples/genesis/main.s +53 -1
  49. package/examples/genesis/templates/hello_sprite.c +25 -3
  50. package/examples/genesis/templates/puzzle.c +37 -3
  51. package/examples/genesis/templates/racing.c +44 -11
  52. package/examples/genesis/templates/sgdk_hello.c +34 -1
  53. package/examples/genesis/templates/shmup.c +31 -1
  54. package/examples/genesis/templates/shmup_2p.c +31 -0
  55. package/examples/genesis/templates/xgm2_demo.c +20 -0
  56. package/examples/gg/templates/default.c +56 -18
  57. package/examples/gg/templates/hello_sprite.c +25 -2
  58. package/examples/gg/templates/music_demo.c +24 -2
  59. package/examples/gg/templates/platformer.c +18 -12
  60. package/examples/gg/templates/puzzle.c +38 -7
  61. package/examples/gg/templates/racing.c +58 -9
  62. package/examples/gg/templates/shmup.c +47 -3
  63. package/examples/gg/templates/sports.c +57 -16
  64. package/examples/gg/templates/tile_engine.c +12 -6
  65. package/examples/lynx/templates/default.c +39 -8
  66. package/examples/lynx/templates/hello_sprite.c +15 -1
  67. package/examples/lynx/templates/music_demo.c +13 -1
  68. package/examples/lynx/templates/puzzle.c +28 -1
  69. package/examples/lynx/templates/racing.c +34 -7
  70. package/examples/lynx/templates/shmup.c +42 -3
  71. package/examples/lynx/templates/sports.c +29 -2
  72. package/examples/msx/platformer/main.c +213 -0
  73. package/examples/msx/puzzle/main.c +250 -0
  74. package/examples/msx/racing/main.c +249 -0
  75. package/examples/msx/shmup/main.c +288 -0
  76. package/examples/msx/sports/main.c +182 -0
  77. package/examples/nes/templates/default.c +67 -19
  78. package/examples/nes/templates/hello_sprite.c +35 -0
  79. package/examples/nes/templates/music_demo.c +40 -0
  80. package/examples/nes/templates/platformer.c +65 -6
  81. package/examples/nes/templates/puzzle.c +67 -6
  82. package/examples/nes/templates/racing.c +45 -13
  83. package/examples/nes/templates/shmup.c +51 -2
  84. package/examples/nes/templates/sports.c +51 -6
  85. package/examples/pce/catch_game/main.c +22 -3
  86. package/examples/pce/music_sfx/main.c +28 -1
  87. package/examples/pce/platformer/main.c +283 -0
  88. package/examples/pce/puzzle/main.c +304 -0
  89. package/examples/pce/racing/main.c +304 -0
  90. package/examples/pce/shmup/main.c +346 -0
  91. package/examples/pce/sports/main.c +254 -0
  92. package/examples/pce/sprite_move/main.c +7 -2
  93. package/examples/sms/main.c +35 -6
  94. package/examples/sms/templates/hello_sprite.c +29 -3
  95. package/examples/sms/templates/music_demo.c +18 -4
  96. package/examples/sms/templates/puzzle.c +34 -5
  97. package/examples/sms/templates/racing.c +39 -2
  98. package/examples/sms/templates/shmup.c +41 -2
  99. package/examples/sms/templates/shmup_2p.c +24 -1
  100. package/examples/sms/templates/sports.c +47 -4
  101. package/examples/snes/main.asm +108 -17
  102. package/examples/snes/templates/c-hello-data.asm +23 -0
  103. package/examples/snes/templates/c-hello.c +18 -1
  104. package/examples/snes/templates/default.c +50 -28
  105. package/examples/snes/templates/hello_sprite-data.asm +23 -0
  106. package/examples/snes/templates/hello_sprite.c +17 -1
  107. package/examples/snes/templates/music_demo-data.asm +23 -0
  108. package/examples/snes/templates/music_demo.c +22 -4
  109. package/examples/snes/templates/platformer-data.asm +22 -0
  110. package/examples/snes/templates/platformer.c +20 -2
  111. package/examples/snes/templates/puzzle-data.asm +22 -0
  112. package/examples/snes/templates/puzzle.c +21 -2
  113. package/examples/snes/templates/racing-data.asm +22 -0
  114. package/examples/snes/templates/racing.c +17 -1
  115. package/examples/snes/templates/shmup-data.asm +22 -0
  116. package/examples/snes/templates/shmup.c +20 -1
  117. package/examples/snes/templates/sports-data.asm +22 -0
  118. package/examples/snes/templates/sports.c +16 -1
  119. package/package.json +1 -1
  120. package/src/cheats/gamegenie.js +0 -1
  121. package/src/cli/smoke.js +1 -3
  122. package/src/cores/wasm/vice_x64_libretro.js +1 -1
  123. package/src/cores/wasm/vice_x64_libretro.wasm +0 -0
  124. package/src/host/LibretroHost.js +191 -16
  125. package/src/host/callbacks.js +9 -1
  126. package/src/host/chafa-render.js +2 -0
  127. package/src/host/dsp-state.js +2 -2
  128. package/src/host/gpgx-state.js +4 -0
  129. package/src/host/types.js +15 -8
  130. package/src/http/routes.js +1 -1
  131. package/src/http/tool-registry.js +26 -1
  132. package/src/mcp/server.js +1 -1
  133. package/src/mcp/state.js +36 -0
  134. package/src/mcp/tools/address-to-symbol.js +0 -1
  135. package/src/mcp/tools/art-loaders.js +1 -1
  136. package/src/mcp/tools/cart-parts.js +75 -4
  137. package/src/mcp/tools/classify-region.js +1 -1
  138. package/src/mcp/tools/diff-roms.js +1 -1
  139. package/src/mcp/tools/disasm-rebuild.js +507 -0
  140. package/src/mcp/tools/disasm.js +97 -9
  141. package/src/mcp/tools/find-references.js +1 -2
  142. package/src/mcp/tools/font-map.js +1 -1
  143. package/src/mcp/tools/frame.js +168 -3
  144. package/src/mcp/tools/index.js +0 -49
  145. package/src/mcp/tools/input-layout.js +0 -1
  146. package/src/mcp/tools/input.js +33 -3
  147. package/src/mcp/tools/lifecycle.js +18 -4
  148. package/src/mcp/tools/lospec.js +0 -19
  149. package/src/mcp/tools/platform-docs.js +1 -1
  150. package/src/mcp/tools/platform-tools.js +4 -4
  151. package/src/mcp/tools/project.js +54 -11
  152. package/src/mcp/tools/reinject.js +0 -1
  153. package/src/mcp/tools/rom-id.js +2 -2
  154. package/src/mcp/tools/snippets.js +2 -2
  155. package/src/mcp/tools/sprite-pipeline.js +1 -2
  156. package/src/mcp/tools/state.js +201 -14
  157. package/src/mcp/tools/tile-inspect.js +1 -1
  158. package/src/mcp/tools/toolchain.js +105 -12
  159. package/src/mcp/tools/watch-memory.js +137 -16
  160. package/src/platforms/_guides/ROMHACKING_PLAYBOOK.md +34 -0
  161. package/src/platforms/atari2600/TROUBLESHOOTING.md +6 -0
  162. package/src/platforms/atari7800/TROUBLESHOOTING.md +6 -0
  163. package/src/platforms/c64/MENTAL_MODEL.md +45 -1
  164. package/src/platforms/c64/TROUBLESHOOTING.md +6 -0
  165. package/src/platforms/c64/d64.js +280 -0
  166. package/src/platforms/c64/sid.js +0 -2
  167. package/src/platforms/common/metasprite-adapters.js +1 -1
  168. package/src/platforms/common/metasprite-codegen.js +3 -3
  169. package/src/platforms/common/registers.js +5 -3
  170. package/src/platforms/gb/MENTAL_MODEL.md +10 -0
  171. package/src/platforms/gb/TROUBLESHOOTING.md +6 -0
  172. package/src/platforms/gb/lib/c/gb_runtime.c +4 -4
  173. package/src/platforms/gba/TROUBLESHOOTING.md +6 -0
  174. package/src/platforms/gbc/TROUBLESHOOTING.md +6 -0
  175. package/src/platforms/gbc/lib/c/gb_runtime.c +4 -4
  176. package/src/platforms/genesis/TROUBLESHOOTING.md +6 -0
  177. package/src/platforms/gg/TROUBLESHOOTING.md +6 -0
  178. package/src/platforms/lynx/TROUBLESHOOTING.md +6 -0
  179. package/src/platforms/msx/MENTAL_MODEL.md +10 -6
  180. package/src/platforms/msx/TROUBLESHOOTING.md +6 -0
  181. package/src/platforms/nes/MENTAL_MODEL.md +63 -2
  182. package/src/platforms/nes/TROUBLESHOOTING.md +6 -0
  183. package/src/platforms/nes/image-to-tilemap.js +3 -0
  184. package/src/platforms/nes/lib/asm/famitone2.s +5 -1
  185. package/src/platforms/pce/MENTAL_MODEL.md +9 -4
  186. package/src/platforms/pce/TROUBLESHOOTING.md +6 -0
  187. package/src/platforms/pce/lib/c/pce_video.c +1 -1
  188. package/src/platforms/sms/TROUBLESHOOTING.md +6 -0
  189. package/src/platforms/snes/TROUBLESHOOTING.md +6 -0
  190. package/src/platforms/snes/brr.js +0 -2
  191. package/src/playtest/playtest.js +0 -7
  192. package/src/rom-id/identifier.js +15 -0
  193. package/src/toolchains/asar/asar.js +0 -9
  194. package/src/toolchains/assemble-snippet.js +30 -12
  195. package/src/toolchains/cc65/ines.js +145 -0
  196. package/src/toolchains/cc65/presets/nes/chr-ram-runtime.cfg +14 -1
  197. package/src/toolchains/cc65/presets/nes/chr-rom.cfg +83 -0
  198. package/src/toolchains/cc65/presets/nes/chr-rom.crt0.s +153 -0
  199. package/src/toolchains/common/reassemble.js +10 -3
  200. package/src/toolchains/common/sdk-cache.js +1 -1
  201. package/src/toolchains/genesis-c/genesis-c.js +5 -3
  202. package/src/toolchains/index.js +27 -3
  203. package/src/toolchains/parse-errors.js +78 -1
  204. package/src/toolchains/sdcc/preflight-lint.js +5 -1
  205. package/src/toolchains/sdcc/sdcc.js +1 -1
  206. package/src/toolchains/sjasm/sjasm.js +1 -1
  207. package/src/toolchains/snes-c/snes-c.js +2 -2
  208. package/src/toolchains/vasm68k/vasm68k.js +2 -4
  209. package/src/toolchains/wladx/wladx.js +1 -1
@@ -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
  {
@@ -24,6 +24,7 @@ extern void sms_vdp_init(void);
24
24
  extern void sms_vdp_display_on(void);
25
25
  extern void sms_load_palette(const uint8_t *palette);
26
26
  extern void sms_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
27
+ extern void sms_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
27
28
  extern void sms_vblank_wait(void);
28
29
  extern uint8_t sms_joypad_read(void);
29
30
  extern uint8_t sms_joypad_read_p2(void);
@@ -40,13 +41,32 @@ extern void sms_sat_upload(void);
40
41
  #define T_ENEMY 3
41
42
 
42
43
  static const uint8_t palette[32] = {
43
- 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44
+ /* BG palette: 0 backdrop space-blue, 1 mid-blue, 2 dark-blue (starfield dither) */
45
+ 0x10, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
44
46
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45
47
  /* Sprite palette: 1 white (P1), 2 yellow (bullet), 3 red (enemy + P2 highlight) */
46
48
  0x00, 0x3F, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00,
47
49
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
48
50
  };
49
51
 
52
+ /* One dithered BG tile (BG bank $0000): plane0=0xAA/0x55, plane1=0x55/0xAA
53
+ * so pixels alternate colour 1 (mid-blue) and colour 2 (dark-blue) in a fine
54
+ * checkerboard. Filling the name table with it gives a two-tone "space"
55
+ * backdrop so the screen never reads as a single flat colour. */
56
+ static const uint8_t bg_tiles[32] = {
57
+ 0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
58
+ 0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
59
+ 0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
60
+ 0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
61
+ };
62
+
63
+ static void draw_bg(void) {
64
+ uint8_t row, col;
65
+ for (row = 0; row < 28; row++)
66
+ for (col = 0; col < 32; col++)
67
+ sms_set_tilemap_cell(row, col, 0, 0);
68
+ }
69
+
50
70
  /* 4 sprite tiles back-to-back: P1 ship (col1), P2 ship (col3), bullet (col2), enemy (col3). */
51
71
  static const uint8_t sprite_tiles[32 * 4] = {
52
72
  /* P1 ship — diamond, colour 1 (plane 0) */
@@ -112,6 +132,9 @@ void main(void) {
112
132
  uint8_t prev1 = 0, prev2 = 0;
113
133
  sms_vdp_init();
114
134
  sms_load_palette(palette);
135
+ /* BG dither tile → BG bank $0000, paint the whole name table. */
136
+ sms_load_tiles(0x0000, bg_tiles, 32);
137
+ draw_bg();
115
138
  sms_load_tiles(0x2000, sprite_tiles, 32 * 4);
116
139
 
117
140
  p1.x = 80; p1.y = 160; p1.alive = 1;
@@ -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();
@@ -82,6 +123,7 @@ void main(void) {
82
123
  do {
83
124
  uint8_t p1, p2;
84
125
  uint8_t slot;
126
+ int16_t target;
85
127
  sms_vblank_wait();
86
128
  sfx_update();
87
129
 
@@ -103,12 +145,13 @@ void main(void) {
103
145
  if ((p1 & JOY_UP) && p1y > COURT_TOP) p1y -= 2;
104
146
  if ((p1 & JOY_DOWN) && p1y < COURT_BOT - PADDLE_H) p1y += 2;
105
147
 
106
- /* P2 input if any, otherwise AI. */
148
+ /* P2 input if any, otherwise AI. (`target` is declared at the top of the
149
+ * loop body — SDCC is C89, declarations must precede statements.) */
107
150
  if (p2 != 0) {
108
151
  if ((p2 & JOY_UP) && p2y > COURT_TOP) p2y -= 2;
109
152
  if ((p2 & JOY_DOWN) && p2y < COURT_BOT - PADDLE_H) p2y += 2;
110
153
  } else {
111
- int16_t target = by - PADDLE_H / 2;
154
+ target = by - PADDLE_H / 2;
112
155
  if (p2y < target && p2y < COURT_BOT - PADDLE_H) p2y += 1;
113
156
  else if (p2y > target && p2y > COURT_TOP) p2y -= 1;
114
157
  }
@@ -2,32 +2,49 @@
2
2
  ;
3
3
  ; What this does:
4
4
  ; 1. Standard SNES reset (sei, clc xce → native mode, stack at $1FFF).
5
- ; 2. Uploads a single CGRAM color (bright blue) so you see a colored
6
- ; backdrop proof your build is running. NOT just a black screen.
7
- ; 3. Turns on the screen at full brightness, then parks forever.
5
+ ; 2. Uploads a 4-colour palette + two 2bpp tiles to VRAM via DMA.
6
+ ; 3. Fills a 32x32 BG1 tilemap with a checkerboard of those two tiles
7
+ ; so the whole screen shows a real tiled pattern — NOT a flat one-
8
+ ; colour backdrop (which reads as "blank" to a human / the verifier).
9
+ ; 4. Points BG1 at the tile + map bases, enables BG1, turns the screen
10
+ ; on at full brightness, then parks forever.
8
11
  ;
9
- ; SUFFICIENT FOR: confirming your toolchain works + your user has
10
- ; something to see in playtest. To go further (load tiles, set up BGs,
11
- ; sprites, sound, vblank handler) see src/platforms/snes/lib/* snippets:
12
+ ; SUFFICIENT FOR: confirming your toolchain works AND showing your user a
13
+ ; real rendered screen in playtest. To go further (sprites, scrolling,
14
+ ; sound, a vblank handler) see src/platforms/snes/lib/* snippets:
12
15
  ;
13
16
  ; listStarterSnippets({platform:"snes"})
14
17
  ;
15
18
  ; The key snippets are:
16
19
  ; - lorom_header.asm: full SNES header (this scaffold's is minimal)
17
20
  ; - cgram_upload.asm: palette upload boilerplate
18
- ; - vram_dma_upload.asm: fast tile/map uploads
21
+ ; - vram_dma_upload.asm: fast tile/map uploads (used below)
19
22
  ; - oam_upload.asm: sprite table writes
20
23
  ; - nmi_safe.asm: vblank handler skeleton
21
24
  ;
22
25
  ; BUILD: complete LoROM image, no extra options needed.
23
- ; build({ output: "rom", platform: "snes", source: /* this file */ });
26
+ ; build({ output: "run", platform: "snes", source: /* this file */ });
24
27
 
25
28
  lorom
26
29
 
30
+ ; ── PPU / DMA registers ────────────────────────────────────────────────
27
31
  INIDISP = $2100 ; screen brightness / forced blank
32
+ BGMODE = $2105 ; BG mode + tile-size
33
+ BG1SC = $2107 ; BG1 tilemap base + size
34
+ BG12NBA = $210B ; BG1/BG2 character (tile) base
35
+ TM = $212C ; main-screen layer enable
28
36
  NMITIMEN = $4200
29
37
  CGADD = $2121
30
38
  CGDATA = $2122
39
+ VMAIN = $2115 ; VRAM address increment mode
40
+ VMADDL = $2116 ; VRAM word address (16-bit)
41
+ VMDATAL = $2118 ; VRAM data port (low)
42
+ DMAP0 = $4300 ; DMA0 control
43
+ BBAD0 = $4301 ; DMA0 B-bus address
44
+ A1T0L = $4302 ; DMA0 source address (16-bit)
45
+ A1B0 = $4304 ; DMA0 source bank
46
+ DAS0L = $4305 ; DMA0 byte count (16-bit)
47
+ MDMAEN = $420B ; DMA enable
31
48
 
32
49
  ; -----------------------------------------------------------------------
33
50
  org $008000
@@ -40,29 +57,103 @@ START:
40
57
  txs ; stack at $1FFF
41
58
  sep #$20 ; A = 8-bit
42
59
 
43
- ; Blank screen during init.
60
+ ; Blank screen during init; disable NMI/HDMA.
44
61
  lda #$80
45
62
  sta INIDISP
46
-
47
- ; Disable interrupts.
48
63
  stz NMITIMEN
49
64
 
50
- ; Upload one color to CGRAM[0] (the backdrop / transparency color).
51
- ; BGR-555 little-endian. $7C00 = bright blue.
65
+ ; ── Palette CGRAM ───────────────────────────────────────────
66
+ ; 4 colours (2bpp): 0 = blue backdrop, 1 = white, 2 = green,
67
+ ; 3 = magenta. BGR-555, little-endian (low byte then high byte).
52
68
  stz CGADD
53
69
  lda #$00
54
- sta CGDATA
70
+ sta CGDATA ; colour 0 low ($7C00 = blue)
71
+ lda #$7C
72
+ sta CGDATA ; colour 0 high
73
+ lda #$FF
74
+ sta CGDATA ; colour 1 low ($7FFF = white)
75
+ lda #$7F
76
+ sta CGDATA ; colour 1 high
77
+ lda #$E0
78
+ sta CGDATA ; colour 2 low ($03E0 = green)
79
+ lda #$03
80
+ sta CGDATA ; colour 2 high
81
+ lda #$1F
82
+ sta CGDATA ; colour 3 low ($7C1F = magenta)
55
83
  lda #$7C
56
- sta CGDATA
84
+ sta CGDATA ; colour 3 high
85
+
86
+ ; ── Tile CHR → VRAM word $0000 (DMA channel 0) ────────────────
87
+ ; Two 8x8 2bpp tiles = 32 bytes. VMAIN $80 = +1 word after the
88
+ ; high-byte write; B-bus $18 = VMDATAL (auto-alternates to $2119).
89
+ ldx #$0000
90
+ stx VMADDL
91
+ lda #$80
92
+ sta VMAIN
93
+ lda #$01
94
+ sta DMAP0 ; DMA mode 1 (2 regs, word transfers)
95
+ lda #$18
96
+ sta BBAD0 ; → $2118 / $2119
97
+ ldx #TILES
98
+ stx A1T0L
99
+ lda #TILES>>16
100
+ sta A1B0
101
+ ldx #(TILES_END-TILES)
102
+ stx DAS0L ; byte count
103
+ lda #$01
104
+ sta MDMAEN ; fire channel 0
105
+
106
+ ; ── Fill BG1 tilemap → VRAM word $0400 (byte $0800) ───────────
107
+ ; A 32x32 map = 1024 entries. Each entry is a word: low byte =
108
+ ; tile index, high byte = attributes (0). We write a checkerboard
109
+ ; of tile 0 / tile 1 directly through the data port (no source
110
+ ; buffer needed). VMAIN already = +1 word per write.
111
+ ldx #$0400
112
+ stx VMADDL
113
+ rep #$20 ; A = 16-bit for word writes
114
+ ldy #$0000 ; entry counter (0..1023)
115
+ .maploop:
116
+ tya
117
+ and #$0001 ; checker by column parity
118
+ sta VMDATAL ; entry = tile 0 or tile 1
119
+ iny
120
+ cpy #1024
121
+ bne .maploop
122
+ sep #$20 ; back to 8-bit A
57
123
 
58
- ; Enable screen at full brightness (bit 7 = 0 to disable forced
59
- ; blank; low nybble = brightness 0..15).
124
+ ; ── BG1 base registers ────────────────────────────────────────
125
+ stz BGMODE ; mode 0, 8x8 tiles
126
+ ; BG1SC: bits 2-7 = tilemap base in $0400-word units, bits 0-1 =
127
+ ; size (00 = 32x32). Map is at word $0400 → base = 1 → ($01<<2)=$04.
128
+ lda #$04
129
+ sta BG1SC
130
+ stz BG12NBA ; BG1 char base = word $0000 (our tiles)
131
+ lda #$01
132
+ sta TM ; enable BG1 on the main screen
133
+
134
+ ; ── Screen ON at full brightness ──────────────────────────────
60
135
  lda #$0F
61
136
  sta INIDISP
62
137
 
63
138
  LOOP:
64
139
  bra LOOP
65
140
 
141
+ ; -----------------------------------------------------------------------
142
+ ; Tile CHR: two 8x8 2bpp tiles (16 bytes each). 2bpp = 2 bitplanes
143
+ ; interleaved per row: byte0=row0 plane0, byte1=row0 plane1, ...
144
+ ;
145
+ ; Tile 0 — solid colour 1 (plane0 all set, plane1 clear → colour 1).
146
+ ; Tile 1 — checker of colour 2 / colour 3 (both planes patterned) so the
147
+ ; map's alternating tiles give a busy, multi-colour screen.
148
+ TILES:
149
+ ; tile 0 (solid: every pixel colour 1)
150
+ db $FF, $00, $FF, $00, $FF, $00, $FF, $00
151
+ db $FF, $00, $FF, $00, $FF, $00, $FF, $00
152
+ ; tile 1 (checkerboard: colour 2 / colour 3 alternating per pixel)
153
+ db $AA, $55, $55, $AA, $AA, $55, $55, $AA
154
+ db $AA, $55, $55, $AA, $AA, $55, $55, $AA
155
+ TILES_END:
156
+
66
157
  ; -----------------------------------------------------------------------
67
158
  ; Emulation-mode reset vector (used at boot, before `xce` flips to native).
68
159
  ; asar's `lorom` directive handles header + rest of vector table padding.
@@ -320,4 +320,27 @@ palfont:
320
320
  .db $00, $00, $00, $00, $00, $00, $00, $00
321
321
  .db $00, $00
322
322
 
323
+
324
+ ; ── Background wallpaper (one 8x8 4bpp tile, 4 solid colour quadrants) ──
325
+ ; Tiled across BG1 it paints the whole screen in four muted colours so the
326
+ ; backdrop never reads as flat/blank. Quadrant->colour: TL=1, TR=2, BL=3,
327
+ ; BR=4. 4bpp plane order: bytes 0-15 = rows 0-7 plane0/plane1 pairs, bytes
328
+ ; 16-31 = rows 0-7 plane2/plane3 pairs.
329
+ tilbg:
330
+ .db $F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F ; rows 0-3: p0=left p1=right
331
+ .db $F0, $F0, $F0, $F0, $F0, $F0, $F0, $F0 ; rows 4-7: p0+p1 = left
332
+ .db $00, $00, $00, $00, $00, $00, $00, $00 ; rows 0-3: p2/p3 = 0
333
+ .db $0F, $00, $0F, $00, $0F, $00, $0F, $00 ; rows 4-7: p2 = right
334
+
335
+ palbg:
336
+ ; 16-colour BG palette; only 1-4 used (the four wallpaper quadrant tones).
337
+ .db $00, $00 ; 0 unused (BG fully opaque)
338
+ .db $C4, $30 ; 1 dark blue
339
+ .db $42, $29 ; 2 dark teal
340
+ .db $88, $30 ; 3 dark purple
341
+ .db $C6, $24 ; 4 dark slate
342
+ .db $00, $00, $00, $00, $00, $00, $00, $00
343
+ .db $00, $00, $00, $00, $00, $00, $00, $00
344
+ .db $00, $00, $00, $00, $00, $00
345
+
323
346
  .ends
@@ -27,13 +27,20 @@
27
27
  #include <snes.h>
28
28
 
29
29
  extern char tilfont, palfont;
30
+ extern char tilbg, palbg; /* wallpaper tile + palette (data.asm) */
30
31
 
31
32
  /* consoleVblank() copies the dirty text tilemap to VRAM during VBlank.
32
33
  * It has no public prototype in console.h, so declare it here. Call it
33
34
  * once per frame (after WaitForVBlank) or via nmiSet(consoleVblank). */
34
35
  extern void consoleVblank(void);
35
36
 
37
+ /* BG1 wallpaper map: a full 32x32 screen of the 4-colour tile so the
38
+ * screen never reads as a flat/blank backdrop. Filled at runtime. */
39
+ static u16 bg_map[32 * 32];
40
+
36
41
  int main(void) {
42
+ u16 i;
43
+
37
44
  /* ── 1. PVSnesLib text-mode setup ─────────────────────────────
38
45
  * Map + tile-data + palette-offset addresses are conventions —
39
46
  * any free VRAM region will work, these match PVSnesLib's
@@ -54,7 +61,17 @@ int main(void) {
54
61
  setMode(BG_MODE1, 0);
55
62
  bgSetGfxPtr(0, 0x3000);
56
63
  bgSetMapPtr(0, 0x6800, SC_32x32);
57
- bgSetDisable(1);
64
+
65
+ /* BG1 = full-screen wallpaper so the screen never reads as blank.
66
+ * Tiles -> VRAM $2000, map -> VRAM $4000 (clear of the console gfx
67
+ * $3000 / map $6800). Map entries use palette block 1 (0x0400) so the
68
+ * wallpaper palette doesn't disturb the console font palette in block 0
69
+ * (HUD text stays legible). */
70
+ bgInitTileSet(1, (u8 *)&tilbg, (u8 *)&palbg, 1,
71
+ 32, 32, BG_16COLORS, 0x2000);
72
+ for (i = 0; i < 32 * 32; i++) bg_map[i] = 0x0400;
73
+ bgInitMapSet(1, (u8 *)bg_map, sizeof(bg_map), SC_32x32, 0x4000);
74
+ bgSetEnable(1);
58
75
  bgSetDisable(2);
59
76
 
60
77
  /* ── 3. Draw text ─────────────────────────────────────────────
@@ -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) {
@@ -340,4 +340,27 @@ palsprite:
340
340
  .db $00, $00, $00, $00, $00, $00, $00, $00
341
341
  .db $00, $00, $00, $00, $00, $00, $00, $00
342
342
 
343
+
344
+ ; ── Background wallpaper (one 8x8 4bpp tile, 4 solid colour quadrants) ──
345
+ ; Tiled across BG1 it paints the whole screen in four muted colours so the
346
+ ; backdrop never reads as flat/blank. Quadrant->colour: TL=1, TR=2, BL=3,
347
+ ; BR=4. 4bpp plane order: bytes 0-15 = rows 0-7 plane0/plane1 pairs, bytes
348
+ ; 16-31 = rows 0-7 plane2/plane3 pairs.
349
+ tilbg:
350
+ .db $F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F ; rows 0-3: p0=left p1=right
351
+ .db $F0, $F0, $F0, $F0, $F0, $F0, $F0, $F0 ; rows 4-7: p0+p1 = left
352
+ .db $00, $00, $00, $00, $00, $00, $00, $00 ; rows 0-3: p2/p3 = 0
353
+ .db $0F, $00, $0F, $00, $0F, $00, $0F, $00 ; rows 4-7: p2 = right
354
+
355
+ palbg:
356
+ ; 16-colour BG palette; only 1-4 used (the four wallpaper quadrant tones).
357
+ .db $00, $00 ; 0 unused (BG fully opaque)
358
+ .db $C4, $30 ; 1 dark blue
359
+ .db $42, $29 ; 2 dark teal
360
+ .db $88, $30 ; 3 dark purple
361
+ .db $C6, $24 ; 4 dark slate
362
+ .db $00, $00, $00, $00, $00, $00, $00, $00
363
+ .db $00, $00, $00, $00, $00, $00, $00, $00
364
+ .db $00, $00, $00, $00, $00, $00
365
+
343
366
  .ends
@@ -24,14 +24,20 @@
24
24
 
25
25
  extern char tilfont, palfont;
26
26
  extern char tilsprite, palsprite;
27
+ extern char tilbg, palbg; /* wallpaper tile + palette (data.asm) */
27
28
 
28
29
  /* consoleVblank() copies the dirty text tilemap to VRAM during VBlank.
29
30
  * No public prototype in console.h, so declare it; call once per frame. */
30
31
  extern void consoleVblank(void);
31
32
 
33
+ /* BG1 wallpaper map: a full 32x32 screen of the 4-colour tile so the
34
+ * screen never reads as a flat/blank backdrop. Filled at runtime. */
35
+ static u16 bg_map[32 * 32];
36
+
32
37
  int main(void) {
33
38
  u16 x = 120, y = 100;
34
39
  u16 prev = 0;
40
+ u16 i;
35
41
 
36
42
  /* Text setup — same layout as c-hello. */
37
43
  consoleSetTextMapPtr(0x6800);
@@ -44,7 +50,17 @@ int main(void) {
44
50
  * registers — point BG0 at the same font ($3000) + map ($6800). */
45
51
  bgSetGfxPtr(0, 0x3000);
46
52
  bgSetMapPtr(0, 0x6800, SC_32x32);
47
- bgSetDisable(1);
53
+
54
+ /* BG1 = full-screen wallpaper so the screen never reads as blank.
55
+ * Tiles -> VRAM $2000, map -> VRAM $4000 (clear of sprites $0000 and
56
+ * the console gfx $3000 / map $6800). Map entries use palette block 1
57
+ * (0x0400) so the wallpaper palette doesn't disturb the console font
58
+ * palette in block 0 (status text stays legible). */
59
+ bgInitTileSet(1, (u8 *)&tilbg, (u8 *)&palbg, 1,
60
+ 32, 32, BG_16COLORS, 0x2000);
61
+ for (i = 0; i < 32 * 32; i++) bg_map[i] = 0x0400;
62
+ bgInitMapSet(1, (u8 *)bg_map, sizeof(bg_map), SC_32x32, 0x4000);
63
+ bgSetEnable(1);
48
64
  bgSetDisable(2);
49
65
 
50
66
  /* OAM init — sprite tiles at VRAM $0000, 8x8 default. */
@@ -315,4 +315,27 @@ palfont:
315
315
  .db $00, $00, $00, $00, $00, $00, $00, $00
316
316
  .db $00, $00
317
317
 
318
+
319
+ ; ── Background wallpaper (one 8x8 4bpp tile, 4 solid colour quadrants) ──
320
+ ; Tiled across BG1 it paints the whole screen in four muted colours so the
321
+ ; backdrop never reads as flat/blank. Quadrant->colour: TL=1, TR=2, BL=3,
322
+ ; BR=4. 4bpp plane order: bytes 0-15 = rows 0-7 plane0/plane1 pairs, bytes
323
+ ; 16-31 = rows 0-7 plane2/plane3 pairs.
324
+ tilbg:
325
+ .db $F0, $0F, $F0, $0F, $F0, $0F, $F0, $0F ; rows 0-3: p0=left p1=right
326
+ .db $F0, $F0, $F0, $F0, $F0, $F0, $F0, $F0 ; rows 4-7: p0+p1 = left
327
+ .db $00, $00, $00, $00, $00, $00, $00, $00 ; rows 0-3: p2/p3 = 0
328
+ .db $0F, $00, $0F, $00, $0F, $00, $0F, $00 ; rows 4-7: p2 = right
329
+
330
+ palbg:
331
+ ; 16-colour BG palette; only 1-4 used (the four wallpaper quadrant tones).
332
+ .db $00, $00 ; 0 unused (BG fully opaque)
333
+ .db $C4, $30 ; 1 dark blue
334
+ .db $42, $29 ; 2 dark teal
335
+ .db $88, $30 ; 3 dark purple
336
+ .db $C6, $24 ; 4 dark slate
337
+ .db $00, $00, $00, $00, $00, $00, $00, $00
338
+ .db $00, $00, $00, $00, $00, $00, $00, $00
339
+ .db $00, $00, $00, $00, $00, $00
340
+
318
341
  .ends