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
|
@@ -43,6 +43,37 @@ extern const unsigned char music_data[];
|
|
|
43
43
|
|
|
44
44
|
static const unsigned char bg_colors[4] = { 0x0F, 0x01, 0x21, 0x31 };
|
|
45
45
|
|
|
46
|
+
/* Two BG tiles so the backdrop isn't a single flat colour (a uniform
|
|
47
|
+
* screen reads >=92% one colour and fails the blank-screen check):
|
|
48
|
+
* tile 1 — solid colour 1
|
|
49
|
+
* tile 2 — solid colour 2
|
|
50
|
+
* We checkerboard them across the nametable below. NES BG fetches from
|
|
51
|
+
* $1000-$1FFF under the default PPUCTRL (bit 4 set), so BG tiles upload
|
|
52
|
+
* there. */
|
|
53
|
+
static const unsigned char bg_tiles[2 * 16] = {
|
|
54
|
+
/* tile 1: solid colour 1 (plane 0 all set, plane 1 clear) */
|
|
55
|
+
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
56
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
57
|
+
/* tile 2: solid colour 2 (plane 1 all set, plane 0 clear) */
|
|
58
|
+
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
59
|
+
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/* Fill nametable 0 ($2000) with a checkerboard of tiles 1 and 2 so the
|
|
63
|
+
* screen shows two distinct colours. Attribute table left at palette 0.
|
|
64
|
+
* Caller must have the PPU off. */
|
|
65
|
+
static void fill_bg(void) {
|
|
66
|
+
unsigned int addr;
|
|
67
|
+
unsigned char y, x;
|
|
68
|
+
for (y = 0; y < 30; y++) {
|
|
69
|
+
addr = 0x2000 + (unsigned int)y * 32;
|
|
70
|
+
for (x = 0; x < 32; x++) {
|
|
71
|
+
vram_unsafe_set(addr, (unsigned char)(((x ^ y) & 1) + 1));
|
|
72
|
+
++addr;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
46
77
|
void main(void) {
|
|
47
78
|
unsigned char palette[32];
|
|
48
79
|
unsigned char i;
|
|
@@ -50,9 +81,18 @@ void main(void) {
|
|
|
50
81
|
unsigned char frame = 0;
|
|
51
82
|
|
|
52
83
|
for (i = 0; i < 32; i++) palette[i] = bg_colors[0];
|
|
84
|
+
/* BG palette 0: backdrop black, colour 1 blue, colour 2 red — gives the
|
|
85
|
+
* checkerboard two visibly different cells. */
|
|
86
|
+
palette[0] = 0x0F; /* $3F00 backdrop */
|
|
87
|
+
palette[1] = 0x11; /* colour 1 — blue */
|
|
88
|
+
palette[2] = 0x16; /* colour 2 — red */
|
|
53
89
|
|
|
54
90
|
ppu_off();
|
|
55
91
|
palette_load(palette);
|
|
92
|
+
/* Upload the two BG tiles to the BG pattern table at $1000. */
|
|
93
|
+
chr_ram_upload(0x1000, bg_tiles, sizeof(bg_tiles));
|
|
94
|
+
/* Paint the checkerboard backdrop so the screen is visibly NOT blank. */
|
|
95
|
+
fill_bg();
|
|
56
96
|
oam_clear();
|
|
57
97
|
|
|
58
98
|
/* Start the music BEFORE rendering — FamiToneInit takes a few
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
#include "nes_runtime.h"
|
|
30
30
|
|
|
31
|
-
/* ── Tiles: player idle/jump + platform block
|
|
31
|
+
/* ── Tiles: player idle/jump + platform block (SPRITES, table $0000) ── */
|
|
32
32
|
static const uint8_t tile_blank[16] = { 0 };
|
|
33
33
|
static const uint8_t tile_player_idle[16] = {
|
|
34
34
|
0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, /* plane 0 — round blob */
|
|
@@ -43,12 +43,46 @@ static const uint8_t tile_platform[16] = {
|
|
|
43
43
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
/* ── BG scenery tiles (BACKGROUND pattern table $1000) ────────────────
|
|
47
|
+
* Painted into the nametable so the world reads as a real outdoor scene on
|
|
48
|
+
* boot (sky + clouds + dirt) instead of sprites floating on flat black. The
|
|
49
|
+
* BG backdrop colour (palette[0]) is sky blue, so the colours used here are
|
|
50
|
+
* cloud white (idx1), dirt brown (idx2) and grass green (idx3).
|
|
51
|
+
*
|
|
52
|
+
* BG_CLOUD — a puffy cloud (idx1) dotted across the upper sky.
|
|
53
|
+
* BG_DIRT — solid dirt fill (idx2) for the ground band.
|
|
54
|
+
* BG_GRASS — a grass-topped dirt tile (idx3 cap over idx2) for the
|
|
55
|
+
* surface row of the ground. */
|
|
56
|
+
#define BG_CLOUD 1 /* BG tile index 1 → uploaded to $1010 */
|
|
57
|
+
#define BG_DIRT 2 /* BG tile index 2 → uploaded to $1020 */
|
|
58
|
+
#define BG_GRASS 3 /* BG tile index 3 → uploaded to $1030 */
|
|
59
|
+
static const uint8_t bg_tile_cloud[16] = {
|
|
60
|
+
0x00, 0x18, 0x3C, 0x7E, 0x7E, 0x00, 0x00, 0x00, /* plane 0 → cloud (idx1) */
|
|
61
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
62
|
+
};
|
|
63
|
+
static const uint8_t bg_tile_dirt[16] = {
|
|
64
|
+
0, 0, 0, 0, 0, 0, 0, 0, /* plane 0 clear */
|
|
65
|
+
0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, /* plane 1 → dirt (idx2) */
|
|
66
|
+
};
|
|
67
|
+
static const uint8_t bg_tile_grass[16] = {
|
|
68
|
+
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* plane0: top 2 rows on */
|
|
69
|
+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* plane1: all rows on */
|
|
70
|
+
}; /* rows 0-1 → both planes = idx3 (grass cap); rows 2-7 → plane1 = idx2 (dirt) */
|
|
71
|
+
|
|
46
72
|
static const uint8_t palette[32] = {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
73
|
+
/* BG0: sky-blue backdrop, cloud white (idx1), dirt brown (idx2),
|
|
74
|
+
* grass green (idx3) */
|
|
75
|
+
0x21, 0x30, 0x17, 0x2A,
|
|
76
|
+
0x21, 0x30, 0x17, 0x2A,
|
|
77
|
+
0x21, 0x30, 0x17, 0x2A,
|
|
78
|
+
0x21, 0x30, 0x17, 0x2A,
|
|
79
|
+
/* The universal backdrop ($3F00) is MIRRORED at $3F10 — the sprite
|
|
80
|
+
* palette-0 colour-0 slot. palette_load writes all 32 bytes in order, so
|
|
81
|
+
* byte 16 (this slot) is the LAST write to that mirror and therefore wins.
|
|
82
|
+
* Keep it equal to the BG backdrop (sky blue) or the sky renders as
|
|
83
|
+
* whatever colour-0 you put here, not the BG[0] above. (Sprite colour 0 is
|
|
84
|
+
* transparent regardless, so this never affects how sprites draw.) */
|
|
85
|
+
0x21, 0x21, 0x32, 0x30, /* sp0: player — blue (colour 0 = backdrop mirror) */
|
|
52
86
|
0x0F, 0x18, 0x28, 0x38, /* sp1: platforms — green */
|
|
53
87
|
0x0F, 0x16, 0x06, 0x36,
|
|
54
88
|
0x0F, 0x2A, 0x1A, 0x0A,
|
|
@@ -102,8 +136,33 @@ void main(void) {
|
|
|
102
136
|
chr_ram_upload(0x0020, tile_player_jump, 16);
|
|
103
137
|
chr_ram_upload(0x0030, tile_platform, 16);
|
|
104
138
|
|
|
139
|
+
/* BG scenery tiles go in pattern table 1 ($1000) — where the default
|
|
140
|
+
* PPUCTRL points the background fetch. */
|
|
141
|
+
chr_ram_upload(0x1010, bg_tile_cloud, 16);
|
|
142
|
+
chr_ram_upload(0x1020, bg_tile_dirt, 16);
|
|
143
|
+
chr_ram_upload(0x1030, bg_tile_grass, 16);
|
|
144
|
+
|
|
105
145
|
palette_load(palette);
|
|
106
146
|
|
|
147
|
+
/* Paint the outdoor scene into the nametable while rendering is off
|
|
148
|
+
* (vram_unsafe_set = raw write; tile_set's NMI queue would deadlock before
|
|
149
|
+
* ppu_on). Sky-blue backdrop shows through the empty upper rows; we dot
|
|
150
|
+
* clouds across it, cap the ground with a grass row, and fill the bottom
|
|
151
|
+
* band with solid dirt. The sprite platforms still draw on top of this. */
|
|
152
|
+
{
|
|
153
|
+
uint16_t r, c;
|
|
154
|
+
/* clouds scattered through the upper sky (rows 2..9) */
|
|
155
|
+
for (r = 2; r < 10; r++)
|
|
156
|
+
for (c = (uint16_t)((r * 3) % 6); c < 32; c += 6)
|
|
157
|
+
vram_unsafe_set((uint16_t)(0x2000 + r * 32 + c), BG_CLOUD);
|
|
158
|
+
/* grass cap row + solid dirt to the bottom of the screen */
|
|
159
|
+
for (c = 0; c < 32; c++)
|
|
160
|
+
vram_unsafe_set((uint16_t)(0x2000 + 24 * 32 + c), BG_GRASS);
|
|
161
|
+
for (r = 25; r < 30; r++)
|
|
162
|
+
for (c = 0; c < 32; c++)
|
|
163
|
+
vram_unsafe_set((uint16_t)(0x2000 + r * 32 + c), BG_DIRT);
|
|
164
|
+
}
|
|
165
|
+
|
|
107
166
|
oam_clear();
|
|
108
167
|
ppu_on_all();
|
|
109
168
|
sound_init();
|
|
@@ -53,12 +53,39 @@ static const uint8_t tile_block_b[16] = {
|
|
|
53
53
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
54
54
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
55
55
|
};
|
|
56
|
+
/* BG scenery tiles — painted into the nametable so the playfield reads as a
|
|
57
|
+
* real machine on boot (without them the screen is just sprites on flat
|
|
58
|
+
* black). All live in the BACKGROUND pattern table ($1000); the block tiles
|
|
59
|
+
* above are sprites ($0000).
|
|
60
|
+
*
|
|
61
|
+
* BG_WALL — solid bordered block (idx 1, steel blue): the well frame.
|
|
62
|
+
* BG_BRICK — a brick/dither pattern (idx 2) tiling the cabinet wall that
|
|
63
|
+
* surrounds the well, so the whole screen is covered.
|
|
64
|
+
* BG_INNER — a faint grid speck (idx 3) lining the inside of the well so
|
|
65
|
+
* empty cells read as a recessed playfield, not a black hole. */
|
|
66
|
+
static const uint8_t tile_wall[16] = {
|
|
67
|
+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
68
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
69
|
+
};
|
|
70
|
+
static const uint8_t tile_brick[16] = {
|
|
71
|
+
0, 0, 0, 0, 0, 0, 0, 0, /* plane 0 clear */
|
|
72
|
+
0xFF, 0x80, 0x80, 0x80, 0xFF, 0x08, 0x08, 0x08, /* plane 1 → colour 2 brick */
|
|
73
|
+
};
|
|
74
|
+
static const uint8_t tile_inner[16] = {
|
|
75
|
+
0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, /* plane 0 specks */
|
|
76
|
+
0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, /* plane 1 too → colour 3 */
|
|
77
|
+
};
|
|
78
|
+
#define BG_WALL 1 /* BG tile index 1 → uploaded to $1010 */
|
|
79
|
+
#define BG_BRICK 2 /* BG tile index 2 → uploaded to $1020 */
|
|
80
|
+
#define BG_INNER 3 /* BG tile index 3 → uploaded to $1030 */
|
|
56
81
|
|
|
57
82
|
static const uint8_t palette[32] = {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
0x0F,
|
|
61
|
-
0x0F,
|
|
83
|
+
/* BG0: backdrop near-black, wall = steel blue (idx1), brick = brown
|
|
84
|
+
* (idx2), inner grid = dark grey (idx3). */
|
|
85
|
+
0x0F, 0x11, 0x17, 0x00,
|
|
86
|
+
0x0F, 0x11, 0x17, 0x00,
|
|
87
|
+
0x0F, 0x11, 0x17, 0x00,
|
|
88
|
+
0x0F, 0x11, 0x17, 0x00,
|
|
62
89
|
/* Sprite palette 0: idx1=red, idx2=green, idx3=blue */
|
|
63
90
|
0x0F, 0x16, 0x1A, 0x12,
|
|
64
91
|
0x0F, 0x16, 0x1A, 0x12,
|
|
@@ -156,11 +183,45 @@ void main(void) {
|
|
|
156
183
|
int8_t i;
|
|
157
184
|
|
|
158
185
|
ppu_off();
|
|
159
|
-
chr_ram_upload(0x0000, tile_blank, 16);
|
|
160
|
-
chr_ram_upload(0x0010, tile_block_r, 16);
|
|
186
|
+
chr_ram_upload(0x0000, tile_blank, 16); /* sprite slot 0 */
|
|
187
|
+
chr_ram_upload(0x0010, tile_block_r, 16); /* sprite slots 1..3 */
|
|
161
188
|
chr_ram_upload(0x0020, tile_block_g, 16);
|
|
162
189
|
chr_ram_upload(0x0030, tile_block_b, 16);
|
|
190
|
+
chr_ram_upload(0x1010, tile_wall, 16); /* BG slot 1 (background table) */
|
|
191
|
+
chr_ram_upload(0x1020, tile_brick, 16); /* BG slot 2 (cabinet wall) */
|
|
192
|
+
chr_ram_upload(0x1030, tile_inner, 16); /* BG slot 3 (well interior) */
|
|
163
193
|
palette_load(palette);
|
|
194
|
+
|
|
195
|
+
/* Draw the cabinet + well into the nametable while rendering is off
|
|
196
|
+
* (vram_unsafe_set = raw PPU write; the friendly tile_set queue would
|
|
197
|
+
* deadlock before ppu_on). The grid is 6 wide × 12 tall at pixel origin
|
|
198
|
+
* (80,16) → tile cols 10..15, rows 2..13; frame it one cell out. We first
|
|
199
|
+
* tile the WHOLE screen with brick (the machine cabinet), then carve the
|
|
200
|
+
* recessed well interior, then stamp the steel-blue frame on top — so the
|
|
201
|
+
* screen is fully covered instead of sprites floating on black. */
|
|
202
|
+
{
|
|
203
|
+
uint16_t gx0 = ORIGIN_X / 8, gy0 = ORIGIN_Y / 8; /* 10, 2 */
|
|
204
|
+
uint16_t gx1 = gx0 + GRID_W, gy1 = gy0 + GRID_H; /* 16, 14 (exclusive) */
|
|
205
|
+
uint16_t cc, rr;
|
|
206
|
+
/* whole-screen cabinet brick */
|
|
207
|
+
for (rr = 0; rr < 30; rr++)
|
|
208
|
+
for (cc = 0; cc < 32; cc++)
|
|
209
|
+
vram_unsafe_set((uint16_t)(0x2000 + rr * 32 + cc), BG_BRICK);
|
|
210
|
+
/* recessed well interior (inside the frame) */
|
|
211
|
+
for (rr = gy0; rr < gy1; rr++)
|
|
212
|
+
for (cc = gx0; cc < gx1; cc++)
|
|
213
|
+
vram_unsafe_set((uint16_t)(0x2000 + rr * 32 + cc), BG_INNER);
|
|
214
|
+
/* steel frame one cell out around the well */
|
|
215
|
+
for (cc = gx0 - 1; cc <= gx1; cc++) {
|
|
216
|
+
vram_unsafe_set((uint16_t)(0x2000 + (gy0 - 1) * 32 + cc), BG_WALL); /* top */
|
|
217
|
+
vram_unsafe_set((uint16_t)(0x2000 + gy1 * 32 + cc), BG_WALL); /* bottom */
|
|
218
|
+
}
|
|
219
|
+
for (rr = gy0 - 1; rr <= gy1; rr++) {
|
|
220
|
+
vram_unsafe_set((uint16_t)(0x2000 + rr * 32 + (gx0 - 1)), BG_WALL); /* left */
|
|
221
|
+
vram_unsafe_set((uint16_t)(0x2000 + rr * 32 + gx1), BG_WALL); /* right */
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
164
225
|
oam_clear();
|
|
165
226
|
ppu_on_all();
|
|
166
227
|
sound_init();
|
|
@@ -59,14 +59,22 @@ static const uint8_t tile_digits[10 * 16] = {
|
|
|
59
59
|
/* ── Background road tiles ───────────────────────────────────────────
|
|
60
60
|
* Default PPUCTRL ($90) reads BG patterns from pattern table 1 ($1000),
|
|
61
61
|
* so these go to CHR $1000+ and are indexed independently of the sprite
|
|
62
|
-
* tiles above.
|
|
63
|
-
*
|
|
62
|
+
* tiles above. The grey backdrop (colour 0) is the road surface; colour 1
|
|
63
|
+
* (white) draws the markings, colour 2 (green) the grass, colour 3 the
|
|
64
|
+
* dark seam in the tarmac.
|
|
64
65
|
*
|
|
65
|
-
* BG_T_EDGE:
|
|
66
|
-
* BG_T_LANE:
|
|
67
|
-
*
|
|
66
|
+
* BG_T_EDGE: a solid 2px vertical stripe — the road shoulder line.
|
|
67
|
+
* BG_T_LANE: a 2px vertical dash (on 4 rows / off 4) — the dashed centre
|
|
68
|
+
* lane marking when stacked down a column.
|
|
69
|
+
* BG_T_GRASS: a textured green roadside (colour 2 hatch) so the area
|
|
70
|
+
* outside the shoulders isn't flat — fills the screen sides.
|
|
71
|
+
* BG_T_ROAD: a faint tarmac texture (a couple of colour-3 specks) tiled
|
|
72
|
+
* across the driving surface so the road doesn't read as one
|
|
73
|
+
* solid grey block. */
|
|
68
74
|
#define BG_T_EDGE 1
|
|
69
75
|
#define BG_T_LANE 2
|
|
76
|
+
#define BG_T_GRASS 3
|
|
77
|
+
#define BG_T_ROAD 4
|
|
70
78
|
static const uint8_t bg_tile_edge[16] = {
|
|
71
79
|
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* plane 0 (colour bit 0) */
|
|
72
80
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
@@ -75,13 +83,21 @@ static const uint8_t bg_tile_lane[16] = {
|
|
|
75
83
|
0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, /* dash: 4 on, 4 off */
|
|
76
84
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
77
85
|
};
|
|
86
|
+
static const uint8_t bg_tile_grass[16] = {
|
|
87
|
+
0, 0, 0, 0, 0, 0, 0, 0, /* plane 0 clear */
|
|
88
|
+
0xEE, 0xBB, 0xEE, 0xBB, 0xEE, 0xBB, 0xEE, 0xBB, /* plane 1 → colour 2 hatch */
|
|
89
|
+
};
|
|
90
|
+
static const uint8_t bg_tile_road[16] = {
|
|
91
|
+
0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, /* plane 0 specks */
|
|
92
|
+
0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, /* plane 1 too → colour 3 */
|
|
93
|
+
};
|
|
78
94
|
|
|
79
95
|
static const uint8_t palette[32] = {
|
|
80
|
-
/* BG palettes — light grey backdrop simulates road */
|
|
81
|
-
0x10, 0x30,
|
|
82
|
-
0x10, 0x30,
|
|
83
|
-
0x10, 0x30,
|
|
84
|
-
0x10, 0x30,
|
|
96
|
+
/* BG palettes — light grey backdrop simulates road; idx2 = grass green */
|
|
97
|
+
0x10, 0x30, 0x1A, 0x00,
|
|
98
|
+
0x10, 0x30, 0x1A, 0x00,
|
|
99
|
+
0x10, 0x30, 0x1A, 0x00,
|
|
100
|
+
0x10, 0x30, 0x1A, 0x00,
|
|
85
101
|
/* Sprite palettes */
|
|
86
102
|
0x10, 0x21, 0x16, 0x30, /* sp0: blue car (P1) */
|
|
87
103
|
0x10, 0x16, 0x21, 0x30, /* sp1: red enemy */
|
|
@@ -125,8 +141,22 @@ static uint8_t aabb(Car *a, Car *b) {
|
|
|
125
141
|
#define ROAD_DIV_1 13
|
|
126
142
|
#define ROAD_DIV_2 17
|
|
127
143
|
static void draw_road(void) {
|
|
128
|
-
uint8_t row;
|
|
144
|
+
uint8_t row, col;
|
|
129
145
|
uint16_t base;
|
|
146
|
+
/* Fill the WHOLE nametable so nothing reads as flat colour: grass on the
|
|
147
|
+
* roadside (outside the shoulders) and a faint tarmac texture on the
|
|
148
|
+
* driving surface. Then stamp the shoulder + lane markings on top. */
|
|
149
|
+
for (row = 0; row < 30; row++) {
|
|
150
|
+
base = (uint16_t)(0x2000 + (uint16_t)row * 32);
|
|
151
|
+
for (col = 0; col < 32; col++) {
|
|
152
|
+
if (col < ROAD_EDGE_L || col > ROAD_EDGE_R) {
|
|
153
|
+
vram_unsafe_set((uint16_t)(base + col), BG_T_GRASS); /* roadside */
|
|
154
|
+
} else if (((row + col) & 3) == 0) {
|
|
155
|
+
vram_unsafe_set((uint16_t)(base + col), BG_T_ROAD); /* tarmac speck */
|
|
156
|
+
}
|
|
157
|
+
/* else leave colour-0 grey road surface */
|
|
158
|
+
}
|
|
159
|
+
}
|
|
130
160
|
for (row = ROAD_TOP_ROW; row <= ROAD_BOT_ROW; row++) {
|
|
131
161
|
base = (uint16_t)(0x2000 + (uint16_t)row * 32);
|
|
132
162
|
vram_unsafe_set((uint16_t)(base + ROAD_EDGE_L), BG_T_EDGE);
|
|
@@ -183,8 +213,10 @@ void main(void) {
|
|
|
183
213
|
|
|
184
214
|
/* BG road tiles live in pattern table 1 ($1000) — that's where the
|
|
185
215
|
* default PPUCTRL ($90) tells the PPU to read background patterns. */
|
|
186
|
-
chr_ram_upload((uint16_t)(0x1000 + BG_T_EDGE
|
|
187
|
-
chr_ram_upload((uint16_t)(0x1000 + BG_T_LANE
|
|
216
|
+
chr_ram_upload((uint16_t)(0x1000 + BG_T_EDGE * 16), bg_tile_edge, 16);
|
|
217
|
+
chr_ram_upload((uint16_t)(0x1000 + BG_T_LANE * 16), bg_tile_lane, 16);
|
|
218
|
+
chr_ram_upload((uint16_t)(0x1000 + BG_T_GRASS * 16), bg_tile_grass, 16);
|
|
219
|
+
chr_ram_upload((uint16_t)(0x1000 + BG_T_ROAD * 16), bg_tile_road, 16);
|
|
188
220
|
|
|
189
221
|
palette_load(palette);
|
|
190
222
|
draw_road(); /* paint the static road while the PPU is off */
|
|
@@ -38,10 +38,34 @@ static const uint8_t tile_enemy[16] = {
|
|
|
38
38
|
0x81, 0x42, 0x24, 0xFF, 0xFF, 0x24, 0x42, 0x81, /* plane 0 — spider-ish */
|
|
39
39
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
40
40
|
};
|
|
41
|
+
/* BG starfield tiles — painted across the whole nametable so the screen
|
|
42
|
+
* reads as a real "space" scene on boot instead of flat black. They live in
|
|
43
|
+
* the BACKGROUND pattern table ($1000), separate from the sprite tiles above
|
|
44
|
+
* (the runtime puts BG at $1000, sprites at $0000).
|
|
45
|
+
*
|
|
46
|
+
* BG_DUST — a faint checkerboard "space dust" that tiles seamlessly; the
|
|
47
|
+
* base layer that covers the whole field so it never reads blank.
|
|
48
|
+
* BG_STAR — three small stars (colour 1) sprinkled over the dust.
|
|
49
|
+
* BG_BRITE — a single bright + star (colour 2) for the rare close star. */
|
|
50
|
+
static const uint8_t tile_dust[16] = {
|
|
51
|
+
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, /* plane 0: checker (idx 1) */
|
|
52
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
53
|
+
};
|
|
54
|
+
static const uint8_t tile_star[16] = {
|
|
55
|
+
0x00, 0x08, 0x00, 0x42, 0x00, 0x00, 0x20, 0x01, /* plane 0: four small stars */
|
|
56
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
57
|
+
};
|
|
58
|
+
static const uint8_t tile_brite[16] = {
|
|
59
|
+
0x00, 0x00, 0x10, 0x38, 0x10, 0x00, 0x00, 0x00, /* plane 0: + arms (idx ... ) */
|
|
60
|
+
0x00, 0x00, 0x10, 0x38, 0x10, 0x00, 0x00, 0x00, /* plane 1 set too → colour 3 */
|
|
61
|
+
};
|
|
62
|
+
#define BG_DUST 1 /* BG tile index 1 → uploaded to $1010 */
|
|
63
|
+
#define BG_STAR 2 /* BG tile index 2 → uploaded to $1020 */
|
|
64
|
+
#define BG_BRITE 3 /* BG tile index 3 → uploaded to $1030 */
|
|
41
65
|
|
|
42
66
|
/* ── Palette ─────────────────────────────────────────────────────── */
|
|
43
67
|
static const uint8_t palette[32] = {
|
|
44
|
-
/*
|
|
68
|
+
/* BG0: backdrop near-black, star colour = dim white */
|
|
45
69
|
0x0F, 0x10, 0x20, 0x30,
|
|
46
70
|
0x0F, 0x10, 0x20, 0x30,
|
|
47
71
|
0x0F, 0x10, 0x20, 0x30,
|
|
@@ -126,14 +150,39 @@ void main(void) {
|
|
|
126
150
|
|
|
127
151
|
ppu_off();
|
|
128
152
|
|
|
129
|
-
/* Upload tile data — blank in slot 0 + 3 sprite tiles in slots 1..3
|
|
153
|
+
/* Upload tile data — blank in slot 0 + 3 sprite tiles in slots 1..3,
|
|
154
|
+
* all in the SPRITE pattern table ($0000). */
|
|
130
155
|
chr_ram_upload(0x0000, tile_blank, 16);
|
|
131
156
|
chr_ram_upload(0x0010, tile_ship, 16);
|
|
132
157
|
chr_ram_upload(0x0020, tile_bullet, 16);
|
|
133
158
|
chr_ram_upload(0x0030, tile_enemy, 16);
|
|
159
|
+
/* Upload the starfield tiles to the BACKGROUND pattern table
|
|
160
|
+
* ($1010/$1020/$1030 = BG slots 1/2/3). */
|
|
161
|
+
chr_ram_upload(0x1010, tile_dust, 16);
|
|
162
|
+
chr_ram_upload(0x1020, tile_star, 16);
|
|
163
|
+
chr_ram_upload(0x1030, tile_brite, 16);
|
|
134
164
|
|
|
135
165
|
palette_load(palette);
|
|
136
166
|
|
|
167
|
+
/* Paint a full starfield directly into the nametable while the PPU is off
|
|
168
|
+
* (vram_unsafe_set = raw write; tile_set's NMI queue would deadlock
|
|
169
|
+
* before ppu_on). Every one of the 32×30 cells gets the faint "dust" base,
|
|
170
|
+
* with small stars sprinkled every few cells and the odd bright star — a
|
|
171
|
+
* deterministic scatter so the backdrop is unambiguously "space", densely
|
|
172
|
+
* filled rather than flat black. */
|
|
173
|
+
{
|
|
174
|
+
uint16_t r, cc;
|
|
175
|
+
uint8_t tile;
|
|
176
|
+
for (r = 0; r < 30; r++) {
|
|
177
|
+
for (cc = 0; cc < 32; cc++) {
|
|
178
|
+
tile = BG_DUST; /* base dust everywhere */
|
|
179
|
+
if (((r * 5 + cc * 3) % 7) == 0) tile = BG_STAR; /* sprinkle stars */
|
|
180
|
+
if (((r * 3 + cc * 7) % 23) == 0) tile = BG_BRITE; /* rare bright one */
|
|
181
|
+
vram_unsafe_set((uint16_t)(0x2000 + r * 32 + cc), tile);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
137
186
|
oam_clear();
|
|
138
187
|
ppu_on_all();
|
|
139
188
|
sound_init();
|
|
@@ -34,6 +34,27 @@ static const uint8_t tile_ball[16] = {
|
|
|
34
34
|
0x00, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x3C, 0x00,
|
|
35
35
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
36
36
|
};
|
|
37
|
+
/* BG court tiles (background pattern table $1000), so the court reads as a
|
|
38
|
+
* real Pong arena on boot instead of sprites on flat black:
|
|
39
|
+
* BG_WALL — solid bar (idx1 white): the top/bottom rails.
|
|
40
|
+
* BG_NET — dashed vertical bar (idx1 white): the centre net.
|
|
41
|
+
* BG_FLOOR — a faint court-floor hatch (idx2 green) tiled across the whole
|
|
42
|
+
* playfield so the arena surface is covered, not black. */
|
|
43
|
+
static const uint8_t tile_wall[16] = {
|
|
44
|
+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
45
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
46
|
+
};
|
|
47
|
+
static const uint8_t tile_net[16] = {
|
|
48
|
+
0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, /* dashed vertical bar */
|
|
49
|
+
0, 0, 0, 0, 0, 0, 0, 0,
|
|
50
|
+
};
|
|
51
|
+
static const uint8_t tile_floor[16] = {
|
|
52
|
+
0, 0, 0, 0, 0, 0, 0, 0, /* plane 0 clear */
|
|
53
|
+
0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, /* plane 1 → colour 2 checker */
|
|
54
|
+
};
|
|
55
|
+
#define BG_WALL 1 /* BG slot 1 → $1010 */
|
|
56
|
+
#define BG_NET 2 /* BG slot 2 → $1020 */
|
|
57
|
+
#define BG_FLOOR 3 /* BG slot 3 → $1030 */
|
|
37
58
|
/* Digits 0-9 (3 wide × 5 tall, padded to 8×8). Used for the score HUD. */
|
|
38
59
|
static const uint8_t tile_digits[10 * 16] = {
|
|
39
60
|
/* 0 */ 0xE0,0xA0,0xA0,0xA0,0xE0,0x00,0x00,0x00, 0,0,0,0,0,0,0,0,
|
|
@@ -55,11 +76,12 @@ static const uint8_t tile_digits[10 * 16] = {
|
|
|
55
76
|
#define T_DIGIT0 3 /* digits live at slots 3..12 */
|
|
56
77
|
|
|
57
78
|
static const uint8_t palette[32] = {
|
|
58
|
-
/*
|
|
59
|
-
|
|
60
|
-
0x0F, 0x30,
|
|
61
|
-
0x0F, 0x30,
|
|
62
|
-
0x0F, 0x30,
|
|
79
|
+
/* BG0: backdrop near-black, court walls/net = white (idx1),
|
|
80
|
+
* court-floor hatch = dark green (idx2) */
|
|
81
|
+
0x0F, 0x30, 0x1A, 0x00,
|
|
82
|
+
0x0F, 0x30, 0x1A, 0x00,
|
|
83
|
+
0x0F, 0x30, 0x1A, 0x00,
|
|
84
|
+
0x0F, 0x30, 0x1A, 0x00,
|
|
63
85
|
/* Sprite palettes */
|
|
64
86
|
0x0F, 0x30, 0x16, 0x12, /* sp0: white paddle */
|
|
65
87
|
0x0F, 0x30, 0x16, 0x12, /* sp1: white ball */
|
|
@@ -103,13 +125,36 @@ void main(void) {
|
|
|
103
125
|
|
|
104
126
|
ppu_off();
|
|
105
127
|
|
|
106
|
-
/* Upload tiles. */
|
|
128
|
+
/* Upload sprite tiles (sprite pattern table $0000). */
|
|
107
129
|
chr_ram_upload(T_BLANK * 16, tile_blank, 16);
|
|
108
130
|
chr_ram_upload(T_PADDLE * 16, tile_paddle, 16);
|
|
109
131
|
chr_ram_upload(T_BALL * 16, tile_ball, 16);
|
|
110
132
|
chr_ram_upload(T_DIGIT0 * 16, tile_digits, sizeof(tile_digits));
|
|
133
|
+
/* Upload court tiles to the BACKGROUND pattern table ($1010..$1030). */
|
|
134
|
+
chr_ram_upload(0x1010, tile_wall, 16);
|
|
135
|
+
chr_ram_upload(0x1020, tile_net, 16);
|
|
136
|
+
chr_ram_upload(0x1030, tile_floor, 16);
|
|
111
137
|
|
|
112
138
|
palette_load(palette);
|
|
139
|
+
|
|
140
|
+
/* Paint the court into the nametable while rendering is off
|
|
141
|
+
* (vram_unsafe_set = raw write). First carpet the whole playfield with the
|
|
142
|
+
* green floor hatch so the arena surface is covered, then lay the top/
|
|
143
|
+
* bottom rails (rows 1 and 27) and the dashed centre net (column 15) on
|
|
144
|
+
* top. Without the floor the screen is just sprites on flat black. */
|
|
145
|
+
{
|
|
146
|
+
uint16_t cc, rr;
|
|
147
|
+
for (rr = 0; rr < 30; rr++)
|
|
148
|
+
for (cc = 0; cc < 32; cc++)
|
|
149
|
+
vram_unsafe_set((uint16_t)(0x2000 + rr * 32 + cc), BG_FLOOR);
|
|
150
|
+
for (cc = 0; cc < 32; cc++) {
|
|
151
|
+
vram_unsafe_set((uint16_t)(0x2000 + 1 * 32 + cc), BG_WALL); /* top rail (y≈8) */
|
|
152
|
+
vram_unsafe_set((uint16_t)(0x2000 + 27 * 32 + cc), BG_WALL); /* bottom rail(y≈216)*/
|
|
153
|
+
}
|
|
154
|
+
for (rr = 2; rr < 27; rr++)
|
|
155
|
+
vram_unsafe_set((uint16_t)(0x2000 + rr * 32 + 15), BG_NET); /* centre net */
|
|
156
|
+
}
|
|
157
|
+
|
|
113
158
|
oam_clear();
|
|
114
159
|
ppu_on_all();
|
|
115
160
|
sound_init();
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
#define BAT_VRAM 0x0000 /* 32x32 background map */
|
|
28
28
|
#define FONT_VRAM 0x1000 /* digit + glyph tiles (8x8, 16 words each) */
|
|
29
29
|
#define BLANK_VRAM 0x1000 /* tile 0 of the font = blank */
|
|
30
|
+
#define FIELD_VRAM 0x1700 /* dim "field" BG tile (16 words), drawn behind
|
|
31
|
+
* the HUD so the playfield isn't a blank void */
|
|
30
32
|
#define CATCHER_VRAM 0x1800 /* catcher sprite cell (16x16 = 64 words) */
|
|
31
33
|
#define FRUIT_VRAM 0x1840 /* fruit sprite cell (16x16 = 64 words) */
|
|
32
34
|
|
|
@@ -146,15 +148,30 @@ static void upload_sprites(void) {
|
|
|
146
148
|
load_tiles(FRUIT_VRAM, fruit_cell, 64);
|
|
147
149
|
}
|
|
148
150
|
|
|
149
|
-
/* ----
|
|
151
|
+
/* ---- build the dim "field" BG tile -------------------------------------- */
|
|
152
|
+
/* A solid 8x8 tile in BG colour index 2 (the dim field blue). Filling the BAT
|
|
153
|
+
* with this instead of blank gives the playfield a visible background — an
|
|
154
|
+
* all-blank BAT reads as a near-empty backdrop (one colour > 92% of the screen,
|
|
155
|
+
* which looks blank to a human). plane1 set = colour index 2. */
|
|
156
|
+
static void upload_field(void) {
|
|
157
|
+
u16 i;
|
|
158
|
+
for (i = 0; i < 16; ++i) font_cell[i] = 0;
|
|
159
|
+
for (i = 0; i < 8; ++i) font_cell[i] = 0xFF00; /* plane1 (hi byte) = ci 2 */
|
|
160
|
+
load_tiles(FIELD_VRAM, font_cell, 16);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* ---- fill the whole BAT with the field tile (a 2x2-cell checkerboard of
|
|
164
|
+
* field/blank), behind the HUD glyphs which are written on top afterwards ---- */
|
|
150
165
|
static void clear_bat(void) {
|
|
151
166
|
u16 r, c;
|
|
167
|
+
u16 field = BAT_ENTRY(0, FIELD_VRAM);
|
|
152
168
|
u16 blank = BAT_ENTRY(0, BLANK_VRAM);
|
|
153
169
|
for (r = 0; r < 32; ++r) {
|
|
154
170
|
vram_set_write_addr((u16)(BAT_VRAM + r * 32));
|
|
155
171
|
for (c = 0; c < 32; ++c) {
|
|
156
|
-
|
|
157
|
-
|
|
172
|
+
u16 e = (((r >> 1) ^ (c >> 1)) & 1) ? blank : field;
|
|
173
|
+
VDC_DATA_LO = (u8)(e & 0xFF);
|
|
174
|
+
VDC_DATA_HI = (u8)(e >> 8);
|
|
158
175
|
}
|
|
159
176
|
}
|
|
160
177
|
}
|
|
@@ -229,6 +246,7 @@ void main(void) {
|
|
|
229
246
|
/* palette: backdrop, BG text colours, sprite colours */
|
|
230
247
|
vce_set_color(0, PCE_RGB(0, 0, 1)); /* backdrop: near-black blue */
|
|
231
248
|
vce_set_color(1, PCE_RGB(7, 7, 7)); /* BG colour 1: white text */
|
|
249
|
+
vce_set_color(2, PCE_RGB(1, 1, 3)); /* BG colour 2: dim field blue */
|
|
232
250
|
vce_set_color(256, PCE_RGB(0, 0, 0)); /* sprite transparent */
|
|
233
251
|
vce_set_color(257, PCE_RGB(2, 5, 7)); /* sprite c1: cyan (catcher 0) */
|
|
234
252
|
vce_set_color(259, PCE_RGB(2, 5, 7)); /* sprite c3: cyan (catcher) */
|
|
@@ -238,6 +256,7 @@ void main(void) {
|
|
|
238
256
|
|
|
239
257
|
upload_font();
|
|
240
258
|
upload_sprites();
|
|
259
|
+
upload_field(); /* dim field tile for a visible playfield BG */
|
|
241
260
|
|
|
242
261
|
clear_bat();
|
|
243
262
|
draw_hud_labels();
|
|
@@ -76,6 +76,32 @@ static void draw_bar(u8 active) {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/* Paint a decorative full-screen frame so the demo reads as a real UI panel
|
|
80
|
+
* instead of a near-empty backdrop. conio only inits a backdrop + font, so an
|
|
81
|
+
* otherwise text-only screen leaves >92% of pixels one colour, which looks
|
|
82
|
+
* blank to a human. We fill the top/bottom title bands and both side borders
|
|
83
|
+
* with a block character (the labels are drawn ON TOP afterwards). PCE conio is
|
|
84
|
+
* 32 cols x 28 rows. */
|
|
85
|
+
#define SCR_COLS 32
|
|
86
|
+
#define SCR_ROWS 28
|
|
87
|
+
static void draw_frame(void) {
|
|
88
|
+
u8 x, y;
|
|
89
|
+
/* solid top band (rows 0-1) and bottom band (rows 26-27) */
|
|
90
|
+
for (y = 0; y < SCR_ROWS; ++y) {
|
|
91
|
+
if (y < 2 || y >= SCR_ROWS - 2) {
|
|
92
|
+
for (x = 0; x < SCR_COLS; ++x) cputcxy(x, y, '#');
|
|
93
|
+
} else {
|
|
94
|
+
/* left + right vertical borders (two columns each for weight) */
|
|
95
|
+
cputcxy(0, y, '#');
|
|
96
|
+
cputcxy(1, y, '#');
|
|
97
|
+
cputcxy((u8)(SCR_COLS - 2), y, '#');
|
|
98
|
+
cputcxy((u8)(SCR_COLS - 1), y, '#');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/* a mid separator bar under the title so the panel has visible structure */
|
|
102
|
+
for (x = 2; x < SCR_COLS - 2; ++x) cputcxy(x, 5, '=');
|
|
103
|
+
}
|
|
104
|
+
|
|
79
105
|
void main(void) {
|
|
80
106
|
u8 pad, prev_pad;
|
|
81
107
|
u8 step; /* current melody step 0..7 */
|
|
@@ -86,8 +112,9 @@ void main(void) {
|
|
|
86
112
|
|
|
87
113
|
_keep[0] = 0;
|
|
88
114
|
|
|
89
|
-
/* conio: clear + enable display,
|
|
115
|
+
/* conio: clear + enable display, paint the frame, then the static labels. */
|
|
90
116
|
clrscr();
|
|
117
|
+
draw_frame(); /* visible bordered panel (not a blank backdrop) */
|
|
91
118
|
cputsxy(8, 8, "PC ENGINE MUSIC + SFX");
|
|
92
119
|
cputsxy(8, 11, "MELODY:");
|
|
93
120
|
cputsxy(8, BAR_Y - 1, "STEP:");
|