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
|
@@ -30,8 +30,22 @@
|
|
|
30
30
|
#define T_SHIP (TILE_USER_INDEX + 1)
|
|
31
31
|
#define T_BULLET (TILE_USER_INDEX + 2)
|
|
32
32
|
#define T_ENEMY (TILE_USER_INDEX + 3)
|
|
33
|
+
#define T_SPACE (TILE_USER_INDEX + 4) /* nebula backdrop A (BG_B) */
|
|
34
|
+
#define T_STARS (TILE_USER_INDEX + 5) /* nebula backdrop B (BG_B) */
|
|
33
35
|
|
|
34
36
|
static const u32 tile_blank[8] = { 0,0,0,0,0,0,0,0 };
|
|
37
|
+
/* Deep-space backdrop tiled across BG_B so the playfield isn't a flat
|
|
38
|
+
* black void. Two distinct nebula blocks are checkerboarded so no single
|
|
39
|
+
* colour dominates the screen: T_SPACE is colour-4 nebula with colour-5
|
|
40
|
+
* star dots; T_STARS swaps the roles (colour-5 field, colour-4 dots). */
|
|
41
|
+
static const u32 tile_space[8] = {
|
|
42
|
+
0x44444444, 0x44455444, 0x44444444, 0x54444445,
|
|
43
|
+
0x44444444, 0x44455444, 0x44444444, 0x54444445,
|
|
44
|
+
};
|
|
45
|
+
static const u32 tile_stars[8] = {
|
|
46
|
+
0x55555555, 0x55544555, 0x55555555, 0x45555554,
|
|
47
|
+
0x55555555, 0x55544555, 0x55555555, 0x45555554,
|
|
48
|
+
};
|
|
35
49
|
static const u32 tile_ship[8] = {
|
|
36
50
|
0x00011000, 0x00011000, 0x00111100, 0x00111100,
|
|
37
51
|
0x01111110, 0x01111110, 0x11111111, 0x11000011,
|
|
@@ -102,8 +116,10 @@ int main(bool hard) {
|
|
|
102
116
|
|
|
103
117
|
/* PAL0 — used by player + font */
|
|
104
118
|
PAL_setColor(0 + 1, 0x0EEE); /* ship white */
|
|
105
|
-
/* PAL1 — bullet */
|
|
119
|
+
/* PAL1 — bullet + space backdrop */
|
|
106
120
|
PAL_setColor(16 + 2, 0x00EE); /* bullet yellow */
|
|
121
|
+
PAL_setColor(16 + 4, 0x0600); /* nebula deep blue */
|
|
122
|
+
PAL_setColor(16 + 5, 0x0402); /* nebula violet */
|
|
107
123
|
/* PAL2 — enemy */
|
|
108
124
|
PAL_setColor(32 + 3, 0x000E); /* enemy red */
|
|
109
125
|
|
|
@@ -113,6 +129,20 @@ int main(bool hard) {
|
|
|
113
129
|
VDP_loadTileData(tile_ship, T_SHIP, 1, DMA);
|
|
114
130
|
VDP_loadTileData(tile_bullet, T_BULLET, 1, DMA);
|
|
115
131
|
VDP_loadTileData(tile_enemy, T_ENEMY, 1, DMA);
|
|
132
|
+
VDP_loadTileData(tile_space, T_SPACE, 1, DMA);
|
|
133
|
+
VDP_loadTileData(tile_stars, T_STARS, 1, DMA);
|
|
134
|
+
|
|
135
|
+
/* Fill the far plane (BG_B) with the space backdrop so the screen
|
|
136
|
+
* isn't an empty black void; sprinkle denser star clusters for
|
|
137
|
+
* variety. Sprites (ship/bullets/enemies) always draw above the
|
|
138
|
+
* planes, so the gameplay reads on top of this with no priority
|
|
139
|
+
* juggling. */
|
|
140
|
+
for (u16 cy = 0; cy < 28; cy++)
|
|
141
|
+
for (u16 cx = 0; cx < 40; cx++)
|
|
142
|
+
VDP_setTileMapXY(BG_B,
|
|
143
|
+
TILE_ATTR_FULL(PAL1, 0, 0, 0,
|
|
144
|
+
((cx ^ cy) & 1) ? T_STARS : T_SPACE),
|
|
145
|
+
cx, cy);
|
|
116
146
|
|
|
117
147
|
player.x = 152; player.y = 180; player.alive = TRUE;
|
|
118
148
|
for (u16 i = 0; i < MAX_BULLETS; i++) bullets[i].alive = FALSE;
|
|
@@ -30,8 +30,22 @@
|
|
|
30
30
|
#define T_SHIP_P2 (TILE_USER_INDEX + 2)
|
|
31
31
|
#define T_BULLET (TILE_USER_INDEX + 3)
|
|
32
32
|
#define T_ENEMY (TILE_USER_INDEX + 4)
|
|
33
|
+
#define T_SPACE (TILE_USER_INDEX + 5) /* nebula backdrop A (BG_B) */
|
|
34
|
+
#define T_STARS (TILE_USER_INDEX + 6) /* nebula backdrop B (BG_B) */
|
|
33
35
|
|
|
34
36
|
static const u32 tile_blank[8] = { 0,0,0,0,0,0,0,0 };
|
|
37
|
+
/* Deep-space backdrop tiled across BG_B so the shared playfield isn't a
|
|
38
|
+
* flat black void. Two distinct nebula blocks are checkerboarded so no
|
|
39
|
+
* single colour dominates: T_SPACE is colour-5 nebula with colour-6 star
|
|
40
|
+
* dots; T_STARS swaps the roles. Palette 1, colours set in main(). */
|
|
41
|
+
static const u32 tile_space[8] = {
|
|
42
|
+
0x55555555, 0x55566555, 0x55555555, 0x65555556,
|
|
43
|
+
0x55555555, 0x55566555, 0x55555555, 0x65555556,
|
|
44
|
+
};
|
|
45
|
+
static const u32 tile_stars[8] = {
|
|
46
|
+
0x66666666, 0x66655666, 0x66666666, 0x56666665,
|
|
47
|
+
0x66666666, 0x66655666, 0x66666666, 0x56666665,
|
|
48
|
+
};
|
|
35
49
|
/* P1 ship — palette 0 colour 1 (white). */
|
|
36
50
|
static const u32 tile_ship_p1[8] = {
|
|
37
51
|
0x00011000, 0x00011000, 0x00111100, 0x00111100,
|
|
@@ -116,6 +130,10 @@ int main(bool hard) {
|
|
|
116
130
|
PAL_setColor(0 + 1, 0x0EEE);
|
|
117
131
|
PAL_setColor(0 + 2, 0x00EE);
|
|
118
132
|
PAL_setColor(0 + 4, 0x000E);
|
|
133
|
+
/* Palette 1 = space backdrop (colours 5/6, kept clear of the PAL0
|
|
134
|
+
* sprite colours so the shared planes never clash). */
|
|
135
|
+
PAL_setColor(16 + 5, 0x0600); /* nebula deep blue */
|
|
136
|
+
PAL_setColor(16 + 6, 0x0402); /* nebula violet */
|
|
119
137
|
/* Palette 2 = enemy red */
|
|
120
138
|
PAL_setColor(32 + 3, 0x00EE);
|
|
121
139
|
|
|
@@ -126,6 +144,19 @@ int main(bool hard) {
|
|
|
126
144
|
VDP_loadTileData(tile_ship_p2, T_SHIP_P2, 1, DMA);
|
|
127
145
|
VDP_loadTileData(tile_bullet, T_BULLET, 1, DMA);
|
|
128
146
|
VDP_loadTileData(tile_enemy, T_ENEMY, 1, DMA);
|
|
147
|
+
VDP_loadTileData(tile_space, T_SPACE, 1, DMA);
|
|
148
|
+
VDP_loadTileData(tile_stars, T_STARS, 1, DMA);
|
|
149
|
+
|
|
150
|
+
/* Fill the far plane (BG_B) with the space backdrop so the shared
|
|
151
|
+
* playfield isn't an empty black void; sprites always draw above the
|
|
152
|
+
* planes, so both ships + bullets + enemies read on top with no
|
|
153
|
+
* priority juggling. */
|
|
154
|
+
for (u16 cy = 0; cy < 28; cy++)
|
|
155
|
+
for (u16 cx = 0; cx < 40; cx++)
|
|
156
|
+
VDP_setTileMapXY(BG_B,
|
|
157
|
+
TILE_ATTR_FULL(PAL1, 0, 0, 0,
|
|
158
|
+
((cx ^ cy) & 1) ? T_STARS : T_SPACE),
|
|
159
|
+
cx, cy);
|
|
129
160
|
|
|
130
161
|
p1.x = 100; p1.y = 180; p1.alive = TRUE;
|
|
131
162
|
p2.x = 220; p2.y = 180; p2.alive = TRUE;
|
|
@@ -25,9 +25,29 @@
|
|
|
25
25
|
* (the byte layout produced by `xgm2tool input.vgm output.xgc`). */
|
|
26
26
|
extern const u8 music_xgm[];
|
|
27
27
|
|
|
28
|
+
/* A backdrop block tiled across plane B so the title screen isn't a flat
|
|
29
|
+
* black void (text alone on black reads as "blank" to a human). Colour
|
|
30
|
+
* index 4 field with a colour-5 frame — both set below. */
|
|
31
|
+
static const u32 tile_bg[8] = {
|
|
32
|
+
0x44444444, 0x45555554, 0x45000054, 0x45000054,
|
|
33
|
+
0x45000054, 0x45000054, 0x45555554, 0x44444444,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
#define T_BG (TILE_USER_INDEX + 0)
|
|
37
|
+
|
|
28
38
|
int main(bool hard) {
|
|
29
39
|
(void)hard;
|
|
30
40
|
|
|
41
|
+
/* Plane-B backdrop colours (palette 1) + a tiled backdrop so the
|
|
42
|
+
* music-demo title screen has a visible background behind the text.
|
|
43
|
+
* Plane A (the font) draws above plane B, so the text reads on top. */
|
|
44
|
+
PAL_setColor(16 + 4, 0x0840); /* dark green field */
|
|
45
|
+
PAL_setColor(16 + 5, 0x0C60); /* lighter frame */
|
|
46
|
+
VDP_loadTileData(tile_bg, T_BG, 1, DMA);
|
|
47
|
+
for (u16 cy = 0; cy < 28; cy++)
|
|
48
|
+
for (u16 cx = 0; cx < 40; cx++)
|
|
49
|
+
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1, 0, 0, 0, T_BG), cx, cy);
|
|
50
|
+
|
|
31
51
|
/* Title screen text — drawn into VDP plane A's default font region. */
|
|
32
52
|
VDP_drawText("XGM2 MUSIC DEMO", 12, 10);
|
|
33
53
|
VDP_drawText("ROM-DEV-MCP / SGDK", 10, 12);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
/* Game Gear hello-world.
|
|
2
|
-
*
|
|
3
|
-
* P1-B1 scrolls the BG by one pixel per frame.
|
|
1
|
+
/* Game Gear hello-world. Paints the whole 160×144 visible viewport with
|
|
2
|
+
* a blue BG panel, drops a yellow 'H' tile in its centre, enables
|
|
3
|
+
* display. Pressing P1-B1 scrolls the BG by one pixel per frame.
|
|
4
4
|
*
|
|
5
|
-
* Self-contained — inlines the VDP helpers so this single file
|
|
5
|
+
* Self-contained — inlines the VDP helpers so this single file plus the
|
|
6
6
|
* bundled gg_crt0.s are all you need to compile and run. For a more
|
|
7
|
-
* modular multi-file project that pulls in
|
|
7
|
+
* modular multi-file project that pulls in the bundled GG runtime,
|
|
8
8
|
* see `hello_sprite` and `tile_engine`.
|
|
9
9
|
*
|
|
10
10
|
* GG specifics this file demonstrates (read MENTAL_MODEL.md for details):
|
|
11
11
|
* - Visible viewport is 160×144 centered in the 256×192 framebuffer.
|
|
12
12
|
* OAM/name-table coords are in 256×192 space; the visible region
|
|
13
|
-
* starts at hw coord (48, 24). The 'H' goes at name-table row
|
|
14
|
-
* col
|
|
13
|
+
* starts at hw coord (48, 24). The 'H' goes at name-table row 11
|
|
14
|
+
* col 15 = visible center.
|
|
15
15
|
* - CRAM entries are 12-bit BGR (2 bytes each, little-endian), 16 BG
|
|
16
16
|
* colors at $0000-$1F and 16 sprite colors at $0020-$3F.
|
|
17
17
|
* - VDP R6 default is 0xFB → sprite tiles read from $0000 (NOT $2000
|
|
@@ -86,13 +86,17 @@ static void vdp_init(void) {
|
|
|
86
86
|
static const uint8_t palette[32] = {
|
|
87
87
|
0x00, 0x00, /* 0 backdrop (black) */
|
|
88
88
|
0xFF, 0x00, /* 1 yellow */
|
|
89
|
-
0x00,
|
|
89
|
+
0x00, 0x0F, /* 2 blue (panel fill A) */
|
|
90
|
+
0x0F, 0x00, /* 3 red (panel fill B) */
|
|
91
|
+
0x00, 0x00, 0x00, 0x00,
|
|
90
92
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
91
93
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
92
94
|
};
|
|
93
95
|
|
|
94
|
-
/*
|
|
95
|
-
*
|
|
96
|
+
/* Tile 1 = an 'H' glyph. Tile 2 = a solid colour-2 fill used to paint
|
|
97
|
+
* the whole visible viewport so the screen is obviously not blank.
|
|
98
|
+
* Mode 4 stores 4 bytes per scanline (one per bitplane). Plane 0 set →
|
|
99
|
+
* colour index 1; plane 1 set → colour index 2. */
|
|
96
100
|
static const uint8_t tile_h[32] = {
|
|
97
101
|
0x66, 0x00, 0x00, 0x00,
|
|
98
102
|
0x66, 0x00, 0x00, 0x00,
|
|
@@ -103,6 +107,21 @@ static const uint8_t tile_h[32] = {
|
|
|
103
107
|
0x66, 0x00, 0x00, 0x00,
|
|
104
108
|
0x00, 0x00, 0x00, 0x00,
|
|
105
109
|
};
|
|
110
|
+
/* Tile 2 = solid colour 2 (blue). Tile 3 = solid colour 3 (red). The
|
|
111
|
+
* viewport is painted as a blue/red checkerboard so no single colour
|
|
112
|
+
* dominates the screen — a clear "it works" panel, not a flat fill. */
|
|
113
|
+
static const uint8_t tile_fill2[32] = {
|
|
114
|
+
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
115
|
+
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
116
|
+
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
117
|
+
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
118
|
+
};
|
|
119
|
+
static const uint8_t tile_fill3[32] = {
|
|
120
|
+
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
121
|
+
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
122
|
+
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
123
|
+
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
124
|
+
};
|
|
106
125
|
|
|
107
126
|
static void load_palette(void) {
|
|
108
127
|
uint8_t i;
|
|
@@ -110,23 +129,42 @@ static void load_palette(void) {
|
|
|
110
129
|
for (i = 0; i < 32; i++) PORT_VDP_DATA = palette[i];
|
|
111
130
|
}
|
|
112
131
|
|
|
113
|
-
static void
|
|
132
|
+
static void load_tiles(void) {
|
|
114
133
|
uint8_t i;
|
|
115
134
|
vdp_set_addr(32, VDP_VRAM_WRITE); /* tile slot 1 = VRAM offset 32 */
|
|
116
135
|
for (i = 0; i < 32; i++) PORT_VDP_DATA = tile_h[i];
|
|
136
|
+
vdp_set_addr(64, VDP_VRAM_WRITE); /* tile slot 2 = VRAM offset 64 */
|
|
137
|
+
for (i = 0; i < 32; i++) PORT_VDP_DATA = tile_fill2[i];
|
|
138
|
+
vdp_set_addr(96, VDP_VRAM_WRITE); /* tile slot 3 = VRAM offset 96 */
|
|
139
|
+
for (i = 0; i < 32; i++) PORT_VDP_DATA = tile_fill3[i];
|
|
117
140
|
}
|
|
118
141
|
|
|
119
|
-
|
|
142
|
+
/* Clear the whole 32×28 name table, then paint the visible 160×144
|
|
143
|
+
* viewport (cols 6..25, rows 3..20) as a solid blue panel inside a red
|
|
144
|
+
* border frame — a clean "it works" screen that is obviously neither
|
|
145
|
+
* blank nor a single flat colour. */
|
|
146
|
+
static void draw_background(void) {
|
|
147
|
+
uint8_t row, col, fill;
|
|
120
148
|
uint16_t i;
|
|
121
149
|
vdp_set_addr(0x3800, VDP_VRAM_WRITE);
|
|
122
150
|
for (i = 0; i < 1792; i++) PORT_VDP_DATA = 0;
|
|
151
|
+
for (row = GG_VIS_ROW_MIN; row <= GG_VIS_ROW_MAX; row++) {
|
|
152
|
+
vdp_set_addr(0x3800 + (row * 32 + GG_VIS_COL_MIN) * 2, VDP_VRAM_WRITE);
|
|
153
|
+
for (col = GG_VIS_COL_MIN; col <= GG_VIS_COL_MAX; col++) {
|
|
154
|
+
if (row == GG_VIS_ROW_MIN || row == GG_VIS_ROW_MAX ||
|
|
155
|
+
col == GG_VIS_COL_MIN || col == GG_VIS_COL_MAX)
|
|
156
|
+
fill = 3; /* red border */
|
|
157
|
+
else
|
|
158
|
+
fill = 2; /* blue interior */
|
|
159
|
+
PORT_VDP_DATA = fill;
|
|
160
|
+
PORT_VDP_DATA = 0;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
123
163
|
}
|
|
124
164
|
|
|
125
|
-
/* Drop the 'H' at name-table (row
|
|
126
|
-
* sits at name-table cols 6..25 / rows 3..20 — so (12,16) is roughly
|
|
127
|
-
* the visible center. */
|
|
165
|
+
/* Drop the 'H' at name-table (row 11, col 15) — the visible centre. */
|
|
128
166
|
static void place_h(void) {
|
|
129
|
-
vdp_set_addr(0x3800 + (
|
|
167
|
+
vdp_set_addr(0x3800 + (11 * 32 + 15) * 2, VDP_VRAM_WRITE);
|
|
130
168
|
PORT_VDP_DATA = 1;
|
|
131
169
|
PORT_VDP_DATA = 0;
|
|
132
170
|
}
|
|
@@ -140,8 +178,8 @@ void main(void) {
|
|
|
140
178
|
|
|
141
179
|
vdp_init();
|
|
142
180
|
load_palette();
|
|
143
|
-
|
|
144
|
-
|
|
181
|
+
load_tiles();
|
|
182
|
+
draw_background();
|
|
145
183
|
place_h();
|
|
146
184
|
|
|
147
185
|
vdp_write_reg(1, 0xE0); /* display ON, vblank IRQ on, 192-line */
|
|
@@ -29,6 +29,7 @@ extern void gg_vdp_write_reg(uint8_t reg, uint8_t value);
|
|
|
29
29
|
extern void gg_vdp_set_addr(uint16_t addr, uint8_t prefix);
|
|
30
30
|
extern void gg_load_palette(const uint8_t *palette);
|
|
31
31
|
extern void gg_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
|
|
32
|
+
extern void gg_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
|
|
32
33
|
extern void gg_vblank_wait(void);
|
|
33
34
|
extern uint8_t gg_joypad_read(void);
|
|
34
35
|
extern void gg_sprite_init(void);
|
|
@@ -41,14 +42,33 @@ extern void gg_sat_upload(void);
|
|
|
41
42
|
* array = garbage = INVISIBLE sprites. Sprite colour index N uses entry 16+N,
|
|
42
43
|
* so sprite colour 1 = entry 17 (white here). */
|
|
43
44
|
static const uint8_t palette[64] = {
|
|
44
|
-
/* BG 0-15:
|
|
45
|
-
0x20,0x02,
|
|
45
|
+
/* BG 0-15: 0 = dark navy backdrop, 1 = teal, 2 = blue (dither tones) */
|
|
46
|
+
0x20,0x02, 0xC8,0x08, 0x80,0x0C, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
46
47
|
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
47
48
|
/* SPRITE 16-31: 16=transparent, 17=white */
|
|
48
49
|
0,0, 0xFF,0x0F, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
49
50
|
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
50
51
|
};
|
|
51
52
|
|
|
53
|
+
/* One dithered BG tile (BG bank $0000): plane0/plane1 alternate so pixels
|
|
54
|
+
* flip between colour 1 (teal) and colour 2 (blue). Filling the name table
|
|
55
|
+
* with it gives a two-tone backdrop so the frame is never a flat colour —
|
|
56
|
+
* a uniform fill still reads as a blank screen. The dither fills the whole
|
|
57
|
+
* 256x192 frame, so it shows in the GG's centered 160x144 window too. */
|
|
58
|
+
static const uint8_t bg_tile[32] = {
|
|
59
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
60
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
61
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
62
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
static void draw_bg(void) {
|
|
66
|
+
uint8_t row, col;
|
|
67
|
+
for (row = 0; row < 28; row++)
|
|
68
|
+
for (col = 0; col < 32; col++)
|
|
69
|
+
gg_set_tilemap_cell(row, col, 0, 0);
|
|
70
|
+
}
|
|
71
|
+
|
|
52
72
|
/* ── Game Gear visible viewport ──────────────────────────────────────
|
|
53
73
|
* Sprite OAM uses SMS HARDWARE coordinates (256x192 space), but the GG
|
|
54
74
|
* LCD only shows the CENTER 160x144. Keep the sprite inside this box or
|
|
@@ -73,6 +93,9 @@ void main(void) {
|
|
|
73
93
|
|
|
74
94
|
gg_vdp_init();
|
|
75
95
|
gg_load_palette(palette);
|
|
96
|
+
/* BG dither tile → BG bank $0000, paint the whole name table. */
|
|
97
|
+
gg_load_tiles(0x0000, bg_tile, 32);
|
|
98
|
+
draw_bg();
|
|
76
99
|
/* Upload one sprite tile to VRAM $2000 (sprite tile area). */
|
|
77
100
|
gg_load_tiles(0x2000, sprite_tile, 32);
|
|
78
101
|
|
|
@@ -30,6 +30,7 @@ extern void gg_vdp_display_on(void);
|
|
|
30
30
|
extern void gg_vdp_set_addr(uint16_t addr, uint8_t prefix);
|
|
31
31
|
extern void gg_load_palette(const uint8_t *palette);
|
|
32
32
|
extern void gg_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
|
|
33
|
+
extern void gg_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
|
|
33
34
|
extern void gg_vblank_wait(void);
|
|
34
35
|
extern uint8_t gg_joypad_read(void);
|
|
35
36
|
extern void gg_sprite_init(void);
|
|
@@ -41,14 +42,32 @@ extern void gg_sat_upload(void);
|
|
|
41
42
|
* (entries 16-31) reading garbage = invisible sprites. Sprite palette:
|
|
42
43
|
* 16 = transparent, 17 = white, 18 = green, 19 = red. */
|
|
43
44
|
static const uint8_t palette[64] = {
|
|
44
|
-
/* BG 0-15:
|
|
45
|
-
0x20,0x02,
|
|
45
|
+
/* BG 0-15: 0 = dark navy backdrop, 1 = teal, 2 = blue (dither tones) */
|
|
46
|
+
0x20,0x02, 0xC8,0x08, 0x80,0x0C, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
46
47
|
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
47
48
|
/* SPRITE 16-31: 16=transparent, 17=white, 18=green, 19=red */
|
|
48
49
|
0,0, 0xFF,0x0F, 0xF0,0x00, 0x0F,0x00, 0,0, 0,0, 0,0, 0,0,
|
|
49
50
|
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
50
51
|
};
|
|
51
52
|
|
|
53
|
+
/* One dithered BG tile (BG bank $0000): plane0/plane1 alternate so pixels
|
|
54
|
+
* flip between colour 1 (teal) and colour 2 (blue). Filling the name table
|
|
55
|
+
* with it gives a two-tone backdrop behind the song indicators so the frame
|
|
56
|
+
* never reads as a single flat colour. */
|
|
57
|
+
static const uint8_t bg_tile[32] = {
|
|
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
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
static void draw_bg(void) {
|
|
65
|
+
uint8_t row, col;
|
|
66
|
+
for (row = 0; row < 28; row++)
|
|
67
|
+
for (col = 0; col < 32; col++)
|
|
68
|
+
gg_set_tilemap_cell(row, col, 0, 0);
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
/* Three 8×8 sprite tiles, 4bpp interleaved (4 planes × 8 rows):
|
|
53
72
|
* tile 0: solid color 1 (white)
|
|
54
73
|
* tile 1: solid color 2 (green)
|
|
@@ -99,6 +118,9 @@ void main(void) {
|
|
|
99
118
|
|
|
100
119
|
gg_vdp_init();
|
|
101
120
|
gg_load_palette(palette);
|
|
121
|
+
/* BG dither tile → BG bank $0000, paint the whole name table. */
|
|
122
|
+
gg_load_tiles(0x0000, bg_tile, 32);
|
|
123
|
+
draw_bg();
|
|
102
124
|
gg_load_tiles(0x2000, sprite_tiles, sizeof(sprite_tiles));
|
|
103
125
|
gg_sprite_init();
|
|
104
126
|
|
|
@@ -52,11 +52,12 @@ extern void gg_sat_upload(void);
|
|
|
52
52
|
|
|
53
53
|
/* GG palette = 32 entries × 2 bytes (4-4-4 BGR LE): low=(g<<4)|r, high=b.
|
|
54
54
|
* gg_load_palette reads 64 bytes; a 32-byte array leaves the sprite palette
|
|
55
|
-
* (entries 16-31) reading garbage = invisible sprites. BG colour 1 =
|
|
56
|
-
*
|
|
55
|
+
* (entries 16-31) reading garbage = invisible sprites. BG colour 1 = light
|
|
56
|
+
* sky blue, 2 = darker sky blue (the sky dither), 3 = mid-grey wall; sprite
|
|
57
|
+
* colour 1 = entry 17 (white player). */
|
|
57
58
|
static const uint8_t palette[64] = {
|
|
58
|
-
/* BG 0-15:
|
|
59
|
-
0x20,0x02,
|
|
59
|
+
/* BG 0-15: 0 = dark navy backdrop, 1 = light sky, 2 = dark sky, 3 = wall grey */
|
|
60
|
+
0x20,0x02, 0xFB,0x0F, 0xC8,0x0C, 0x88,0x08, 0,0, 0,0, 0,0, 0,0,
|
|
60
61
|
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
61
62
|
/* SPRITE 16-31: 16=transparent, 17=white player */
|
|
62
63
|
0,0, 0xFF,0x0F, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
@@ -64,14 +65,19 @@ static const uint8_t palette[64] = {
|
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
static const uint8_t bg_tiles[32 * 2] = {
|
|
67
|
-
/* T_OPEN —
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
/* T_OPEN — dithered sky: pixels alternate colour 1 (light) / colour 2
|
|
69
|
+
* (dark) so the sky fills the screen but no single colour dominates.
|
|
70
|
+
* plane0 = 0xAA (cols 0,2,4,6 -> colour 1), plane1 = 0x55 (cols 1,3,5,7
|
|
71
|
+
* -> colour 2). */
|
|
72
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
73
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
74
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
75
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
76
|
+
/* T_WALL — solid block in colour 3 (planes 0+1 set) */
|
|
77
|
+
0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
|
|
78
|
+
0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
|
|
79
|
+
0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
|
|
80
|
+
0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
|
|
75
81
|
};
|
|
76
82
|
|
|
77
83
|
static const uint8_t player_tile[32] = {
|
|
@@ -25,16 +25,19 @@ extern uint8_t gg_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
|
|
31
|
-
|
|
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 *
|
|
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,13 +89,30 @@ 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
|
|
92
|
+
return T_FIELD; /* empty cell shows the dim well interior, not backdrop */
|
|
80
93
|
}
|
|
81
94
|
|
|
95
|
+
/* GG shows only the centered cols 6..25 / rows 3..20. Place the 6×12 grid
|
|
96
|
+
* at tilemap cols 7..12, rows 4..15 so the whole well sits inside that
|
|
97
|
+
* visible band. */
|
|
82
98
|
static void draw_cell(int8_t col, int8_t row, uint8_t cell) {
|
|
83
99
|
if (row < 0 || row >= ROWS) return;
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
gg_set_tilemap_cell((uint8_t)(row + 4), (uint8_t)(col + 7), tile_for(cell), 0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Draw the well: a grey border frame around the 6×12 play field with a dim
|
|
104
|
+
* field interior, so the playfield is clearly visible even when empty. The
|
|
105
|
+
* grid maps cell (col,row) -> tilemap (row+4, col+7) = rows 4..15 cols 7..12.
|
|
106
|
+
* Frame the perimeter at rows 3..16, cols 6..13 — inside the GG viewport. */
|
|
107
|
+
static void draw_well(void) {
|
|
108
|
+
uint8_t r, c;
|
|
109
|
+
for (r = 3; r <= 16; r++) {
|
|
110
|
+
for (c = 6; c <= 13; c++) {
|
|
111
|
+
uint8_t t = T_FIELD;
|
|
112
|
+
if (r == 3 || r == 16 || c == 6 || c == 13) t = T_WALL;
|
|
113
|
+
gg_set_tilemap_cell(r, c, t, 0);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
86
116
|
}
|
|
87
117
|
|
|
88
118
|
static void draw_grid(void) {
|
|
@@ -151,7 +181,7 @@ void main(void) {
|
|
|
151
181
|
|
|
152
182
|
gg_vdp_init();
|
|
153
183
|
gg_load_palette(palette);
|
|
154
|
-
gg_load_tiles(0x0000, bg_tiles, 32 *
|
|
184
|
+
gg_load_tiles(0x0000, bg_tiles, 32 * 6);
|
|
155
185
|
|
|
156
186
|
for (r = 0; r < 24; r++) for (c = 0; c < 32; c++) gg_set_tilemap_cell(r, c, T_BLANK, 0);
|
|
157
187
|
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) grid[r][c] = 0;
|
|
@@ -159,6 +189,7 @@ void main(void) {
|
|
|
159
189
|
score = 0;
|
|
160
190
|
fall_timer = 0;
|
|
161
191
|
new_piece();
|
|
192
|
+
draw_well();
|
|
162
193
|
draw_grid();
|
|
163
194
|
|
|
164
195
|
sfx_init();
|
|
@@ -18,6 +18,7 @@ extern void gg_vdp_init(void);
|
|
|
18
18
|
extern void gg_vdp_display_on(void);
|
|
19
19
|
extern void gg_load_palette(const uint8_t *palette);
|
|
20
20
|
extern void gg_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
|
|
21
|
+
extern void gg_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
|
|
21
22
|
extern void gg_vblank_wait(void);
|
|
22
23
|
extern uint8_t gg_joypad_read(void);
|
|
23
24
|
extern void gg_sprite_init(void);
|
|
@@ -32,25 +33,49 @@ extern void gg_sat_upload(void);
|
|
|
32
33
|
#define VIS_X1 207 /* 48 + 160 - 1 */
|
|
33
34
|
#define VIS_Y1 167 /* 24 + 144 - 1 */
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
#define
|
|
36
|
+
/* Explicit (uint8_t) casts: the computed int expressions all fit in a byte,
|
|
37
|
+
* but SDCC warns (158) on the implicit int->uint8_t narrowing in the const
|
|
38
|
+
* initializers below unless the conversion is spelled out. */
|
|
39
|
+
#define LANE_LEFT_X ((uint8_t)(VIS_X0 + 28)) /* 76 */
|
|
40
|
+
#define LANE_MID_X ((uint8_t)((VIS_X0 + VIS_X1) / 2 - 4)) /* ~123 */
|
|
41
|
+
#define LANE_RIGHT_X ((uint8_t)(VIS_X1 - 36)) /* 171 */
|
|
42
|
+
#define PLAYER_Y ((uint8_t)(VIS_Y1 - 16))
|
|
39
43
|
#define MAX_OBSTACLES 4
|
|
40
44
|
|
|
41
45
|
/* GG palette = 32 entries × 2 bytes (4-4-4 BGR LE): low=(g<<4)|r, high=b.
|
|
42
46
|
* gg_load_palette reads 64 bytes; a 32-byte array leaves the sprite palette
|
|
43
|
-
* (entries 16-31) reading garbage = invisible sprites.
|
|
44
|
-
*
|
|
47
|
+
* (entries 16-31) reading garbage = invisible sprites. BG colour 1 = grass
|
|
48
|
+
* green, BG colour 2 = road grey. Sprite colour 1 = entry 17 (white),
|
|
49
|
+
* colour 2 = entry 18 (red). */
|
|
45
50
|
static const uint8_t palette[64] = {
|
|
46
|
-
/* BG 0-15:
|
|
47
|
-
0x20,0x02,
|
|
51
|
+
/* BG 0-15: 0 = dark navy backdrop, 1 = grass green, 2 = road grey */
|
|
52
|
+
0x20,0x02, 0x60,0x00, 0x66,0x06, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
48
53
|
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
49
54
|
/* SPRITE 16-31: 16=transparent, 17=white, 18=red */
|
|
50
55
|
0,0, 0xFF,0x0F, 0x0F,0x00, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
51
56
|
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
52
57
|
};
|
|
53
58
|
|
|
59
|
+
/* Three BG tiles for the track, loaded into the BG tile bank at $0000:
|
|
60
|
+
* tile 0 = blank (all zeros → shows the backdrop colour)
|
|
61
|
+
* tile 1 = solid grass (colour 1)
|
|
62
|
+
* tile 2 = solid road (colour 2) */
|
|
63
|
+
static const uint8_t bg_tiles[96] = {
|
|
64
|
+
/* BG tile 0 = blank */
|
|
65
|
+
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
|
66
|
+
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
|
67
|
+
/* BG tile 1 = grass (colour 1 → plane 0 set) */
|
|
68
|
+
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
69
|
+
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
70
|
+
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
71
|
+
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
72
|
+
/* BG tile 2 = road (colour 2 → plane 1 set) */
|
|
73
|
+
0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
|
|
74
|
+
0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
|
|
75
|
+
0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
|
|
76
|
+
0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
|
|
77
|
+
};
|
|
78
|
+
|
|
54
79
|
/* Two sprite tiles — player (colour 1) + enemy (colour 2). */
|
|
55
80
|
static const uint8_t tiles[64] = {
|
|
56
81
|
/* Tile 0 = player car (colour 1 → plane 0 set) */
|
|
@@ -65,6 +90,28 @@ static const uint8_t tiles[64] = {
|
|
|
65
90
|
0x00,0x7E,0x00,0x00, 0x00,0x66,0x00,0x00,
|
|
66
91
|
};
|
|
67
92
|
|
|
93
|
+
/* Paint the visible viewport: grey road down the centre lanes, green
|
|
94
|
+
* grass on the shoulders. Visible name-table region is cols 6..25,
|
|
95
|
+
* rows 3..20 (the centered 160×144). BG tile bank is $0000. */
|
|
96
|
+
#define VIS_COL_MIN 6
|
|
97
|
+
#define VIS_COL_MAX 25
|
|
98
|
+
#define VIS_ROW_MIN 3
|
|
99
|
+
#define VIS_ROW_MAX 20
|
|
100
|
+
static void draw_track(void) {
|
|
101
|
+
uint8_t row, col;
|
|
102
|
+
/* Blank the whole 32×28 name table to backdrop (tile 0). */
|
|
103
|
+
for (row = 0; row < 28; row++)
|
|
104
|
+
for (col = 0; col < 32; col++) gg_set_tilemap_cell(row, col, 0, 0);
|
|
105
|
+
/* Paint the visible viewport: road (tile 2) down the central lanes,
|
|
106
|
+
* grass (tile 1) on the shoulders. */
|
|
107
|
+
for (row = VIS_ROW_MIN; row <= VIS_ROW_MAX; row++) {
|
|
108
|
+
for (col = VIS_COL_MIN; col <= VIS_COL_MAX; col++) {
|
|
109
|
+
uint8_t road = (col >= VIS_COL_MIN + 4 && col <= VIS_COL_MAX - 4);
|
|
110
|
+
gg_set_tilemap_cell(row, col, road ? 2 : 1, 0);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
68
115
|
typedef struct { uint8_t x, y, alive; } Car;
|
|
69
116
|
|
|
70
117
|
static Car player;
|
|
@@ -110,7 +157,9 @@ void main(void) {
|
|
|
110
157
|
uint8_t i;
|
|
111
158
|
gg_vdp_init();
|
|
112
159
|
gg_load_palette(palette);
|
|
113
|
-
gg_load_tiles(
|
|
160
|
+
gg_load_tiles(0x0000, bg_tiles, 96); /* BG tiles → BG bank $0000 */
|
|
161
|
+
gg_load_tiles(0x2000, tiles, 64); /* sprite tiles → sprite bank $2000 */
|
|
162
|
+
draw_track();
|
|
114
163
|
gg_sprite_init();
|
|
115
164
|
sfx_init();
|
|
116
165
|
gg_vdp_display_on();
|