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.
- package/AGENTS.md +75 -16
- package/CHANGELOG.md +316 -0
- package/examples/README.md +2 -0
- package/examples/atari2600/templates/platformer.asm +460 -0
- package/examples/atari2600/templates/racing.asm +463 -0
- package/examples/atari2600/templates/shmup.asm +386 -0
- package/examples/atari2600/templates/sports.asm +362 -0
- package/examples/atari7800/templates/default.c +49 -5
- package/examples/atari7800/templates/hello_sprite.c +48 -4
- package/examples/atari7800/templates/music_demo.c +47 -2
- package/examples/atari7800/templates/platformer.c +43 -4
- package/examples/atari7800/templates/puzzle.c +39 -4
- package/examples/atari7800/templates/racing.c +39 -4
- package/examples/atari7800/templates/shmup.c +40 -2
- package/examples/atari7800/templates/sports.c +36 -5
- package/examples/c64/templates/platformer.c +19 -5
- package/examples/c64/templates/puzzle.c +32 -2
- package/examples/c64/templates/shmup.c +28 -2
- package/examples/c64/templates/sports.c +30 -2
- package/examples/c64/templates/tile_engine.c +77 -27
- package/examples/gb/templates/default.c +110 -16
- package/examples/gb/templates/hello_sprite.c +15 -6
- package/examples/gb/templates/music_demo.c +36 -0
- package/examples/gb/templates/platformer.c +28 -6
- package/examples/gb/templates/puzzle.c +35 -4
- package/examples/gb/templates/racing.c +75 -10
- package/examples/gb/templates/shmup.c +41 -3
- package/examples/gb/templates/sports.c +51 -3
- package/examples/gb/templates/tile_engine.c +3 -2
- package/examples/gba/templates/gba_hello.c +29 -11
- package/examples/gba/templates/maxmod_demo.c +36 -2
- package/examples/gba/templates/platformer.c +3 -1
- package/examples/gba/templates/puzzle.c +15 -3
- package/examples/gba/templates/racing.c +65 -3
- package/examples/gba/templates/shmup.c +41 -4
- package/examples/gba/templates/sports.c +36 -2
- package/examples/gba/templates/tonc_hello.c +41 -5
- package/examples/gba/templates/tonc_hello_sprite.c +35 -1
- package/examples/gbc/templates/default.c +103 -26
- package/examples/gbc/templates/hello_sprite.c +12 -3
- package/examples/gbc/templates/music_demo.c +56 -12
- package/examples/gbc/templates/platformer.c +28 -6
- package/examples/gbc/templates/puzzle.c +35 -4
- package/examples/gbc/templates/racing.c +88 -21
- package/examples/gbc/templates/shmup.c +37 -3
- package/examples/gbc/templates/sports.c +48 -3
- package/examples/gbc/templates/tile_engine.c +3 -2
- package/examples/genesis/main.s +53 -1
- package/examples/genesis/templates/hello_sprite.c +25 -3
- package/examples/genesis/templates/puzzle.c +37 -3
- package/examples/genesis/templates/racing.c +44 -11
- package/examples/genesis/templates/sgdk_hello.c +34 -1
- package/examples/genesis/templates/shmup.c +31 -1
- package/examples/genesis/templates/shmup_2p.c +31 -0
- package/examples/genesis/templates/xgm2_demo.c +20 -0
- package/examples/gg/templates/default.c +56 -18
- package/examples/gg/templates/hello_sprite.c +25 -2
- package/examples/gg/templates/music_demo.c +24 -2
- package/examples/gg/templates/platformer.c +18 -12
- package/examples/gg/templates/puzzle.c +38 -7
- package/examples/gg/templates/racing.c +58 -9
- package/examples/gg/templates/shmup.c +47 -3
- package/examples/gg/templates/sports.c +57 -16
- package/examples/gg/templates/tile_engine.c +12 -6
- package/examples/lynx/templates/default.c +39 -8
- package/examples/lynx/templates/hello_sprite.c +15 -1
- package/examples/lynx/templates/music_demo.c +13 -1
- package/examples/lynx/templates/puzzle.c +28 -1
- package/examples/lynx/templates/racing.c +34 -7
- package/examples/lynx/templates/shmup.c +42 -3
- package/examples/lynx/templates/sports.c +29 -2
- package/examples/msx/platformer/main.c +213 -0
- package/examples/msx/puzzle/main.c +250 -0
- package/examples/msx/racing/main.c +249 -0
- package/examples/msx/shmup/main.c +288 -0
- package/examples/msx/sports/main.c +182 -0
- package/examples/nes/templates/default.c +67 -19
- package/examples/nes/templates/hello_sprite.c +35 -0
- package/examples/nes/templates/music_demo.c +40 -0
- package/examples/nes/templates/platformer.c +65 -6
- package/examples/nes/templates/puzzle.c +67 -6
- package/examples/nes/templates/racing.c +45 -13
- package/examples/nes/templates/shmup.c +51 -2
- package/examples/nes/templates/sports.c +51 -6
- package/examples/pce/catch_game/main.c +22 -3
- package/examples/pce/music_sfx/main.c +28 -1
- package/examples/pce/platformer/main.c +283 -0
- package/examples/pce/puzzle/main.c +304 -0
- package/examples/pce/racing/main.c +304 -0
- package/examples/pce/shmup/main.c +346 -0
- package/examples/pce/sports/main.c +254 -0
- package/examples/pce/sprite_move/main.c +7 -2
- package/examples/sms/main.c +35 -6
- package/examples/sms/templates/hello_sprite.c +29 -3
- package/examples/sms/templates/music_demo.c +18 -4
- package/examples/sms/templates/puzzle.c +34 -5
- package/examples/sms/templates/racing.c +39 -2
- package/examples/sms/templates/shmup.c +41 -2
- package/examples/sms/templates/shmup_2p.c +24 -1
- package/examples/sms/templates/sports.c +47 -4
- package/examples/snes/main.asm +108 -17
- package/examples/snes/templates/c-hello-data.asm +23 -0
- package/examples/snes/templates/c-hello.c +18 -1
- package/examples/snes/templates/default.c +50 -28
- package/examples/snes/templates/hello_sprite-data.asm +23 -0
- package/examples/snes/templates/hello_sprite.c +17 -1
- package/examples/snes/templates/music_demo-data.asm +23 -0
- package/examples/snes/templates/music_demo.c +22 -4
- package/examples/snes/templates/platformer-data.asm +22 -0
- package/examples/snes/templates/platformer.c +20 -2
- package/examples/snes/templates/puzzle-data.asm +22 -0
- package/examples/snes/templates/puzzle.c +21 -2
- package/examples/snes/templates/racing-data.asm +22 -0
- package/examples/snes/templates/racing.c +17 -1
- package/examples/snes/templates/shmup-data.asm +22 -0
- package/examples/snes/templates/shmup.c +20 -1
- package/examples/snes/templates/sports-data.asm +22 -0
- package/examples/snes/templates/sports.c +16 -1
- package/package.json +1 -1
- package/src/cheats/gamegenie.js +0 -1
- package/src/cli/smoke.js +1 -3
- package/src/cores/wasm/vice_x64_libretro.js +1 -1
- package/src/cores/wasm/vice_x64_libretro.wasm +0 -0
- package/src/host/LibretroHost.js +191 -16
- package/src/host/callbacks.js +9 -1
- package/src/host/chafa-render.js +2 -0
- package/src/host/dsp-state.js +2 -2
- package/src/host/gpgx-state.js +4 -0
- package/src/host/types.js +15 -8
- package/src/http/routes.js +1 -1
- package/src/http/tool-registry.js +26 -1
- package/src/mcp/server.js +1 -1
- package/src/mcp/state.js +36 -0
- package/src/mcp/tools/address-to-symbol.js +0 -1
- package/src/mcp/tools/art-loaders.js +1 -1
- package/src/mcp/tools/cart-parts.js +75 -4
- package/src/mcp/tools/classify-region.js +1 -1
- package/src/mcp/tools/diff-roms.js +1 -1
- package/src/mcp/tools/disasm-rebuild.js +507 -0
- package/src/mcp/tools/disasm.js +97 -9
- package/src/mcp/tools/find-references.js +1 -2
- package/src/mcp/tools/font-map.js +1 -1
- package/src/mcp/tools/frame.js +168 -3
- package/src/mcp/tools/index.js +0 -49
- package/src/mcp/tools/input-layout.js +0 -1
- package/src/mcp/tools/input.js +33 -3
- package/src/mcp/tools/lifecycle.js +18 -4
- package/src/mcp/tools/lospec.js +0 -19
- package/src/mcp/tools/platform-docs.js +1 -1
- package/src/mcp/tools/platform-tools.js +4 -4
- package/src/mcp/tools/project.js +54 -11
- package/src/mcp/tools/reinject.js +0 -1
- package/src/mcp/tools/rom-id.js +2 -2
- package/src/mcp/tools/snippets.js +2 -2
- package/src/mcp/tools/sprite-pipeline.js +1 -2
- package/src/mcp/tools/state.js +201 -14
- package/src/mcp/tools/tile-inspect.js +1 -1
- package/src/mcp/tools/toolchain.js +105 -12
- package/src/mcp/tools/watch-memory.js +137 -16
- package/src/platforms/_guides/ROMHACKING_PLAYBOOK.md +34 -0
- package/src/platforms/atari2600/TROUBLESHOOTING.md +6 -0
- package/src/platforms/atari7800/TROUBLESHOOTING.md +6 -0
- package/src/platforms/c64/MENTAL_MODEL.md +45 -1
- package/src/platforms/c64/TROUBLESHOOTING.md +6 -0
- package/src/platforms/c64/d64.js +280 -0
- package/src/platforms/c64/sid.js +0 -2
- package/src/platforms/common/metasprite-adapters.js +1 -1
- package/src/platforms/common/metasprite-codegen.js +3 -3
- package/src/platforms/common/registers.js +5 -3
- package/src/platforms/gb/MENTAL_MODEL.md +10 -0
- package/src/platforms/gb/TROUBLESHOOTING.md +6 -0
- package/src/platforms/gb/lib/c/gb_runtime.c +4 -4
- package/src/platforms/gba/TROUBLESHOOTING.md +6 -0
- package/src/platforms/gbc/TROUBLESHOOTING.md +6 -0
- package/src/platforms/gbc/lib/c/gb_runtime.c +4 -4
- package/src/platforms/genesis/TROUBLESHOOTING.md +6 -0
- package/src/platforms/gg/TROUBLESHOOTING.md +6 -0
- package/src/platforms/lynx/TROUBLESHOOTING.md +6 -0
- package/src/platforms/msx/MENTAL_MODEL.md +10 -6
- package/src/platforms/msx/TROUBLESHOOTING.md +6 -0
- package/src/platforms/nes/MENTAL_MODEL.md +63 -2
- package/src/platforms/nes/TROUBLESHOOTING.md +6 -0
- package/src/platforms/nes/image-to-tilemap.js +3 -0
- package/src/platforms/nes/lib/asm/famitone2.s +5 -1
- package/src/platforms/pce/MENTAL_MODEL.md +9 -4
- package/src/platforms/pce/TROUBLESHOOTING.md +6 -0
- package/src/platforms/pce/lib/c/pce_video.c +1 -1
- package/src/platforms/sms/TROUBLESHOOTING.md +6 -0
- package/src/platforms/snes/TROUBLESHOOTING.md +6 -0
- package/src/platforms/snes/brr.js +0 -2
- package/src/playtest/playtest.js +0 -7
- package/src/rom-id/identifier.js +15 -0
- package/src/toolchains/asar/asar.js +0 -9
- package/src/toolchains/assemble-snippet.js +30 -12
- package/src/toolchains/cc65/ines.js +145 -0
- package/src/toolchains/cc65/presets/nes/chr-ram-runtime.cfg +14 -1
- package/src/toolchains/cc65/presets/nes/chr-rom.cfg +83 -0
- package/src/toolchains/cc65/presets/nes/chr-rom.crt0.s +153 -0
- package/src/toolchains/common/reassemble.js +10 -3
- package/src/toolchains/common/sdk-cache.js +1 -1
- package/src/toolchains/genesis-c/genesis-c.js +5 -3
- package/src/toolchains/index.js +27 -3
- package/src/toolchains/parse-errors.js +78 -1
- package/src/toolchains/sdcc/preflight-lint.js +5 -1
- package/src/toolchains/sdcc/sdcc.js +1 -1
- package/src/toolchains/sjasm/sjasm.js +1 -1
- package/src/toolchains/snes-c/snes-c.js +2 -2
- package/src/toolchains/vasm68k/vasm68k.js +2 -4
- package/src/toolchains/wladx/wladx.js +1 -1
|
@@ -35,6 +35,22 @@ static const u32 tile_solid_1[8] = {
|
|
|
35
35
|
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
36
36
|
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
37
37
|
};
|
|
38
|
+
/* BG court tiles (4bpp). The court fills BG1 so the arena isn't flat black
|
|
39
|
+
* (paddles+ball alone on black read as blank to a human — frame verify <92%).
|
|
40
|
+
* TILE_COURT (idx1 green): the playing surface, tiled across the screen.
|
|
41
|
+
* TILE_NET (idx2 white): a dashed vertical centre net. */
|
|
42
|
+
#define TILE_COURT 1
|
|
43
|
+
#define TILE_NET 2
|
|
44
|
+
/* Court surface as a two-green checkerboard (idx1 + idx3) so no single colour
|
|
45
|
+
* dominates the screen — a flat one-colour court still trips the blank check. */
|
|
46
|
+
static const u32 tile_court[8] = {
|
|
47
|
+
0x11331133, 0x11331133, 0x11331133, 0x11331133,
|
|
48
|
+
0x33113311, 0x33113311, 0x33113311, 0x33113311,
|
|
49
|
+
};
|
|
50
|
+
static const u32 tile_net[8] = {
|
|
51
|
+
0x00022000, 0x00022000, 0x00000000, 0x00000000,
|
|
52
|
+
0x00022000, 0x00022000, 0x00000000, 0x00000000,
|
|
53
|
+
};
|
|
38
54
|
|
|
39
55
|
static OBJ_ATTR obj_buffer[128];
|
|
40
56
|
|
|
@@ -76,9 +92,27 @@ int main(void) {
|
|
|
76
92
|
|
|
77
93
|
sfx_init();
|
|
78
94
|
|
|
79
|
-
/* TTE for scores + hint. */
|
|
95
|
+
/* TTE for scores + hint (BG0). */
|
|
80
96
|
tte_init_chr4c_default(0, BG_CBB(0) | BG_SBB(31));
|
|
81
|
-
|
|
97
|
+
|
|
98
|
+
/* Court background on BG1 so the arena reads as a real Pong court, not
|
|
99
|
+
* flat black. Tiles in char-block 1, map in screen-block 29. */
|
|
100
|
+
pal_bg_mem[1] = RGB15(2, 12, 4); /* court green (light) */
|
|
101
|
+
pal_bg_mem[2] = CLR_WHITE; /* net */
|
|
102
|
+
pal_bg_mem[3] = RGB15(1, 8, 3); /* court green (dark) */
|
|
103
|
+
tonccpy(&tile_mem[1][TILE_COURT], tile_court, sizeof(tile_court));
|
|
104
|
+
tonccpy(&tile_mem[1][TILE_NET], tile_net, sizeof(tile_net));
|
|
105
|
+
{
|
|
106
|
+
SCR_ENTRY *cmap = se_mem[29];
|
|
107
|
+
int tx, ty;
|
|
108
|
+
for (ty = 0; ty < 32; ty++)
|
|
109
|
+
for (tx = 0; tx < 32; tx++)
|
|
110
|
+
cmap[ty * 32 + tx] = SE_BUILD(
|
|
111
|
+
(tx == 15) ? TILE_NET : TILE_COURT, 0, 0, 0);
|
|
112
|
+
}
|
|
113
|
+
REG_BG1CNT = BG_CBB(1) | BG_SBB(29) | BG_REG_32x32 | BG_4BPP | BG_PRIO(3);
|
|
114
|
+
|
|
115
|
+
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0 | DCNT_BG1 | DCNT_OBJ | DCNT_OBJ_1D;
|
|
82
116
|
tte_write("#{P:16,2}P1");
|
|
83
117
|
tte_write("#{P:208,2}P2");
|
|
84
118
|
tte_write("#{P:36,150}UP/DOWN MOVES YOUR PADDLE");
|
|
@@ -31,17 +31,53 @@
|
|
|
31
31
|
|
|
32
32
|
#include <tonc.h>
|
|
33
33
|
|
|
34
|
+
/* ── Backdrop tiles (4bpp, 8 rows × 32 bits) ─────────────────────────
|
|
35
|
+
* Two solid colour tiles so the whole BG0 map reads as a checkerboard,
|
|
36
|
+
* not a flat blank backdrop. Every nibble of tile 1 = palette index 1,
|
|
37
|
+
* every nibble of tile 2 = palette index 2 — so the tile is one solid
|
|
38
|
+
* colour. (m3_fill's tiled-mode equivalent: paint the whole screen.) */
|
|
39
|
+
static const u32 tile_solid1[8] = {
|
|
40
|
+
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
41
|
+
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
42
|
+
};
|
|
43
|
+
static const u32 tile_solid2[8] = {
|
|
44
|
+
0x22222222, 0x22222222, 0x22222222, 0x22222222,
|
|
45
|
+
0x22222222, 0x22222222, 0x22222222, 0x22222222,
|
|
46
|
+
};
|
|
47
|
+
|
|
34
48
|
int main(void) {
|
|
49
|
+
/* ── Filled tiled backdrop on BG0 ────────────────────────────
|
|
50
|
+
* Without this the screen is just the black backdrop colour and a
|
|
51
|
+
* few text glyphs — which reads as "blank". We lay a two-tone
|
|
52
|
+
* checkerboard across the entire 32x32 BG0 map so a clear majority
|
|
53
|
+
* of the screen is coloured (the GBA tiled-mode analogue of
|
|
54
|
+
* m3_fill-ing a Mode-3 framebuffer). Tile data → char-block 0,
|
|
55
|
+
* map → screen-block 28 (clear of TTE's char-block 2 / SBB 30). */
|
|
56
|
+
pal_bg_mem[0] = CLR_BLACK;
|
|
57
|
+
pal_bg_mem[1] = RGB15(3, 6, 14); /* deep sky blue */
|
|
58
|
+
pal_bg_mem[2] = RGB15(2, 4, 9); /* darker navy */
|
|
59
|
+
tonccpy(&tile_mem[0][1], tile_solid1, sizeof(tile_solid1));
|
|
60
|
+
tonccpy(&tile_mem[0][2], tile_solid2, sizeof(tile_solid2));
|
|
61
|
+
REG_BG0CNT = BG_CBB(0) | BG_SBB(28) | BG_REG_32x32 | BG_4BPP | BG_PRIO(3);
|
|
62
|
+
{
|
|
63
|
+
SCR_ENTRY *map = se_mem[28];
|
|
64
|
+
for (int ty = 0; ty < 32; ty++)
|
|
65
|
+
for (int tx = 0; tx < 32; tx++)
|
|
66
|
+
map[ty * 32 + tx] = SE_BUILD(1 + ((tx ^ ty) & 1), 0, 0, 0);
|
|
67
|
+
}
|
|
68
|
+
|
|
35
69
|
/* Initialise TTE in 4-bits-per-pixel chr-mode with the built-in
|
|
36
70
|
* sys8 font. Cleanest API in the entire GBA ecosystem — one call
|
|
37
|
-
* gets you a usable text terminal on
|
|
71
|
+
* gets you a usable text terminal. We put it on BG1 (char-block 2,
|
|
72
|
+
* screen-block 30) so it sits cleanly in front of the BG0 backdrop.
|
|
38
73
|
*
|
|
39
74
|
* NOTE: we deliberately do NOT call tte_init_con() — that lives
|
|
40
75
|
* in the excluded tte_iohook.c (the libsysbase bridge). Without
|
|
41
76
|
* it, printf/iprintf don't route through TTE — but `tte_write` /
|
|
42
77
|
* `tte_printf` work directly without any libsysbase plumbing,
|
|
43
78
|
* which is what the Tonc tutorial uses everywhere anyway. */
|
|
44
|
-
tte_init_chr4c_default(
|
|
79
|
+
tte_init_chr4c_default(1, BG_CBB(2) | BG_SBB(30));
|
|
80
|
+
REG_BG1CNT |= BG_PRIO(0); /* text in front of the backdrop */
|
|
45
81
|
|
|
46
82
|
/* ── IRQ setup ── REQUIRED for VBlankIntrWait() to work ──────
|
|
47
83
|
* Without this, the BIOS halts the CPU on the first
|
|
@@ -50,9 +86,9 @@ int main(void) {
|
|
|
50
86
|
irq_init(NULL);
|
|
51
87
|
irq_add(II_VBLANK, NULL);
|
|
52
88
|
|
|
53
|
-
/* Set DISPCNT — turn on BG0 (
|
|
54
|
-
*
|
|
55
|
-
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0;
|
|
89
|
+
/* Set DISPCNT — turn on BG0 (the filled backdrop) and BG1 (TTE
|
|
90
|
+
* text). DCNT_MODE0 is the tile-BG mode. */
|
|
91
|
+
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0 | DCNT_BG1;
|
|
56
92
|
|
|
57
93
|
/* Draw text. tte_write moves the internal cursor; \n wraps. */
|
|
58
94
|
tte_write("#{P:32,32}"); /* position cursor at pixel (32,32) */
|
|
@@ -35,6 +35,19 @@ static const u32 sprite_tile[8] = {
|
|
|
35
35
|
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
/* ── Backdrop tiles (4bpp) ───────────────────────────────────────────
|
|
39
|
+
* Two solid-colour BG tiles so we can lay a two-tone checkerboard across
|
|
40
|
+
* the whole BG0 map. Without a filled backdrop the screen is just the
|
|
41
|
+
* black backdrop colour and one tiny sprite — which reads as "blank". */
|
|
42
|
+
static const u32 bg_tile1[8] = {
|
|
43
|
+
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
44
|
+
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
45
|
+
};
|
|
46
|
+
static const u32 bg_tile2[8] = {
|
|
47
|
+
0x22222222, 0x22222222, 0x22222222, 0x22222222,
|
|
48
|
+
0x22222222, 0x22222222, 0x22222222, 0x22222222,
|
|
49
|
+
};
|
|
50
|
+
|
|
38
51
|
/* Shadow OAM. Tonc's recommended pattern: allocate your own 128-slot
|
|
39
52
|
* buffer in WRAM, mutate it freely each frame, then oam_copy to push
|
|
40
53
|
* the changes to OAM hardware via DMA. */
|
|
@@ -81,12 +94,33 @@ int main(void) {
|
|
|
81
94
|
ATTR2_PALBANK(0) | 0);
|
|
82
95
|
obj_set_pos(&obj_buffer[0], x, y);
|
|
83
96
|
|
|
97
|
+
/* ── Filled checkerboard backdrop on BG0 ─────────────────────
|
|
98
|
+
* Lay a two-tone checkerboard across the whole 32x32 BG0 map so the
|
|
99
|
+
* frame has real content behind the sprite instead of a flat black
|
|
100
|
+
* backdrop (which reads as "blank"). BG tiles → char-block 0, map →
|
|
101
|
+
* screen-block 28 (the OBJ tiles live in char-block 4, so there's no
|
|
102
|
+
* clash). */
|
|
103
|
+
pal_bg_mem[0] = CLR_BLACK;
|
|
104
|
+
pal_bg_mem[1] = RGB15(3, 6, 14); /* deep sky blue */
|
|
105
|
+
pal_bg_mem[2] = RGB15(2, 4, 9); /* darker navy */
|
|
106
|
+
tonccpy(&tile_mem[0][1], bg_tile1, sizeof(bg_tile1));
|
|
107
|
+
tonccpy(&tile_mem[0][2], bg_tile2, sizeof(bg_tile2));
|
|
108
|
+
REG_BG0CNT = BG_CBB(0) | BG_SBB(28) | BG_REG_32x32 | BG_4BPP | BG_PRIO(3);
|
|
109
|
+
{
|
|
110
|
+
SCR_ENTRY *map = se_mem[28];
|
|
111
|
+
int tx, ty;
|
|
112
|
+
for (ty = 0; ty < 32; ty++)
|
|
113
|
+
for (tx = 0; tx < 32; tx++)
|
|
114
|
+
map[ty * 32 + tx] = SE_BUILD(1 + ((tx ^ ty) & 1), 0, 0, 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
84
117
|
/* ── Display setup ───────────────────────────────────────────
|
|
85
118
|
* DCNT_MODE0 — tiled mode (so BG0..3 are tile maps)
|
|
119
|
+
* DCNT_BG0 — enable the checkerboard backdrop
|
|
86
120
|
* DCNT_OBJ — enable sprites
|
|
87
121
|
* DCNT_OBJ_1D — linear OAM tile addressing (vs the 2D matrix
|
|
88
122
|
* layout that's almost always wrong) */
|
|
89
|
-
REG_DISPCNT = DCNT_MODE0 | DCNT_OBJ | DCNT_OBJ_1D;
|
|
123
|
+
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0 | DCNT_OBJ | DCNT_OBJ_1D;
|
|
90
124
|
|
|
91
125
|
/* ── Game loop ───────────────────────────────────────────────
|
|
92
126
|
* key_poll() updates the key state — call it once per frame.
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/* ── default.c — minimal Game Boy Color (CGB) starter ─────────────
|
|
2
2
|
*
|
|
3
|
-
*
|
|
4
|
-
* +
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* A "hello, it works! IN COLOR" screen: a tiled background (two bands
|
|
4
|
+
* + a centre box) drawn with a real CGB palette, plus a sprite that
|
|
5
|
+
* bounces around. The very first GBC build shows recognizable content
|
|
6
|
+
* — not a flat colour. Use this as the starting point when you're not
|
|
7
|
+
* yet sure what you want to build; edit from here.
|
|
7
8
|
*
|
|
8
9
|
* GBC-specific notes:
|
|
9
10
|
* - CGB uses BCPS/BCPD ($FF68/$FF69) to write into 64 bytes of
|
|
10
|
-
* palette RAM (8 BG palettes × 4
|
|
11
|
-
* little-endian)
|
|
11
|
+
* palette RAM (8 BG palettes × 4 colours × 2 bytes each, BGR555
|
|
12
|
+
* little-endian) and OCPS/OCPD for the sprite palettes. The DMG-only
|
|
13
|
+
* BGP/OBP0/OBP1 ($FF47-$49) registers do nothing in CGB mode.
|
|
14
|
+
* - You MUST put tiles in VRAM and enable the BG (LCDC bit 0) or the
|
|
15
|
+
* screen stays one flat colour — the #1 GB "why is it blank" footgun.
|
|
16
|
+
* We upload tiles to $8000 and select LCDC_TILE_DATA_LO (unsigned
|
|
17
|
+
* $8000 addressing) so tile index N lives at $8000 + N*16.
|
|
12
18
|
* - patchGbHeader on a .gbc file sets $0143 = $80 (CGB-aware) by
|
|
13
|
-
* default. The corresponding `.gb` default uses BGP — don't
|
|
19
|
+
* default. The corresponding `.gb` default uses DMG BGP — don't
|
|
14
20
|
* cross-pollinate the two trees.
|
|
15
|
-
* - This template is intentionally DIFFERENT from examples/gb/
|
|
16
|
-
* templates/default.c — that one demonstrates DMG palettes.
|
|
17
21
|
*
|
|
18
22
|
* For something more game-shaped, peek at other templates in this dir:
|
|
19
23
|
* - hello_sprite — sprite + d-pad movement
|
|
@@ -25,37 +29,110 @@
|
|
|
25
29
|
#include "gb_hardware.h"
|
|
26
30
|
#include "gb_runtime.h"
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
/* Three 8×8 tiles, 2bpp (16 bytes each: row N = byte 2N low-bits, 2N+1
|
|
33
|
+
* high-bits). Colour index per tile pixel selects into the 4-colour
|
|
34
|
+
* palette below.
|
|
35
|
+
* tile 0 — blank (all colour 0 — reserved so OAM Y=0 doesn't glitch)
|
|
36
|
+
* tile 1 — solid (all colour 1 — the background fill / bands)
|
|
37
|
+
* tile 2 — sprite (a filled diamond in colour 3) */
|
|
38
|
+
static const uint8_t tiles[3 * 16] = {
|
|
39
|
+
/* tile 0: blank */
|
|
40
|
+
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
41
|
+
/* tile 1: solid colour 1 (low plane all-on, high plane all-off) */
|
|
42
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
43
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
44
|
+
/* tile 2: diamond in colour 3 (both planes set on the diamond pixels) */
|
|
45
|
+
0x18,0x18, 0x3C,0x3C, 0x7E,0x7E, 0xFF,0xFF,
|
|
46
|
+
0xFF,0xFF, 0x7E,0x7E, 0x3C,0x3C, 0x18,0x18,
|
|
33
47
|
};
|
|
34
48
|
|
|
49
|
+
/* BG palette 0 (BGR555). Colour 0 = backdrop, colour 1 = the bands/box.
|
|
50
|
+
* We cycle colour 1 through these four shades each ~32 frames so you can
|
|
51
|
+
* SEE the CGB palette path is alive. */
|
|
52
|
+
static const uint16_t bg_fill_cycle[4] = {
|
|
53
|
+
0x03FF, /* yellow-ish */
|
|
54
|
+
0x7C00, /* blue */
|
|
55
|
+
0x03E0, /* green */
|
|
56
|
+
0x001F, /* red */
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/* Sprite palette (OBJ palette 0): 0 transparent, 3 = white diamond. */
|
|
60
|
+
static const uint16_t obj_pal[4] = {
|
|
61
|
+
0x0000, /* 0 transparent (sprite colour 0 never drawn) */
|
|
62
|
+
0x7FFF, /* 1 white */
|
|
63
|
+
0x03E0, /* 2 green */
|
|
64
|
+
0x7FFF, /* 3 white */
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
static void set_bg_color1(uint16_t bgr555) {
|
|
68
|
+
/* Write BG palette 0: colour 0 = dark backdrop, colour 1 = bgr555. */
|
|
69
|
+
BCPS = 0x80; /* palette 0, colour 0, auto-inc */
|
|
70
|
+
BCPD = 0x08; BCPD = 0x21; /* colour 0 = dark grey (0x2108) */
|
|
71
|
+
BCPD = (uint8_t)(bgr555 & 0xFF); /* colour 1 lo */
|
|
72
|
+
BCPD = (uint8_t)((bgr555 >> 8) & 0xFF); /* colour 1 hi */
|
|
73
|
+
}
|
|
74
|
+
|
|
35
75
|
void main(void) {
|
|
36
|
-
uint8_t
|
|
76
|
+
uint8_t x, y;
|
|
77
|
+
uint8_t sx = 76, sy = 64; /* sprite screen position */
|
|
78
|
+
int8_t dx = 1, dy = 1; /* sprite velocity */
|
|
37
79
|
uint8_t shade = 0;
|
|
38
80
|
uint16_t frame = 0;
|
|
81
|
+
uint8_t *bg_map = BG_MAP_0; /* $9800 */
|
|
82
|
+
uint8_t i;
|
|
39
83
|
|
|
84
|
+
/* 1. LCD off (safely — lcd_init_default checks LCDC.7 first). */
|
|
40
85
|
lcd_init_default();
|
|
86
|
+
LCDC = 0;
|
|
41
87
|
|
|
42
|
-
/*
|
|
43
|
-
|
|
88
|
+
/* 2. Tiles → VRAM at $8000 (unsigned addressing, see LCDC_TILE_DATA_LO). */
|
|
89
|
+
memcpy_vram((void *)0x8000, tiles, sizeof(tiles));
|
|
90
|
+
|
|
91
|
+
/* 3. Palettes. */
|
|
92
|
+
set_bg_color1(bg_fill_cycle[0]);
|
|
93
|
+
OCPS = 0x80;
|
|
44
94
|
for (i = 0; i < 4; i++) {
|
|
45
|
-
|
|
46
|
-
|
|
95
|
+
OCPD = (uint8_t)(obj_pal[i] & 0xFF);
|
|
96
|
+
OCPD = (uint8_t)((obj_pal[i] >> 8) & 0xFF);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* 4. Paint the BG map (32×32; we fill the visible 20×18). Background
|
|
100
|
+
* is tile 0 (blank → backdrop colour); two solid bands + a centre
|
|
101
|
+
* box are tile 1 so the screen reads as real content. */
|
|
102
|
+
for (y = 0; y < 32; y++)
|
|
103
|
+
for (x = 0; x < 32; x++)
|
|
104
|
+
bg_map[y * 32 + x] = 0;
|
|
105
|
+
for (x = 0; x < 20; x++) {
|
|
106
|
+
bg_map[2 * 32 + x] = 1; /* top band (row 2) */
|
|
107
|
+
bg_map[15 * 32 + x] = 1; /* bottom band (row 15) */
|
|
47
108
|
}
|
|
109
|
+
for (y = 6; y < 12; y++)
|
|
110
|
+
for (x = 6; x < 14; x++)
|
|
111
|
+
bg_map[y * 32 + x] = 1; /* centre box */
|
|
112
|
+
|
|
113
|
+
/* 5. Initial sprite. */
|
|
114
|
+
oam_clear();
|
|
115
|
+
oam_set(0, (uint8_t)(sy + 16), (uint8_t)(sx + 8), 2, 0);
|
|
116
|
+
|
|
117
|
+
/* 6. LCD on with BG + OBJ + $8000 tile addressing. */
|
|
118
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
48
119
|
|
|
49
120
|
for (;;) {
|
|
50
121
|
wait_vblank();
|
|
122
|
+
oam_dma_flush();
|
|
123
|
+
|
|
51
124
|
frame++;
|
|
52
|
-
if ((frame & 0x1F) == 0) {
|
|
53
|
-
shade = (shade + 1) & 0x03;
|
|
54
|
-
|
|
55
|
-
for (i = 0; i < 4; i++) {
|
|
56
|
-
BCPD = (uint8_t)(palettes[shade] & 0xFF);
|
|
57
|
-
BCPD = (uint8_t)((palettes[shade] >> 8) & 0xFF);
|
|
58
|
-
}
|
|
125
|
+
if ((frame & 0x1F) == 0) { /* every 32 frames: cycle BG colour */
|
|
126
|
+
shade = (uint8_t)((shade + 1) & 0x03);
|
|
127
|
+
set_bg_color1(bg_fill_cycle[shade]);
|
|
59
128
|
}
|
|
129
|
+
|
|
130
|
+
/* Bounce the sprite around the 160×144 visible area. */
|
|
131
|
+
sx = (uint8_t)(sx + dx);
|
|
132
|
+
sy = (uint8_t)(sy + dy);
|
|
133
|
+
if (sx < 1 || sx > 152) dx = (int8_t)-dx;
|
|
134
|
+
if (sy < 1 || sy > 136) dy = (int8_t)-dy;
|
|
135
|
+
oam_clear();
|
|
136
|
+
oam_set(0, (uint8_t)(sy + 16), (uint8_t)(sx + 8), 2, 0);
|
|
60
137
|
}
|
|
61
138
|
}
|
|
@@ -54,6 +54,8 @@ void main(void) {
|
|
|
54
54
|
uint8_t x = 80; /* hardware X = screen X + 8 */
|
|
55
55
|
uint8_t y = 80; /* hardware Y = screen Y + 16 */
|
|
56
56
|
uint8_t i;
|
|
57
|
+
uint8_t *bg_map;
|
|
58
|
+
uint16_t j;
|
|
57
59
|
|
|
58
60
|
/* ── 1. LCD off (safe whether it was on or off) ──────────────────
|
|
59
61
|
* lcd_init_default() checks LCDC.7 and only waits for vblank if the
|
|
@@ -71,6 +73,13 @@ void main(void) {
|
|
|
71
73
|
* TROUBLESHOOTING.md "VRAM stays empty / sprite never appears". */
|
|
72
74
|
memcpy_vram((uint8_t *)0x8010, tile_data, 16);
|
|
73
75
|
|
|
76
|
+
/* ── 2b. Fill the BG tilemap so the screen isn't an empty backdrop. ──
|
|
77
|
+
* With LCDC_TILE_DATA_LO ($8000 addressing) BG tile index 1 == our tile
|
|
78
|
+
* at $8010 — so we tile the whole 32×32 BG map with it. Pointer-walk write
|
|
79
|
+
* (NOT bg_map[k]=1, which SDCC sm83 miscompiles into VRAM). */
|
|
80
|
+
bg_map = (uint8_t *)0x9800;
|
|
81
|
+
for (j = 0; j < 32u * 32u; j++) *bg_map++ = 1;
|
|
82
|
+
|
|
74
83
|
/* ── 3. Object palette 0 (CGB path) ──────────────────────────────
|
|
75
84
|
* OCPS bit 7 = auto-increment after each write; bits 5..3 = palette
|
|
76
85
|
* index (0..7); bits 2..1 = color index (0..3); bit 0 = MSB select.
|
|
@@ -102,9 +111,9 @@ void main(void) {
|
|
|
102
111
|
oam_dma_flush();
|
|
103
112
|
|
|
104
113
|
/* ── 5. Turn the LCD back on with BG + OBJ enabled. ──────────────
|
|
105
|
-
* LCDC bits: 0x80=LCD on, 0x02=OBJ on, 0x10=tile data at $8000.
|
|
106
|
-
*
|
|
107
|
-
LCDC = LCDC_LCD_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
114
|
+
* LCDC bits: 0x80=LCD on, 0x01=BG on, 0x02=OBJ on, 0x10=tile data at $8000.
|
|
115
|
+
* BG is on now that we filled the BG map in step 2b. */
|
|
116
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
108
117
|
|
|
109
118
|
/* ── 6. APU on — let the player beep ──────────────────────────── */
|
|
110
119
|
sound_init();
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
|
|
24
24
|
extern const huge_song_t sample_song;
|
|
25
25
|
|
|
26
|
+
/* CGB BG palette 0: backdrop colour cycles (bg_colors[shade]) while
|
|
27
|
+
* colours 1..3 stay fixed and bright, so the checkerboard backdrop below
|
|
28
|
+
* always shows several distinct colours (not a single flat field). */
|
|
26
29
|
static const uint16_t bg_colors[4] = {
|
|
27
30
|
0x4210, /* dim purple */
|
|
28
31
|
0x4308, /* dim blue */
|
|
@@ -30,21 +33,66 @@ static const uint16_t bg_colors[4] = {
|
|
|
30
33
|
0x0017, /* dim red */
|
|
31
34
|
};
|
|
32
35
|
|
|
36
|
+
/* Two 8×8 2bpp tiles so the BG isn't a single flat colour:
|
|
37
|
+
* tile 1 — solid colour 1
|
|
38
|
+
* tile 2 — solid colour 2
|
|
39
|
+
* Checkerboarded across the BG map below. */
|
|
40
|
+
static const uint8_t tile_solid1[16] = {
|
|
41
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
42
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
43
|
+
};
|
|
44
|
+
static const uint8_t tile_solid2[16] = {
|
|
45
|
+
0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF,
|
|
46
|
+
0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/* Write CGB BG palette 0 with the current backdrop shade plus fixed bright
|
|
50
|
+
* colours 1..3 (blue / green / white) so the checkerboard is multi-colour. */
|
|
51
|
+
static void set_bg_palette(uint8_t shade) {
|
|
52
|
+
BCPS = 0x80; /* auto-increment, start at palette 0 colour 0 */
|
|
53
|
+
/* colour 0 — animated backdrop */
|
|
54
|
+
BCPD = (uint8_t)(bg_colors[shade] & 0xFFu);
|
|
55
|
+
BCPD = (uint8_t)((bg_colors[shade] >> 8) & 0xFFu);
|
|
56
|
+
/* colour 1 — bright blue */
|
|
57
|
+
BCPD = (uint8_t)(0x7C00u & 0xFFu);
|
|
58
|
+
BCPD = (uint8_t)((0x7C00u >> 8) & 0xFFu);
|
|
59
|
+
/* colour 2 — bright green */
|
|
60
|
+
BCPD = (uint8_t)(0x03E0u & 0xFFu);
|
|
61
|
+
BCPD = (uint8_t)((0x03E0u >> 8) & 0xFFu);
|
|
62
|
+
/* colour 3 — white */
|
|
63
|
+
BCPD = (uint8_t)(0x7FFFu & 0xFFu);
|
|
64
|
+
BCPD = (uint8_t)((0x7FFFu >> 8) & 0xFFu);
|
|
65
|
+
}
|
|
66
|
+
|
|
33
67
|
void main(void) {
|
|
34
|
-
uint8_t i;
|
|
35
68
|
uint8_t shade = 0;
|
|
36
69
|
uint16_t frame = 0;
|
|
70
|
+
uint8_t *bg_map;
|
|
71
|
+
uint16_t j;
|
|
37
72
|
|
|
38
73
|
lcd_init_default();
|
|
39
|
-
|
|
74
|
+
LCDC = 0; /* LCD off so we can write VRAM freely */
|
|
40
75
|
|
|
41
|
-
/*
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
76
|
+
/* Upload two tiles to VRAM slots 1 ($8010) and 2 ($8020). Use
|
|
77
|
+
* memcpy_vram (pointer-walk) — an indexed dst[i]=src[i] loop into VRAM
|
|
78
|
+
* is miscompiled by SDCC sm83. */
|
|
79
|
+
memcpy_vram((uint8_t *)0x8010, tile_solid1, 16);
|
|
80
|
+
memcpy_vram((uint8_t *)0x8020, tile_solid2, 16);
|
|
81
|
+
|
|
82
|
+
/* Checkerboard the 32×32 BG map at $9800 with tiles 1 and 2. Pointer-walk
|
|
83
|
+
* (NOT bg_map[k]=..., which SDCC sm83 miscompiles into VRAM). */
|
|
84
|
+
bg_map = (uint8_t *)0x9800;
|
|
85
|
+
for (j = 0; j < 32u * 32u; j++) {
|
|
86
|
+
*bg_map++ = (uint8_t)((((j ^ (j >> 5)) & 1u) ? 1u : 2u));
|
|
46
87
|
}
|
|
47
88
|
|
|
89
|
+
set_bg_palette(shade);
|
|
90
|
+
|
|
91
|
+
/* LCD on with BG enabled, $8000 tile-data addressing. */
|
|
92
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_TILE_DATA_LO;
|
|
93
|
+
|
|
94
|
+
sound_init();
|
|
95
|
+
|
|
48
96
|
hUGE_init(&sample_song);
|
|
49
97
|
|
|
50
98
|
for (;;) {
|
|
@@ -53,11 +101,7 @@ void main(void) {
|
|
|
53
101
|
frame++;
|
|
54
102
|
if ((frame & 0x3F) == 0) { /* every 64 frames ≈ 1 s */
|
|
55
103
|
shade = (uint8_t)((shade + 1) & 0x03);
|
|
56
|
-
|
|
57
|
-
for (i = 0; i < 4; i++) {
|
|
58
|
-
BCPD = (uint8_t)(bg_colors[shade] & 0xFFu);
|
|
59
|
-
BCPD = (uint8_t)((bg_colors[shade] >> 8) & 0xFFu);
|
|
60
|
-
}
|
|
104
|
+
set_bg_palette(shade);
|
|
61
105
|
}
|
|
62
106
|
}
|
|
63
107
|
}
|
|
@@ -28,9 +28,26 @@ static const uint8_t tile_platform[16] = {
|
|
|
28
28
|
0xFF,0xFF, 0x80,0x80, 0x80,0x80, 0x80,0x80,
|
|
29
29
|
0x80,0x80, 0x80,0x80, 0x80,0x80, 0xFF,0xFF,
|
|
30
30
|
};
|
|
31
|
+
/* ── Backdrop tiles ───────────────────────────────────────────────────
|
|
32
|
+
* Fill the whole world so the screen is never one flat colour (the #1 GB
|
|
33
|
+
* "why is it blank" footgun). tile_sky is a sparse dot pattern over the
|
|
34
|
+
* sky; tile_ground is a textured dirt fill under the floor line. */
|
|
35
|
+
static const uint8_t tile_sky[16] = {
|
|
36
|
+
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x20,0x20,
|
|
37
|
+
0x00,0x00, 0x00,0x00, 0x02,0x02, 0x00,0x00,
|
|
38
|
+
};
|
|
39
|
+
static const uint8_t tile_ground[16] = {
|
|
40
|
+
0xFF,0x00, 0xDB,0x24, 0xFF,0x00, 0x6D,0x92,
|
|
41
|
+
0xFF,0x00, 0xDB,0x24, 0xFF,0x00, 0x6D,0x92,
|
|
42
|
+
};
|
|
43
|
+
#define T_BLANK 0
|
|
44
|
+
#define T_PLATFORM 2
|
|
45
|
+
#define T_SKY 3
|
|
46
|
+
#define T_GROUND 4
|
|
31
47
|
|
|
32
48
|
static const uint16_t obj_palette[4] = { 0x7FFF, 0x001F, 0x03E0, 0x7C00 };
|
|
33
|
-
|
|
49
|
+
/* BG palette: 0 sky-blue, 1 mid, 2 dirt-dark, 3 near-black detail. */
|
|
50
|
+
static const uint16_t bg_palette[4] = { 0x7E10, 0x5294, 0x114A, 0x0000 };
|
|
34
51
|
|
|
35
52
|
typedef struct { int16_t x, y, w, h; } Rect;
|
|
36
53
|
|
|
@@ -61,8 +78,9 @@ static uint8_t on_platform(int16_t px, int16_t py) {
|
|
|
61
78
|
|
|
62
79
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
63
80
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
82
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
83
|
+
memcpy_vram(dst, src, 16);
|
|
66
84
|
}
|
|
67
85
|
|
|
68
86
|
static void paint_platforms(void) {
|
|
@@ -74,8 +92,10 @@ static void paint_platforms(void) {
|
|
|
74
92
|
const Rect *p;
|
|
75
93
|
/* k MUST be uint16_t: 32*18 = 576 > 255, so a uint8_t counter would
|
|
76
94
|
* never reach the bound and this loop would spin forever (the BG map
|
|
77
|
-
* never clears, main() never starts). Classic SDCC limited-range trap.
|
|
78
|
-
|
|
95
|
+
* never clears, main() never starts). Classic SDCC limited-range trap.
|
|
96
|
+
* Fill sky above the floor line (row 16 = y 128) and textured ground
|
|
97
|
+
* at and below it, so the whole world is a real scene, not blank. */
|
|
98
|
+
for (k = 0; k < 32 * 18; k++) map[k] = (k >= 16 * 32) ? T_GROUND : T_SKY;
|
|
79
99
|
for (i = 0; i < N_PLATFORMS; i++) {
|
|
80
100
|
p = &platforms[i];
|
|
81
101
|
cx = p->x >> 3;
|
|
@@ -84,7 +104,7 @@ static void paint_platforms(void) {
|
|
|
84
104
|
ch = (p->h + 7) >> 3;
|
|
85
105
|
for (j = 0; j < cw; j++) {
|
|
86
106
|
if (cx + j < 32 && cy < 32)
|
|
87
|
-
map[cy * 32 + cx + j] =
|
|
107
|
+
map[cy * 32 + cx + j] = T_PLATFORM; /* platform top edge */
|
|
88
108
|
}
|
|
89
109
|
}
|
|
90
110
|
}
|
|
@@ -111,6 +131,8 @@ void main(void) {
|
|
|
111
131
|
upload_tile(0, tile_blank);
|
|
112
132
|
upload_tile(1, tile_player);
|
|
113
133
|
upload_tile(2, tile_platform);
|
|
134
|
+
upload_tile(T_SKY, tile_sky);
|
|
135
|
+
upload_tile(T_GROUND, tile_ground);
|
|
114
136
|
|
|
115
137
|
OCPS = 0x80;
|
|
116
138
|
for (i = 0; i < 4; i++) {
|
|
@@ -21,8 +21,22 @@
|
|
|
21
21
|
#define T_R 1
|
|
22
22
|
#define T_G 2
|
|
23
23
|
#define T_B 3
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
#define T_WALL 4
|
|
25
|
+
|
|
26
|
+
/* tile_blank is the EMPTY-cell / backdrop tile. It is NOT all-zero: a
|
|
27
|
+
* subtle dither (colour 0 + faint colour 1) so the empty playfield and the
|
|
28
|
+
* area around the well read as a textured surface, never one flat colour
|
|
29
|
+
* (the #1 GB "why is it blank" footgun). Locked blocks / the active piece
|
|
30
|
+
* overdraw it with the R/G/B shape tiles. */
|
|
31
|
+
static const uint8_t tile_blank[16] = {
|
|
32
|
+
0x00,0x00, 0x22,0x00, 0x00,0x00, 0x88,0x00,
|
|
33
|
+
0x00,0x00, 0x22,0x00, 0x00,0x00, 0x88,0x00,
|
|
34
|
+
};
|
|
35
|
+
/* Well frame: a solid colour-2 border drawn around the play area. */
|
|
36
|
+
static const uint8_t tile_wall[16] = {
|
|
37
|
+
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
38
|
+
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
39
|
+
};
|
|
26
40
|
/* Three distinct tile shapes (since GB BG is 2bpp, we differentiate
|
|
27
41
|
* by *shape*, not colour-on-CGB). The CGB palette path could give us
|
|
28
42
|
* real colours; for DMG-compatibility we use shape. */
|
|
@@ -127,8 +141,23 @@ static void lock_piece(void) {
|
|
|
127
141
|
|
|
128
142
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
129
143
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
130
|
-
|
|
131
|
-
|
|
144
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
145
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
146
|
+
memcpy_vram(dst, src, 16);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Draw the well frame around the 6×12 play area. Grid cells live at
|
|
150
|
+
* map[(row+1)*32 + (col+7)] (rows 1..12, cols 7..12), so the frame is the
|
|
151
|
+
* column to each side (6 and 13) and the floor row just below (row 13). */
|
|
152
|
+
static void draw_well(void) {
|
|
153
|
+
uint8_t *map = (uint8_t *)0x9800;
|
|
154
|
+
uint8_t r;
|
|
155
|
+
for (r = 1; r <= 12; r++) {
|
|
156
|
+
map[r * 32 + 6] = T_WALL; /* left wall */
|
|
157
|
+
map[r * 32 + 13] = T_WALL; /* right wall */
|
|
158
|
+
}
|
|
159
|
+
for (r = 6; r <= 13; r++)
|
|
160
|
+
map[13 * 32 + r] = T_WALL; /* floor */
|
|
132
161
|
}
|
|
133
162
|
|
|
134
163
|
void main(void) {
|
|
@@ -145,6 +174,7 @@ void main(void) {
|
|
|
145
174
|
upload_tile(T_R, tile_r);
|
|
146
175
|
upload_tile(T_G, tile_g);
|
|
147
176
|
upload_tile(T_B, tile_b);
|
|
177
|
+
upload_tile(T_WALL, tile_wall);
|
|
148
178
|
|
|
149
179
|
BCPS = 0x80;
|
|
150
180
|
for (i = 0; i < 4; i++) {
|
|
@@ -165,6 +195,7 @@ void main(void) {
|
|
|
165
195
|
score = 0;
|
|
166
196
|
fall_timer = 0;
|
|
167
197
|
new_piece();
|
|
198
|
+
draw_well();
|
|
168
199
|
draw_grid();
|
|
169
200
|
|
|
170
201
|
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_TILE_DATA_LO;
|