romdevtools 0.21.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 +15 -4
- package/CHANGELOG.md +58 -0
- package/examples/atari7800/templates/hello_sprite.c +48 -4
- package/examples/atari7800/templates/music_demo.c +47 -2
- package/examples/c64/templates/tile_engine.c +77 -27
- package/examples/gb/templates/hello_sprite.c +15 -6
- package/examples/gb/templates/music_demo.c +36 -0
- package/examples/gb/templates/platformer.c +3 -2
- package/examples/gb/templates/puzzle.c +3 -2
- package/examples/gb/templates/racing.c +3 -2
- package/examples/gb/templates/shmup.c +3 -2
- package/examples/gb/templates/sports.c +3 -2
- package/examples/gb/templates/tile_engine.c +3 -2
- package/examples/gba/templates/maxmod_demo.c +36 -2
- package/examples/gba/templates/platformer.c +3 -1
- package/examples/gba/templates/tonc_hello_sprite.c +35 -1
- package/examples/gbc/templates/hello_sprite.c +12 -3
- package/examples/gbc/templates/music_demo.c +56 -12
- package/examples/gbc/templates/platformer.c +3 -2
- package/examples/gbc/templates/puzzle.c +3 -2
- package/examples/gbc/templates/racing.c +3 -2
- package/examples/gbc/templates/shmup.c +3 -2
- package/examples/gbc/templates/sports.c +3 -2
- 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/shmup_2p.c +31 -0
- package/examples/genesis/templates/xgm2_demo.c +20 -0
- package/examples/gg/templates/hello_sprite.c +25 -2
- package/examples/gg/templates/music_demo.c +24 -2
- package/examples/gg/templates/racing.c +7 -4
- package/examples/gg/templates/sports.c +11 -13
- package/examples/gg/templates/tile_engine.c +12 -6
- package/examples/lynx/templates/hello_sprite.c +15 -1
- package/examples/lynx/templates/music_demo.c +13 -1
- package/examples/nes/templates/hello_sprite.c +35 -0
- package/examples/nes/templates/music_demo.c +40 -0
- package/examples/pce/catch_game/main.c +22 -3
- package/examples/pce/music_sfx/main.c +28 -1
- package/examples/pce/sprite_move/main.c +7 -2
- package/examples/sms/templates/hello_sprite.c +29 -3
- package/examples/sms/templates/music_demo.c +18 -4
- package/examples/sms/templates/shmup_2p.c +24 -1
- package/examples/sms/templates/sports.c +4 -2
- 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/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.c +4 -1
- package/examples/snes/templates/puzzle.c +4 -1
- package/package.json +1 -1
- package/src/cheats/gamegenie.js +0 -1
- package/src/cli/smoke.js +1 -3
- package/src/host/LibretroHost.js +69 -15
- 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/http/routes.js +1 -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 +0 -1
- 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 +1 -1
- package/src/mcp/tools/disasm.js +2 -3
- package/src/mcp/tools/find-references.js +1 -2
- package/src/mcp/tools/font-map.js +1 -1
- 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 +14 -2
- 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 +0 -2
- 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/tile-inspect.js +1 -1
- package/src/mcp/tools/toolchain.js +29 -9
- package/src/mcp/tools/watch-memory.js +13 -3
- 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/TROUBLESHOOTING.md +6 -0
- package/src/platforms/c64/d64.js +0 -1
- 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/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/TROUBLESHOOTING.md +6 -0
- 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/TROUBLESHOOTING.md +6 -0
- 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/toolchains/asar/asar.js +0 -9
- package/src/toolchains/assemble-snippet.js +30 -12
- package/src/toolchains/cc65/presets/nes/chr-ram-runtime.cfg +14 -1
- package/src/toolchains/common/reassemble.js +0 -1
- 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
|
@@ -47,6 +47,19 @@ extern const u8 soundbank_bin[];
|
|
|
47
47
|
* Adjust if you regenerate with a differently-named .xm. */
|
|
48
48
|
#define MOD_CHIPTUNE 0
|
|
49
49
|
|
|
50
|
+
/* ── Backdrop tiles (4bpp) ───────────────────────────────────────────
|
|
51
|
+
* Two solid-colour tiles so we can lay a two-tone checkerboard across the
|
|
52
|
+
* whole BG1 map. Without a filled backdrop the screen is just the black
|
|
53
|
+
* backdrop colour plus a few text glyphs — which reads as "blank". */
|
|
54
|
+
static const u32 tile_solid1[8] = {
|
|
55
|
+
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
56
|
+
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
57
|
+
};
|
|
58
|
+
static const u32 tile_solid2[8] = {
|
|
59
|
+
0x22222222, 0x22222222, 0x22222222, 0x22222222,
|
|
60
|
+
0x22222222, 0x22222222, 0x22222222, 0x22222222,
|
|
61
|
+
};
|
|
62
|
+
|
|
50
63
|
int main(void) {
|
|
51
64
|
/* ── IRQ setup ── Maxmod requires mmVBlank() in the VBlank slot.
|
|
52
65
|
* Without this, the mixer DMA buffers never get swapped and audio
|
|
@@ -55,9 +68,30 @@ int main(void) {
|
|
|
55
68
|
irq_init(NULL);
|
|
56
69
|
irq_add(II_VBLANK, mmVBlank);
|
|
57
70
|
|
|
58
|
-
/*
|
|
71
|
+
/* ── Filled checkerboard backdrop on BG1 ─────────────────────────
|
|
72
|
+
* Lay a two-tone checkerboard across the whole 32x32 BG1 map so the
|
|
73
|
+
* frame has real content behind the text instead of a flat black
|
|
74
|
+
* backdrop (which reads as "blank"). Tiles → char-block 1, map →
|
|
75
|
+
* screen-block 28 — clear of TTE's char-block 0 / screen-block 31. */
|
|
76
|
+
pal_bg_mem[0] = CLR_BLACK;
|
|
77
|
+
pal_bg_mem[1] = RGB15(3, 6, 14); /* deep sky blue */
|
|
78
|
+
pal_bg_mem[2] = RGB15(2, 4, 9); /* darker navy */
|
|
79
|
+
tonccpy(&tile_mem[1][1], tile_solid1, sizeof(tile_solid1));
|
|
80
|
+
tonccpy(&tile_mem[1][2], tile_solid2, sizeof(tile_solid2));
|
|
81
|
+
REG_BG1CNT = BG_CBB(1) | BG_SBB(28) | BG_REG_32x32 | BG_4BPP | BG_PRIO(3);
|
|
82
|
+
{
|
|
83
|
+
SCR_ENTRY *map = se_mem[28];
|
|
84
|
+
int tx, ty;
|
|
85
|
+
for (ty = 0; ty < 32; ty++)
|
|
86
|
+
for (tx = 0; tx < 32; tx++)
|
|
87
|
+
map[ty * 32 + tx] = SE_BUILD(1 + ((tx ^ ty) & 1), 0, 0, 0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* TTE setup so we can show a status banner. Standard Tonc setup.
|
|
91
|
+
* TTE text on BG0 in front of the BG1 backdrop. */
|
|
59
92
|
tte_init_chr4c_default(0, BG_CBB(0) | BG_SBB(31));
|
|
60
|
-
|
|
93
|
+
REG_BG0CNT |= BG_PRIO(0); /* text in front of the backdrop */
|
|
94
|
+
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0 | DCNT_BG1;
|
|
61
95
|
|
|
62
96
|
tte_write("#{P:24,32}");
|
|
63
97
|
tte_write("Maxmod demo");
|
|
@@ -45,7 +45,9 @@ static const Rect platforms[] = {
|
|
|
45
45
|
{ 440, 120, 56, 8 },
|
|
46
46
|
{ 180, 52, 48, 8 },
|
|
47
47
|
};
|
|
48
|
-
|
|
48
|
+
/* (int) cast so `for (int i = 0; i < N_PLATFORMS; ...)` doesn't compare a
|
|
49
|
+
* signed counter against an unsigned size_t (-Wsign-compare). */
|
|
50
|
+
#define N_PLATFORMS ((int)(sizeof(platforms) / sizeof(platforms[0])))
|
|
49
51
|
|
|
50
52
|
static OBJ_ATTR obj_buffer[128];
|
|
51
53
|
|
|
@@ -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.
|
|
@@ -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
|
}
|
|
@@ -78,8 +78,9 @@ static uint8_t on_platform(int16_t px, int16_t py) {
|
|
|
78
78
|
|
|
79
79
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
80
80
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
81
|
-
|
|
82
|
-
|
|
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);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
static void paint_platforms(void) {
|
|
@@ -141,8 +141,9 @@ static void lock_piece(void) {
|
|
|
141
141
|
|
|
142
142
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
143
143
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
144
|
-
|
|
145
|
-
|
|
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);
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
/* Draw the well frame around the 6×12 play area. Grid cells live at
|
|
@@ -114,8 +114,9 @@ static void spawn_obstacle(void) {
|
|
|
114
114
|
|
|
115
115
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
116
116
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
118
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
119
|
+
memcpy_vram(dst, src, 16);
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
/* Paint the road into BG map 0 ($9800). 20×18 visible cells:
|
|
@@ -108,8 +108,9 @@ static void spawn(void) {
|
|
|
108
108
|
|
|
109
109
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
110
110
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
112
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
113
|
+
memcpy_vram(dst, src, 16);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
/* Paint a starfield into BG map 0 ($9800): fill the visible 20×18 with the
|
|
@@ -79,8 +79,9 @@ static void reset_match(void) {
|
|
|
79
79
|
|
|
80
80
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
81
81
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
83
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
84
|
+
memcpy_vram(dst, src, 16);
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
/* Paint the Pong court into BG map 0 ($9800): dithered turf everywhere,
|
|
@@ -143,8 +143,9 @@ static const uint8_t rooms[ROOMS][ROWS * COLS] = {
|
|
|
143
143
|
|
|
144
144
|
/* ── Helpers ────────────────────────────────────────────────────── */
|
|
145
145
|
static void copy_to_vram(uint8_t *dst, const uint8_t *src, uint16_t n) {
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
/* Delegate to the runtime's pointer-walk copy — an indexed dst[i]=src[i]
|
|
147
|
+
* loop into VRAM is miscompiled by SDCC sm83. */
|
|
148
|
+
memcpy_vram(dst, src, n);
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
static void load_bg_palette(void) {
|
package/examples/genesis/main.s
CHANGED
|
@@ -89,7 +89,7 @@ Start:
|
|
|
89
89
|
move.w #$0000,VDP_DATA ; color 0: transparent / backdrop
|
|
90
90
|
move.w #$0EEE,VDP_DATA ; color 1: white-ish ($0E in each nybble)
|
|
91
91
|
move.w #$00EE,VDP_DATA ; color 2: yellow
|
|
92
|
-
move.w #$
|
|
92
|
+
move.w #$0840,VDP_DATA ; color 3: dark teal (backdrop fill)
|
|
93
93
|
|
|
94
94
|
; -----------------------------------------------------------
|
|
95
95
|
; Upload one 4bpp tile (8x8 'H', color 2 = yellow) to VRAM at
|
|
@@ -104,6 +104,34 @@ Start:
|
|
|
104
104
|
move.l (a1)+,VDP_DATA ; each row = one longword (4 bytes)
|
|
105
105
|
dbra d0,.tile_loop
|
|
106
106
|
|
|
107
|
+
; -----------------------------------------------------------
|
|
108
|
+
; Upload two PATTERNED backdrop tiles (#2 and #3) to VRAM at
|
|
109
|
+
; byte offsets $40 and $60 (VRAM cmd $40400000). Each is a teal
|
|
110
|
+
; (color 3) field sprinkled with white (color 1) dots, with the
|
|
111
|
+
; roles swapped between the two so that when we checkerboard them
|
|
112
|
+
; across the plane no single colour dominates the screen — a
|
|
113
|
+
; uniform fill still reads as "blank" to a human, so we vary it.
|
|
114
|
+
; 16 longwords total (8 rows × 2 tiles) written back-to-back.
|
|
115
|
+
move.l #$40400000,VDP_CTRL
|
|
116
|
+
lea TileBgA(pc),a1
|
|
117
|
+
moveq #16-1,d0 ; 8 rows × 2 tiles
|
|
118
|
+
.bg_tile_loop:
|
|
119
|
+
move.l (a1)+,VDP_DATA
|
|
120
|
+
dbra d0,.bg_tile_loop
|
|
121
|
+
|
|
122
|
+
; -----------------------------------------------------------
|
|
123
|
+
; Fill the ENTIRE plane A name table, checkerboarding tiles #2
|
|
124
|
+
; and #3 so there's a varied visible background behind the 'H'.
|
|
125
|
+
; Plane A base is $C000; plane size is 64x32 = 2048 cells. VRAM
|
|
126
|
+
; write at $C000 = cmd $40000003. d1 toggles 2↔3 each cell.
|
|
127
|
+
move.l #$40000003,VDP_CTRL
|
|
128
|
+
move.w #2048-1,d0 ; 2048 cells to write
|
|
129
|
+
moveq #2,d1 ; start with tile #2
|
|
130
|
+
.bg_fill_loop:
|
|
131
|
+
move.w d1,VDP_DATA ; tile d1 (pal 0, no flip, low pri)
|
|
132
|
+
eor.w #$0001,d1 ; toggle 2↔3 (tile index xor 1)
|
|
133
|
+
dbra d0,.bg_fill_loop
|
|
134
|
+
|
|
107
135
|
; -----------------------------------------------------------
|
|
108
136
|
; Place tile #1 in plane A near screen center.
|
|
109
137
|
; Plane A base is at VRAM $C000 (default after VDP init).
|
|
@@ -159,3 +187,27 @@ TileH:
|
|
|
159
187
|
dc.l $20000020
|
|
160
188
|
dc.l $20000020
|
|
161
189
|
dc.l $00000000 ; row 7: blank
|
|
190
|
+
|
|
191
|
+
; -----------------------------------------------------------------------
|
|
192
|
+
; Two patterned backdrop tiles, 4bpp, uploaded back-to-back as #2 and #3.
|
|
193
|
+
; TileBgA = teal (color 3) field with white (color 1) dots; TileBgB swaps
|
|
194
|
+
; the roles (white field, teal dots). Checkerboarding them across the
|
|
195
|
+
; plane keeps any single colour well under the "blank" threshold.
|
|
196
|
+
TileBgA: ; tile #2 — teal field, white dots
|
|
197
|
+
dc.l $33333333
|
|
198
|
+
dc.l $33133313
|
|
199
|
+
dc.l $33333333
|
|
200
|
+
dc.l $13333331
|
|
201
|
+
dc.l $33333333
|
|
202
|
+
dc.l $33133313
|
|
203
|
+
dc.l $33333333
|
|
204
|
+
dc.l $13333331
|
|
205
|
+
TileBgB: ; tile #3 — white field, teal dots
|
|
206
|
+
dc.l $11111111
|
|
207
|
+
dc.l $11311131
|
|
208
|
+
dc.l $11111111
|
|
209
|
+
dc.l $31111113
|
|
210
|
+
dc.l $11111111
|
|
211
|
+
dc.l $11311131
|
|
212
|
+
dc.l $11111111
|
|
213
|
+
dc.l $31111113
|
|
@@ -29,6 +29,17 @@ static const u32 tile_data[8] = {
|
|
|
29
29
|
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
/* A checkered backdrop block tiled across plane B so the screen isn't a
|
|
33
|
+
* flat black void (a lone sprite on black reads as "blank" to a human).
|
|
34
|
+
* Colour index 4 with a thin colour-5 frame — we set both below. */
|
|
35
|
+
static const u32 tile_bg[8] = {
|
|
36
|
+
0x44444444, 0x45555554, 0x45000054, 0x45000054,
|
|
37
|
+
0x45000054, 0x45000054, 0x45555554, 0x44444444,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
#define T_SPRITE (TILE_USER_INDEX + 0)
|
|
41
|
+
#define T_BG (TILE_USER_INDEX + 1)
|
|
42
|
+
|
|
32
43
|
int main(bool hard) {
|
|
33
44
|
(void)hard;
|
|
34
45
|
|
|
@@ -38,11 +49,22 @@ int main(bool hard) {
|
|
|
38
49
|
|
|
39
50
|
/* Make sure sprite palette 0 entry 1 is white so we can see our
|
|
40
51
|
* tile. SGDK uses 0RRR0GGG0BBB packed words (BGR, 3 bits each). */
|
|
41
|
-
PAL_setColor(1, 0x0EEE); /* near-white */
|
|
52
|
+
PAL_setColor(1, 0x0EEE); /* near-white sprite */
|
|
53
|
+
/* Plane-B backdrop colours (palette 1). */
|
|
54
|
+
PAL_setColor(16 + 4, 0x0640); /* dark teal field */
|
|
55
|
+
PAL_setColor(16 + 5, 0x0860); /* lighter frame */
|
|
42
56
|
|
|
43
57
|
/* Upload the user tile to VRAM at TILE_USER_INDEX (everything
|
|
44
58
|
* below that is reserved for SGDK's font + system tiles). */
|
|
45
|
-
VDP_loadTileData(tile_data,
|
|
59
|
+
VDP_loadTileData(tile_data, T_SPRITE, 1, DMA);
|
|
60
|
+
VDP_loadTileData(tile_bg, T_BG, 1, DMA);
|
|
61
|
+
|
|
62
|
+
/* Tile plane B with the backdrop block so there's a visible
|
|
63
|
+
* background behind the sprite + text. Sprites + the font plane (A)
|
|
64
|
+
* always draw above plane B, so the d-pad sprite reads on top. */
|
|
65
|
+
for (u16 cy = 0; cy < 28; cy++)
|
|
66
|
+
for (u16 cx = 0; cx < 40; cx++)
|
|
67
|
+
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1, 0, 0, 0, T_BG), cx, cy);
|
|
46
68
|
|
|
47
69
|
VDP_drawText("D-PAD MOVES THE SPRITE", 8, 2);
|
|
48
70
|
VDP_drawText("START FOR SOFT RESET", 9, 4);
|
|
@@ -66,7 +88,7 @@ int main(bool hard) {
|
|
|
66
88
|
* SPRITE_SIZE(1,1) = 8×8. TILE_ATTR_FULL(palette,prio,vflip,
|
|
67
89
|
* hflip,tile_index). */
|
|
68
90
|
VDP_setSprite(0, px, py, SPRITE_SIZE(1, 1),
|
|
69
|
-
TILE_ATTR_FULL(PAL0,
|
|
91
|
+
TILE_ATTR_FULL(PAL0, 1, 0, 0, T_SPRITE));
|
|
70
92
|
VDP_updateSprites(1, DMA);
|
|
71
93
|
|
|
72
94
|
SYS_doVBlankProcess();
|
|
@@ -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);
|
|
@@ -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
|
|