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
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
#define P0C1 (*(volatile uint8_t*)0x21)
|
|
15
15
|
#define P0C2 (*(volatile uint8_t*)0x22)
|
|
16
16
|
#define P0C3 (*(volatile uint8_t*)0x23)
|
|
17
|
+
#define P1C1 (*(volatile uint8_t*)0x25)
|
|
18
|
+
#define P2C1 (*(volatile uint8_t*)0x29)
|
|
17
19
|
#define MSTAT (*(volatile uint8_t*)0x28)
|
|
18
20
|
#define DPPH (*(volatile uint8_t*)0x2C)
|
|
19
21
|
#define DPPL (*(volatile uint8_t*)0x30)
|
|
@@ -44,6 +46,40 @@ MK_DL(dl_row4); MK_DL(dl_row5); MK_DL(dl_row6); MK_DL(dl_row7);
|
|
|
44
46
|
|
|
45
47
|
static uint8_t dl_empty[2] = { 0, 0 };
|
|
46
48
|
|
|
49
|
+
/* ── Background playfield ─────────────────────────────────────────
|
|
50
|
+
* Without a full-screen drawable the display list emits only the
|
|
51
|
+
* player and ~99% of the screen stays the flat BACKGRND colour
|
|
52
|
+
* (reads as "blank"). These full-width bands give the level a sky,
|
|
53
|
+
* a field, and a solid ground strip the player stands on.
|
|
54
|
+
*
|
|
55
|
+
* A single DL drawable is at most 32 bytes = 128 px wide, so a full
|
|
56
|
+
* 160-px line needs TWO drawables. Width = byte[3] low 5 bits (32-n);
|
|
57
|
+
* high 3 bits = palette. */
|
|
58
|
+
static const uint8_t band_pix[32] = {
|
|
59
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
|
|
60
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
|
|
61
|
+
};
|
|
62
|
+
#define MK_BAND(name, pal) static uint8_t name[11] = { \
|
|
63
|
+
0, 0x40, 0, ((pal) << 5) | 0, 0, /* 128 px @ x0 */ \
|
|
64
|
+
0, 0x40, 0, ((pal) << 5) | 24, 128, /* 32 px @ x128 */ \
|
|
65
|
+
0 }
|
|
66
|
+
MK_BAND(dl_field, 1);
|
|
67
|
+
MK_BAND(dl_ground, 2);
|
|
68
|
+
/* Ground strip starts just below where the player rests (GROUND_Y). */
|
|
69
|
+
#define GROUND_ZONE 200
|
|
70
|
+
|
|
71
|
+
static void set_band_addr(uint8_t* dl) {
|
|
72
|
+
uint16_t a = (uint16_t)(uintptr_t)band_pix;
|
|
73
|
+
dl[0] = dl[5] = (uint8_t)(a & 0xFF);
|
|
74
|
+
dl[2] = dl[7] = (uint8_t)(a >> 8);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static uint16_t bg_zone_dl(int zone) {
|
|
78
|
+
if (zone >= GROUND_ZONE) return (uint16_t)(uintptr_t)dl_ground;
|
|
79
|
+
if (zone >= 28) return (uint16_t)(uintptr_t)dl_field;
|
|
80
|
+
return (uint16_t)(uintptr_t)dl_empty;
|
|
81
|
+
}
|
|
82
|
+
|
|
47
83
|
#define DLL_ZONES 243
|
|
48
84
|
static uint8_t dll[DLL_ZONES * 3];
|
|
49
85
|
|
|
@@ -60,7 +96,6 @@ static void set_dll_entry(int idx, uint16_t dl_ptr) {
|
|
|
60
96
|
}
|
|
61
97
|
|
|
62
98
|
static void build_dll(uint8_t y) {
|
|
63
|
-
uint16_t empty = (uint16_t)(uintptr_t)dl_empty;
|
|
64
99
|
int i;
|
|
65
100
|
for (i = 0; i < DLL_ZONES; i++) {
|
|
66
101
|
uint16_t dl;
|
|
@@ -74,7 +109,7 @@ static void build_dll(uint8_t y) {
|
|
|
74
109
|
case 5: dl = (uint16_t)(uintptr_t)dl_row5; break;
|
|
75
110
|
case 6: dl = (uint16_t)(uintptr_t)dl_row6; break;
|
|
76
111
|
case 7: dl = (uint16_t)(uintptr_t)dl_row7; break;
|
|
77
|
-
default: dl =
|
|
112
|
+
default: dl = bg_zone_dl(i); break;
|
|
78
113
|
}
|
|
79
114
|
set_dll_entry(i, dl);
|
|
80
115
|
}
|
|
@@ -112,13 +147,17 @@ void main(void) {
|
|
|
112
147
|
set_dl_addr(dl_row5, player_row5);
|
|
113
148
|
set_dl_addr(dl_row6, player_row6);
|
|
114
149
|
set_dl_addr(dl_row7, player_row7);
|
|
150
|
+
set_band_addr(dl_field);
|
|
151
|
+
set_band_addr(dl_ground);
|
|
115
152
|
set_x((uint8_t)px);
|
|
116
153
|
build_dll((uint8_t)(py16 >> 4));
|
|
117
154
|
|
|
118
|
-
BACKGRND = 0x84;
|
|
119
|
-
P0C1 = 0x46;
|
|
155
|
+
BACKGRND = 0x84; /* sky */
|
|
156
|
+
P0C1 = 0x46; /* player */
|
|
120
157
|
P0C2 = 0x0F;
|
|
121
158
|
P0C3 = 0x36;
|
|
159
|
+
P1C1 = 0x96; /* distant field (teal) */
|
|
160
|
+
P2C1 = 0x24; /* ground (brown) */
|
|
122
161
|
CHARBASE = 0;
|
|
123
162
|
OFFSET = 0;
|
|
124
163
|
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
#define P0C1 (*(volatile uint8_t*)0x21)
|
|
21
21
|
#define P0C2 (*(volatile uint8_t*)0x22)
|
|
22
22
|
#define P0C3 (*(volatile uint8_t*)0x23)
|
|
23
|
+
#define P1C1 (*(volatile uint8_t*)0x25)
|
|
24
|
+
#define P2C1 (*(volatile uint8_t*)0x29)
|
|
23
25
|
#define MSTAT (*(volatile uint8_t*)0x28)
|
|
24
26
|
#define DPPH (*(volatile uint8_t*)0x2C)
|
|
25
27
|
#define DPPL (*(volatile uint8_t*)0x30)
|
|
@@ -53,6 +55,37 @@ MK_DL(dl_row4); MK_DL(dl_row5); MK_DL(dl_row6); MK_DL(dl_row7);
|
|
|
53
55
|
|
|
54
56
|
static uint8_t dl_empty[2] = { 0, 0 };
|
|
55
57
|
|
|
58
|
+
/* ── Background well ──────────────────────────────────────────────
|
|
59
|
+
* Without a full-screen drawable the display list emits only the
|
|
60
|
+
* falling block and ~99% of the screen stays the flat BACKGRND colour
|
|
61
|
+
* (reads as "blank"). Each well zone draws three full-width segments:
|
|
62
|
+
* a side wall (palette 2), the playfield well in the centre where the
|
|
63
|
+
* piece falls (palette 1), the other wall (palette 2). Width =
|
|
64
|
+
* byte[3] low 5 bits (32-n); high 3 bits = palette. */
|
|
65
|
+
static const uint8_t band_pix[16] = {
|
|
66
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
|
|
67
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
|
|
68
|
+
};
|
|
69
|
+
/* 12 bytes (48 px) wall @ x0, 16 bytes (64 px) well @ x48,
|
|
70
|
+
* 12 bytes (48 px) wall @ x112, terminator. */
|
|
71
|
+
static uint8_t dl_well[16] = {
|
|
72
|
+
0, 0x40, 0, (2 << 5) | 20, 0,
|
|
73
|
+
0, 0x40, 0, (1 << 5) | 16, 48,
|
|
74
|
+
0, 0x40, 0, (2 << 5) | 20, 112,
|
|
75
|
+
0
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
static void set_well_addr(void) {
|
|
79
|
+
uint16_t a = (uint16_t)(uintptr_t)band_pix;
|
|
80
|
+
dl_well[0] = dl_well[5] = dl_well[10] = (uint8_t)(a & 0xFF);
|
|
81
|
+
dl_well[2] = dl_well[7] = dl_well[12] = (uint8_t)(a >> 8);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static uint16_t bg_zone_dl(int zone) {
|
|
85
|
+
if (zone >= 32 && zone < 200) return (uint16_t)(uintptr_t)dl_well;
|
|
86
|
+
return (uint16_t)(uintptr_t)dl_empty;
|
|
87
|
+
}
|
|
88
|
+
|
|
56
89
|
#define DLL_ZONES 243
|
|
57
90
|
static uint8_t dll[DLL_ZONES * 3];
|
|
58
91
|
|
|
@@ -80,7 +113,6 @@ static void set_x(uint8_t x) {
|
|
|
80
113
|
}
|
|
81
114
|
|
|
82
115
|
static void build_dll(int y) {
|
|
83
|
-
uint16_t empty = (uint16_t)(uintptr_t)dl_empty;
|
|
84
116
|
int i;
|
|
85
117
|
for (i = 0; i < DLL_ZONES; i++) {
|
|
86
118
|
uint16_t dl;
|
|
@@ -94,7 +126,7 @@ static void build_dll(int y) {
|
|
|
94
126
|
case 5: dl = (uint16_t)(uintptr_t)dl_row5; break;
|
|
95
127
|
case 6: dl = (uint16_t)(uintptr_t)dl_row6; break;
|
|
96
128
|
case 7: dl = (uint16_t)(uintptr_t)dl_row7; break;
|
|
97
|
-
default: dl =
|
|
129
|
+
default: dl = bg_zone_dl(i); break;
|
|
98
130
|
}
|
|
99
131
|
set_dll_entry(i, dl);
|
|
100
132
|
}
|
|
@@ -120,13 +152,16 @@ void main(void) {
|
|
|
120
152
|
piece_x_col = COLS / 2;
|
|
121
153
|
piece_y = TOP_Y;
|
|
122
154
|
color_cycle = 0;
|
|
155
|
+
set_well_addr();
|
|
123
156
|
set_x((uint8_t)(60 + piece_x_col * CELL_W_PIX));
|
|
124
157
|
build_dll(piece_y);
|
|
125
158
|
|
|
126
|
-
BACKGRND = 0x00;
|
|
127
|
-
P0C1 = 0x46; /* red */
|
|
159
|
+
BACKGRND = 0x00; /* black surround */
|
|
160
|
+
P0C1 = 0x46; /* falling piece (red) */
|
|
128
161
|
P0C2 = 0x46;
|
|
129
162
|
P0C3 = 0x46;
|
|
163
|
+
P1C1 = 0x02; /* well interior (dark blue-grey) */
|
|
164
|
+
P2C1 = 0x08; /* well walls (steel) */
|
|
130
165
|
CHARBASE = 0;
|
|
131
166
|
OFFSET = 0;
|
|
132
167
|
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
#define P0C1 (*(volatile uint8_t*)0x21)
|
|
18
18
|
#define P0C2 (*(volatile uint8_t*)0x22)
|
|
19
19
|
#define P0C3 (*(volatile uint8_t*)0x23)
|
|
20
|
+
#define P1C1 (*(volatile uint8_t*)0x25)
|
|
21
|
+
#define P2C1 (*(volatile uint8_t*)0x29)
|
|
20
22
|
#define MSTAT (*(volatile uint8_t*)0x28)
|
|
21
23
|
#define DPPH (*(volatile uint8_t*)0x2C)
|
|
22
24
|
#define DPPL (*(volatile uint8_t*)0x30)
|
|
@@ -44,6 +46,37 @@ MK_DL(dl_row4); MK_DL(dl_row5); MK_DL(dl_row6); MK_DL(dl_row7);
|
|
|
44
46
|
|
|
45
47
|
static uint8_t dl_empty[2] = { 0, 0 };
|
|
46
48
|
|
|
49
|
+
/* ── Background road ──────────────────────────────────────────────
|
|
50
|
+
* Without a full-screen drawable the display list emits only the car
|
|
51
|
+
* and ~99% of the screen stays the flat BACKGRND colour (reads as
|
|
52
|
+
* "blank"). Each road zone draws three full-width segments: grass on
|
|
53
|
+
* the left (palette 1), the grey road down the centre (palette 2),
|
|
54
|
+
* grass on the right (palette 1). Width = byte[3] low 5 bits (32-n);
|
|
55
|
+
* high 3 bits = palette. */
|
|
56
|
+
static const uint8_t band_pix[16] = {
|
|
57
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
|
|
58
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
|
|
59
|
+
};
|
|
60
|
+
/* 8 bytes (32 px) grass @ x0, 16 bytes (64 px) road @ x32,
|
|
61
|
+
* 8 bytes (32 px) grass @ x96, terminator. */
|
|
62
|
+
static uint8_t dl_road[16] = {
|
|
63
|
+
0, 0x40, 0, (1 << 5) | 24, 0,
|
|
64
|
+
0, 0x40, 0, (2 << 5) | 16, 32,
|
|
65
|
+
0, 0x40, 0, (1 << 5) | 24, 96,
|
|
66
|
+
0
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
static void set_road_addr(void) {
|
|
70
|
+
uint16_t a = (uint16_t)(uintptr_t)band_pix;
|
|
71
|
+
dl_road[0] = dl_road[5] = dl_road[10] = (uint8_t)(a & 0xFF);
|
|
72
|
+
dl_road[2] = dl_road[7] = dl_road[12] = (uint8_t)(a >> 8);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static uint16_t bg_zone_dl(int zone) {
|
|
76
|
+
if (zone >= 16 && zone < 220) return (uint16_t)(uintptr_t)dl_road;
|
|
77
|
+
return (uint16_t)(uintptr_t)dl_empty;
|
|
78
|
+
}
|
|
79
|
+
|
|
47
80
|
#define DLL_ZONES 243
|
|
48
81
|
static uint8_t dll[DLL_ZONES * 3];
|
|
49
82
|
|
|
@@ -71,7 +104,6 @@ static void set_x(uint8_t x) {
|
|
|
71
104
|
}
|
|
72
105
|
|
|
73
106
|
static void build_dll(void) {
|
|
74
|
-
uint16_t empty = (uint16_t)(uintptr_t)dl_empty;
|
|
75
107
|
int i;
|
|
76
108
|
for (i = 0; i < DLL_ZONES; i++) {
|
|
77
109
|
uint16_t dl;
|
|
@@ -85,7 +117,7 @@ static void build_dll(void) {
|
|
|
85
117
|
case 5: dl = (uint16_t)(uintptr_t)dl_row5; break;
|
|
86
118
|
case 6: dl = (uint16_t)(uintptr_t)dl_row6; break;
|
|
87
119
|
case 7: dl = (uint16_t)(uintptr_t)dl_row7; break;
|
|
88
|
-
default: dl =
|
|
120
|
+
default: dl = bg_zone_dl(i); break;
|
|
89
121
|
}
|
|
90
122
|
set_dll_entry(i, dl);
|
|
91
123
|
}
|
|
@@ -110,13 +142,16 @@ void main(void) {
|
|
|
110
142
|
set_dl_addr(dl_row7, car_row7);
|
|
111
143
|
|
|
112
144
|
lane = 1;
|
|
145
|
+
set_road_addr();
|
|
113
146
|
set_x(lane_xs[lane]);
|
|
114
147
|
build_dll();
|
|
115
148
|
|
|
116
|
-
BACKGRND = 0x88;
|
|
117
|
-
P0C1 = 0x46;
|
|
149
|
+
BACKGRND = 0x88; /* sky/horizon */
|
|
150
|
+
P0C1 = 0x46; /* car */
|
|
118
151
|
P0C2 = 0x0F;
|
|
119
152
|
P0C3 = 0x36;
|
|
153
|
+
P1C1 = 0xC8; /* roadside grass (green) */
|
|
154
|
+
P2C1 = 0x06; /* road surface (grey) */
|
|
120
155
|
CHARBASE = 0;
|
|
121
156
|
OFFSET = 0;
|
|
122
157
|
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
#define P0C1 (*(volatile uint8_t*)0x21)
|
|
21
21
|
#define P0C2 (*(volatile uint8_t*)0x22)
|
|
22
22
|
#define P0C3 (*(volatile uint8_t*)0x23)
|
|
23
|
+
#define P1C1 (*(volatile uint8_t*)0x25)
|
|
24
|
+
#define P2C1 (*(volatile uint8_t*)0x29)
|
|
23
25
|
#define MSTAT (*(volatile uint8_t*)0x28)
|
|
24
26
|
#define DPPH (*(volatile uint8_t*)0x2C)
|
|
25
27
|
#define DPPL (*(volatile uint8_t*)0x30)
|
|
@@ -50,6 +52,39 @@ MK_DL(dl_row4); MK_DL(dl_row5); MK_DL(dl_row6); MK_DL(dl_row7);
|
|
|
50
52
|
|
|
51
53
|
static uint8_t dl_empty[2] = { 0, 0 };
|
|
52
54
|
|
|
55
|
+
/* ── Background playfield ─────────────────────────────────────────
|
|
56
|
+
* Without a full-screen drawable the display list emits only the
|
|
57
|
+
* ship and ~99% of the screen stays the flat BACKGRND colour (reads
|
|
58
|
+
* as "blank"). These full-width bands fill the non-ship zones with a
|
|
59
|
+
* starfield-style background so the frame has real content.
|
|
60
|
+
*
|
|
61
|
+
* A single DL drawable is at most 32 bytes = 128 px wide, so a full
|
|
62
|
+
* 160-px line needs TWO drawables. Width = byte[3] low 5 bits (32-n);
|
|
63
|
+
* high 3 bits = palette. */
|
|
64
|
+
static const uint8_t band_pix[32] = {
|
|
65
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
|
|
66
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
|
|
67
|
+
};
|
|
68
|
+
#define MK_BAND(name, pal) static uint8_t name[11] = { \
|
|
69
|
+
0, 0x40, 0, ((pal) << 5) | 0, 0, /* 128 px @ x0 */ \
|
|
70
|
+
0, 0x40, 0, ((pal) << 5) | 24, 128, /* 32 px @ x128 */ \
|
|
71
|
+
0 }
|
|
72
|
+
MK_BAND(dl_field, 1);
|
|
73
|
+
MK_BAND(dl_ground, 2);
|
|
74
|
+
#define GROUND_ZONE 188
|
|
75
|
+
|
|
76
|
+
static void set_band_addr(uint8_t* dl) {
|
|
77
|
+
uint16_t a = (uint16_t)(uintptr_t)band_pix;
|
|
78
|
+
dl[0] = dl[5] = (uint8_t)(a & 0xFF);
|
|
79
|
+
dl[2] = dl[7] = (uint8_t)(a >> 8);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static uint16_t bg_zone_dl(int zone) {
|
|
83
|
+
if (zone >= GROUND_ZONE) return (uint16_t)(uintptr_t)dl_ground;
|
|
84
|
+
if (zone >= 28) return (uint16_t)(uintptr_t)dl_field;
|
|
85
|
+
return (uint16_t)(uintptr_t)dl_empty;
|
|
86
|
+
}
|
|
87
|
+
|
|
53
88
|
#define DLL_ZONES 243
|
|
54
89
|
static uint8_t dll[DLL_ZONES * 3];
|
|
55
90
|
|
|
@@ -74,7 +109,6 @@ static void set_x(uint8_t x) {
|
|
|
74
109
|
}
|
|
75
110
|
|
|
76
111
|
static void build_dll(int y) {
|
|
77
|
-
uint16_t empty = (uint16_t)(uintptr_t)dl_empty;
|
|
78
112
|
int i;
|
|
79
113
|
for (i = 0; i < DLL_ZONES; i++) {
|
|
80
114
|
uint16_t dl;
|
|
@@ -88,7 +122,7 @@ static void build_dll(int y) {
|
|
|
88
122
|
case 5: dl = (uint16_t)(uintptr_t)dl_row5; break;
|
|
89
123
|
case 6: dl = (uint16_t)(uintptr_t)dl_row6; break;
|
|
90
124
|
case 7: dl = (uint16_t)(uintptr_t)dl_row7; break;
|
|
91
|
-
default: dl =
|
|
125
|
+
default: dl = bg_zone_dl(i); break;
|
|
92
126
|
}
|
|
93
127
|
set_dll_entry(i, dl);
|
|
94
128
|
}
|
|
@@ -111,6 +145,8 @@ void main(void) {
|
|
|
111
145
|
set_dl_addr(dl_row5, ship_row5);
|
|
112
146
|
set_dl_addr(dl_row6, ship_row6);
|
|
113
147
|
set_dl_addr(dl_row7, ship_row7);
|
|
148
|
+
set_band_addr(dl_field);
|
|
149
|
+
set_band_addr(dl_ground);
|
|
114
150
|
|
|
115
151
|
player_x = 80;
|
|
116
152
|
player_y = 180;
|
|
@@ -121,6 +157,8 @@ void main(void) {
|
|
|
121
157
|
P0C1 = 0x0F;
|
|
122
158
|
P0C2 = 0x1C;
|
|
123
159
|
P0C3 = 0x46;
|
|
160
|
+
P1C1 = 0x84; /* upper nebula band (deep blue) */
|
|
161
|
+
P2C1 = 0x82; /* lower nebula band (darker blue) */
|
|
124
162
|
CHARBASE = 0;
|
|
125
163
|
OFFSET = 0;
|
|
126
164
|
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
#define P0C1 (*(volatile uint8_t*)0x21)
|
|
22
22
|
#define P0C2 (*(volatile uint8_t*)0x22)
|
|
23
23
|
#define P0C3 (*(volatile uint8_t*)0x23)
|
|
24
|
+
#define P1C1 (*(volatile uint8_t*)0x25)
|
|
25
|
+
#define P2C1 (*(volatile uint8_t*)0x29)
|
|
24
26
|
#define MSTAT (*(volatile uint8_t*)0x28)
|
|
25
27
|
#define DPPH (*(volatile uint8_t*)0x2C)
|
|
26
28
|
#define DPPL (*(volatile uint8_t*)0x30)
|
|
@@ -60,6 +62,29 @@ static uint8_t scanline_dls[PLAY_LINES * DL_BYTES_PER_LINE];
|
|
|
60
62
|
|
|
61
63
|
static uint8_t dl_empty[2] = { 0, 0 };
|
|
62
64
|
|
|
65
|
+
/* ── Court border bands ───────────────────────────────────────────
|
|
66
|
+
* The court itself (per-scanline DLs below) only draws two thin
|
|
67
|
+
* paddles + a ball, so on a black screen ~99% of the frame is blank.
|
|
68
|
+
* Fill the zones above and below the court with full-width bands so
|
|
69
|
+
* the court is framed by visible walls. A single DL drawable is at
|
|
70
|
+
* most 32 bytes = 128 px wide, so a full 160-px line needs TWO
|
|
71
|
+
* drawables. Width = byte[3] low 5 bits (32-n); high 3 bits = palette. */
|
|
72
|
+
static const uint8_t band_pix[32] = {
|
|
73
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
|
|
74
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
|
|
75
|
+
};
|
|
76
|
+
#define MK_BAND(name, pal) static uint8_t name[11] = { \
|
|
77
|
+
0, 0x40, 0, ((pal) << 5) | 0, 0, /* 128 px @ x0 */ \
|
|
78
|
+
0, 0x40, 0, ((pal) << 5) | 24, 128, /* 32 px @ x128 */ \
|
|
79
|
+
0 }
|
|
80
|
+
MK_BAND(dl_wall, 1);
|
|
81
|
+
|
|
82
|
+
static void set_band_addr(uint8_t* dl) {
|
|
83
|
+
uint16_t a = (uint16_t)(uintptr_t)band_pix;
|
|
84
|
+
dl[0] = dl[5] = (uint8_t)(a & 0xFF);
|
|
85
|
+
dl[2] = dl[7] = (uint8_t)(a >> 8);
|
|
86
|
+
}
|
|
87
|
+
|
|
63
88
|
#define DLL_ZONES 243
|
|
64
89
|
static uint8_t dll[DLL_ZONES * 3];
|
|
65
90
|
|
|
@@ -92,19 +117,23 @@ static uint8_t emit_obj(uint8_t* dl, uint8_t off, uint16_t data_addr,
|
|
|
92
117
|
|
|
93
118
|
/* Rebuild ALL scanline DLs + DLL based on current object positions. */
|
|
94
119
|
static void rebuild(void) {
|
|
120
|
+
uint16_t wall = (uint16_t)(uintptr_t)dl_wall;
|
|
95
121
|
uint16_t empty = (uint16_t)(uintptr_t)dl_empty;
|
|
96
122
|
uint16_t solid = (uint16_t)(uintptr_t)solid_row;
|
|
97
123
|
int line;
|
|
98
124
|
int i;
|
|
99
125
|
|
|
100
|
-
/* DLL: point each play-area line at its scanline DL slot;
|
|
101
|
-
*
|
|
126
|
+
/* DLL: point each play-area line at its scanline DL slot; frame the
|
|
127
|
+
* court with full-width wall bands above and below, leaving a thin
|
|
128
|
+
* empty gutter right at the court edges. */
|
|
102
129
|
for (i = 0; i < DLL_ZONES; i++) {
|
|
103
130
|
if (i >= COURT_TOP && i < COURT_BOT) {
|
|
104
131
|
uint16_t dlp = (uint16_t)(uintptr_t)&scanline_dls[(i - COURT_TOP) * DL_BYTES_PER_LINE];
|
|
105
132
|
set_dll_entry(i, dlp);
|
|
133
|
+
} else if (i >= COURT_TOP - 8 && i < COURT_BOT + 8) {
|
|
134
|
+
set_dll_entry(i, empty); /* small gutter around the court */
|
|
106
135
|
} else {
|
|
107
|
-
set_dll_entry(i,
|
|
136
|
+
set_dll_entry(i, wall);
|
|
108
137
|
}
|
|
109
138
|
}
|
|
110
139
|
|
|
@@ -151,13 +180,15 @@ void main(void) {
|
|
|
151
180
|
p1y = 110; p2y = 110;
|
|
152
181
|
serve_ball(0);
|
|
153
182
|
|
|
154
|
-
BACKGRND = 0x00; /* black */
|
|
155
|
-
P0C1 = 0x0F; /* white
|
|
183
|
+
BACKGRND = 0x00; /* black court */
|
|
184
|
+
P0C1 = 0x0F; /* white paddles + ball */
|
|
156
185
|
P0C2 = 0x0F;
|
|
157
186
|
P0C3 = 0x0F;
|
|
187
|
+
P1C1 = 0x48; /* court walls (blue) */
|
|
158
188
|
CHARBASE = 0;
|
|
159
189
|
OFFSET = 0;
|
|
160
190
|
|
|
191
|
+
set_band_addr(dl_wall);
|
|
161
192
|
rebuild();
|
|
162
193
|
|
|
163
194
|
dll_addr = (uint16_t)(uintptr_t)dll;
|
|
@@ -96,10 +96,24 @@ static void render_view(uint8_t coarseCol) {
|
|
|
96
96
|
uint16_t off = (uint16_t)r * 40 + sc;
|
|
97
97
|
if (wc < WORLD_COLS && world_is_wall((uint8_t)wc, r)) {
|
|
98
98
|
SCREEN[off] = 0xA0; /* reverse-space solid block */
|
|
99
|
-
COLORS[off] = 0x0C; /* mid grey */
|
|
99
|
+
COLORS[off] = 0x0C; /* mid grey platform */
|
|
100
|
+
} else if (r >= 22) {
|
|
101
|
+
/* Ground fill below the floor row: dithered earth so the lower
|
|
102
|
+
* band reads as solid terrain, not void. */
|
|
103
|
+
SCREEN[off] = 0xA0;
|
|
104
|
+
COLORS[off] = (((uint8_t)wc ^ r) & 1) ? 0x09 : 0x08; /* brown / orange */
|
|
100
105
|
} else {
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
/* Textured sky so two colours share the backdrop and neither the
|
|
107
|
+
* sky nor the border dominates the frame. Sparse '.' stars on a
|
|
108
|
+
* coarse lattice add detail; reverse-space everywhere else gives a
|
|
109
|
+
* filled (non-blank) sky band that scrolls with the world. */
|
|
110
|
+
if (((wc * 3u + r * 7u) % 23u) == 0u) {
|
|
111
|
+
SCREEN[off] = 0x2E; /* '.' distant detail */
|
|
112
|
+
COLORS[off] = 0x01; /* white */
|
|
113
|
+
} else {
|
|
114
|
+
SCREEN[off] = 0xA0; /* solid block sky */
|
|
115
|
+
COLORS[off] = (((uint8_t)wc ^ (r >> 1)) & 1) ? 0x06 : 0x0E; /* blue / light blue */
|
|
116
|
+
}
|
|
103
117
|
}
|
|
104
118
|
}
|
|
105
119
|
}
|
|
@@ -133,8 +147,8 @@ void main(void) {
|
|
|
133
147
|
copy_sprite(0, player_sprite);
|
|
134
148
|
SPRITE_POINTERS[0] = 0x80; /* $2000/64 */
|
|
135
149
|
POKE(VIC_SPR_COL(0), 0x07); /* yellow player */
|
|
136
|
-
POKE(VIC_BORDER,
|
|
137
|
-
POKE(VIC_BG0, 0x06);
|
|
150
|
+
POKE(VIC_BORDER, 0x00); /* black border frames the scene */
|
|
151
|
+
POKE(VIC_BG0, 0x06); /* sky-blue (shows through any gaps) */
|
|
138
152
|
|
|
139
153
|
render_view(0); /* paint the initial 40-col view */
|
|
140
154
|
|
|
@@ -40,6 +40,35 @@ static void wait_vblank(void) {
|
|
|
40
40
|
while (PEEK(VIC_RASTER) >= 250) { }
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/* Paint the playfield surround so the board reads as a real puzzle screen
|
|
44
|
+
* instead of a tiny well floating in a black void: a dithered backdrop fills
|
|
45
|
+
* the whole 40x25 matrix (two dark blues, so two colours share the screen
|
|
46
|
+
* and neither dominates), then a bright frame is drawn one cell outside the
|
|
47
|
+
* 6x12 well, and the well interior is cleared to black so the falling blocks
|
|
48
|
+
* pop. Call ONCE before draw_grid(); draw_grid() owns the interior after. */
|
|
49
|
+
static void draw_field(void) {
|
|
50
|
+
uint16_t i;
|
|
51
|
+
uint8_t r, c;
|
|
52
|
+
int8_t fr, fc;
|
|
53
|
+
for (i = 0; i < 1000; i++) {
|
|
54
|
+
SCREEN[i] = 0xA0; /* solid block backdrop */
|
|
55
|
+
COLORS[i] = ((i ^ (i >> 5)) & 1) ? 0x06 : 0x0E; /* blue / light blue */
|
|
56
|
+
}
|
|
57
|
+
/* Bright frame one cell outside the well. */
|
|
58
|
+
for (fc = -1; fc <= COLS; fc++) {
|
|
59
|
+
r = (uint8_t)(GRID_R - 1); SCREEN[r * 40 + GRID_C + fc] = 0xA0; COLORS[r * 40 + GRID_C + fc] = 0x01;
|
|
60
|
+
r = (uint8_t)(GRID_R + ROWS); SCREEN[r * 40 + GRID_C + fc] = 0xA0; COLORS[r * 40 + GRID_C + fc] = 0x01;
|
|
61
|
+
}
|
|
62
|
+
for (fr = -1; fr <= ROWS; fr++) {
|
|
63
|
+
r = (uint8_t)(GRID_R + fr);
|
|
64
|
+
SCREEN[r * 40 + GRID_C - 1] = 0xA0; COLORS[r * 40 + GRID_C - 1] = 0x01;
|
|
65
|
+
SCREEN[r * 40 + GRID_C + COLS] = 0xA0; COLORS[r * 40 + GRID_C + COLS] = 0x01;
|
|
66
|
+
}
|
|
67
|
+
/* Clear the well interior to black so colored blocks stand out. */
|
|
68
|
+
for (r = 0; r < ROWS; r++)
|
|
69
|
+
for (c = 0; c < COLS; c++) SCREEN[(GRID_R + r) * 40 + GRID_C + c] = ' ';
|
|
70
|
+
}
|
|
71
|
+
|
|
43
72
|
static uint8_t rng_pick(void) {
|
|
44
73
|
rng = rng * 1103515245u + 12345u;
|
|
45
74
|
return (uint8_t)(1 + (rng >> 16) % 3);
|
|
@@ -131,14 +160,15 @@ static void lock_piece(void) {
|
|
|
131
160
|
|
|
132
161
|
void main(void) {
|
|
133
162
|
uint8_t r, c, pad, prev = 0, fall_rate, t;
|
|
134
|
-
POKE(VIC_BORDER,
|
|
135
|
-
POKE(VIC_BG0, 0x00);
|
|
163
|
+
POKE(VIC_BORDER, 0x06); /* blue border frames the playfield */
|
|
164
|
+
POKE(VIC_BG0, 0x00); /* black well interior so blocks pop */
|
|
136
165
|
|
|
137
166
|
for (r = 0; r < ROWS; r++)
|
|
138
167
|
for (c = 0; c < COLS; c++) grid[r][c] = 0;
|
|
139
168
|
|
|
140
169
|
score = 0; fall_timer = 0;
|
|
141
170
|
sfx_init();
|
|
171
|
+
draw_field(); /* paint the textured surround + well frame */
|
|
142
172
|
new_piece();
|
|
143
173
|
draw_grid();
|
|
144
174
|
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
#define POKE(addr, val) (*(volatile uint8_t*)(addr) = (val))
|
|
20
20
|
#define PEEK(addr) (*(volatile uint8_t*)(addr))
|
|
21
21
|
|
|
22
|
+
#define SCREEN ((volatile uint8_t*)0x0400)
|
|
23
|
+
#define COLORS ((volatile uint8_t*)0xD800)
|
|
24
|
+
|
|
22
25
|
#define SPRITE_POINTERS ((volatile uint8_t*)0x07F8)
|
|
23
26
|
#define SPRITE_DATA_BASE 0x2000 /* sprite N data at $2000 + N*64 — NOT $0800,
|
|
24
27
|
* which collides with the $0801 .prg load (C64-1) */
|
|
@@ -105,6 +108,28 @@ static void wait_vblank(void) {
|
|
|
105
108
|
while (PEEK(VIC_RASTER) >= 250) { }
|
|
106
109
|
}
|
|
107
110
|
|
|
111
|
+
/* Paint a deep-space backdrop into the 40x25 character matrix so the
|
|
112
|
+
* playfield reads as space instead of a flat black void. Every cell gets
|
|
113
|
+
* a dithered "nebula" char (reverse-space 0xA0) in one of two dark blues,
|
|
114
|
+
* so two colours share the screen and no single colour dominates. A sparse
|
|
115
|
+
* scatter of bright '.' stars (drawn as a normal glyph over the dither)
|
|
116
|
+
* adds twinkle. Cosmetic only — sprites still move over the top. */
|
|
117
|
+
static void draw_starfield(void) {
|
|
118
|
+
uint16_t i;
|
|
119
|
+
uint8_t r, c;
|
|
120
|
+
for (i = 0; i < 1000; i++) {
|
|
121
|
+
SCREEN[i] = 0xA0; /* solid block fills the cell */
|
|
122
|
+
COLORS[i] = ((i ^ (i >> 5)) & 1) ? 0x06 : 0x0B; /* blue / dark grey */
|
|
123
|
+
}
|
|
124
|
+
/* Scatter stars on a coarse lattice so ~1 in 12 cells twinkles. */
|
|
125
|
+
for (r = 1; r < 25; r += 3) {
|
|
126
|
+
for (c = (uint8_t)(r * 5u % 7u); c < 40; c += 7) {
|
|
127
|
+
SCREEN[r * 40 + c] = 0x2E; /* '.' star glyph */
|
|
128
|
+
COLORS[r * 40 + c] = ((r + c) & 1) ? 0x01 : 0x0F; /* white / l.grey */
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
108
133
|
static void copy_sprite(uint8_t slot, const uint8_t *data) {
|
|
109
134
|
uint8_t i;
|
|
110
135
|
volatile uint8_t *dst = (volatile uint8_t*)(SPRITE_DATA_BASE + slot * 64);
|
|
@@ -130,8 +155,9 @@ void main(void) {
|
|
|
130
155
|
for (i = 0; i < MAX_BULLETS; i++) POKE(VIC_SPR_COL(SLOT_BULLET0 + i), 0x01); /* white */
|
|
131
156
|
for (i = 0; i < MAX_ENEMIES; i++) POKE(VIC_SPR_COL(SLOT_ENEMY0 + i), 0x02); /* red */
|
|
132
157
|
|
|
133
|
-
POKE(VIC_BORDER, 0x00);
|
|
134
|
-
POKE(VIC_BG0,
|
|
158
|
+
POKE(VIC_BORDER, 0x00); /* black border frames the starfield */
|
|
159
|
+
POKE(VIC_BG0, 0x06); /* deep-blue space background */
|
|
160
|
+
draw_starfield(); /* paint the textured space backdrop */
|
|
135
161
|
|
|
136
162
|
player.x = 152; player.y = 200; player.alive = 1;
|
|
137
163
|
for (i = 0; i < MAX_BULLETS; i++) bullets[i].alive = 0;
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
#define POKE(addr, val) (*(volatile uint8_t*)(addr) = (val))
|
|
12
12
|
#define PEEK(addr) (*(volatile uint8_t*)(addr))
|
|
13
13
|
|
|
14
|
+
#define SCREEN ((volatile uint8_t*)0x0400)
|
|
15
|
+
#define COLORS ((volatile uint8_t*)0xD800)
|
|
16
|
+
|
|
14
17
|
#define JOY_UP 0x01
|
|
15
18
|
#define JOY_DOWN 0x02
|
|
16
19
|
|
|
@@ -39,6 +42,30 @@ static void wait_vblank(void) {
|
|
|
39
42
|
while (PEEK(VIC_RASTER) >= 250) { }
|
|
40
43
|
}
|
|
41
44
|
|
|
45
|
+
/* Paint a Pong court into the 40x25 character matrix so the table reads as
|
|
46
|
+
* a real court instead of a flat black void: a dithered green playfield
|
|
47
|
+
* (two greens, so two colours share the screen and neither dominates),
|
|
48
|
+
* solid top/bottom boundary rails, and a dashed centre net. Cosmetic only —
|
|
49
|
+
* the paddle/ball sprites move over the top. */
|
|
50
|
+
static void draw_court(void) {
|
|
51
|
+
uint16_t i;
|
|
52
|
+
uint8_t r, c;
|
|
53
|
+
/* Dithered green playfield fills every cell. */
|
|
54
|
+
for (i = 0; i < 1000; i++) {
|
|
55
|
+
SCREEN[i] = 0xA0; /* solid block */
|
|
56
|
+
COLORS[i] = ((i ^ (i >> 5)) & 1) ? 0x05 : 0x09; /* green / brown */
|
|
57
|
+
}
|
|
58
|
+
/* Top + bottom boundary rails (rows 1 and 23) in white. */
|
|
59
|
+
for (c = 0; c < 40; c++) {
|
|
60
|
+
SCREEN[1 * 40 + c] = 0xA0; COLORS[1 * 40 + c] = 0x01;
|
|
61
|
+
SCREEN[23 * 40 + c] = 0xA0; COLORS[23 * 40 + c] = 0x01;
|
|
62
|
+
}
|
|
63
|
+
/* Dashed centre net (column 20). */
|
|
64
|
+
for (r = 2; r < 23; r++) {
|
|
65
|
+
if (r & 1) { SCREEN[r * 40 + 20] = 0xA0; COLORS[r * 40 + 20] = 0x01; }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
42
69
|
static void copy_sprite(uint8_t slot, const uint8_t *data) {
|
|
43
70
|
uint8_t i;
|
|
44
71
|
volatile uint8_t *dst = (volatile uint8_t*)(0x2000 + slot * 64); /* $2000, not $0800 (collides w/ $0801 .prg) */
|
|
@@ -63,8 +90,9 @@ void main(void) {
|
|
|
63
90
|
POKE(VIC_SPR_COL(1), 0x01);
|
|
64
91
|
POKE(VIC_SPR_COL(2), 0x07); /* yellow ball */
|
|
65
92
|
|
|
66
|
-
POKE(VIC_BORDER, 0x00);
|
|
67
|
-
POKE(VIC_BG0,
|
|
93
|
+
POKE(VIC_BORDER, 0x00); /* black border frames the court */
|
|
94
|
+
POKE(VIC_BG0, 0x05); /* green court background */
|
|
95
|
+
draw_court(); /* paint the textured Pong court */
|
|
68
96
|
|
|
69
97
|
/* P1 paddle on left, P2 on right. */
|
|
70
98
|
POKE(VIC_SPRITE_X(0), 30);
|