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
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* PC Engine "sports" — a Pong-style two-paddle scaffold.
|
|
3
|
+
*
|
|
4
|
+
* Two paddles and a bouncing ball on a netted court. The d-pad moves player 1's
|
|
5
|
+
* (left) paddle up/down. Player 2's (right) paddle follows the ball with a
|
|
6
|
+
* chase-AI so the game is playable solo. The ball deflects off paddles and the
|
|
7
|
+
* top/bottom court lines; a ball past either edge scores for the other side and
|
|
8
|
+
* re-serves. Score is shown with background digit tiles. Mirrors the
|
|
9
|
+
* NES/Genesis/SNES/GB/SMS sports scaffolds.
|
|
10
|
+
*
|
|
11
|
+
* Paddles + ball are hardware sprites; the court (green field, white border
|
|
12
|
+
* lines, dashed centre net) is the BG tilemap, so the screen is clearly a
|
|
13
|
+
* sports court (clears the verify gate).
|
|
14
|
+
*
|
|
15
|
+
* PCE notes (see pce_hw.h / MENTAL_MODEL.md):
|
|
16
|
+
* - disp_enable() turns on BG + sprites + the VBlank IRQ (waitvsync needs it).
|
|
17
|
+
* - .bss must be non-empty (pce_video.c's _pce_keep[] covers it).
|
|
18
|
+
*
|
|
19
|
+
* cc65 is C89 — declare locals at the top of a block.
|
|
20
|
+
*/
|
|
21
|
+
#include <pce.h>
|
|
22
|
+
#include <stdint.h> /* int8_t/int16_t for ball velocity + positions */
|
|
23
|
+
#include "pce_hw.h"
|
|
24
|
+
|
|
25
|
+
/* ---- VRAM layout (word addresses) --------------------------------------- */
|
|
26
|
+
#define BAT_VRAM 0x0000
|
|
27
|
+
#define FONT_VRAM 0x1000 /* digit tiles */
|
|
28
|
+
#define GREEN_VRAM 0x1400 /* court field (colour 1) */
|
|
29
|
+
#define LINE_VRAM 0x1410 /* court line / border (colour 2) */
|
|
30
|
+
#define NET_VRAM 0x1420 /* dashed centre net */
|
|
31
|
+
#define PADDLE_VRAM 0x1800 /* 16x16 paddle segment */
|
|
32
|
+
#define BALL_VRAM 0x1840 /* 16x16 ball */
|
|
33
|
+
|
|
34
|
+
#define BAT_ENTRY(pal, vram) ((u16)(((pal) << 12) | ((vram) >> 4)))
|
|
35
|
+
|
|
36
|
+
#define COURT_TOP 24
|
|
37
|
+
#define COURT_BOT 216
|
|
38
|
+
#define PADDLE_H 48 /* 3 stacked 16px sprite segments */
|
|
39
|
+
#define BALL_SIZE 12
|
|
40
|
+
#define PADDLE_X1 16
|
|
41
|
+
#define PADDLE_X2 224
|
|
42
|
+
|
|
43
|
+
/* ---- font (digits only) ------------------------------------------------- */
|
|
44
|
+
#define NUM_GLYPHS 10
|
|
45
|
+
static const u8 FONT5x7[NUM_GLYPHS][7] = {
|
|
46
|
+
{0x0E,0x11,0x13,0x15,0x19,0x11,0x0E}, /* 0 */
|
|
47
|
+
{0x04,0x0C,0x04,0x04,0x04,0x04,0x0E}, /* 1 */
|
|
48
|
+
{0x0E,0x11,0x01,0x02,0x04,0x08,0x1F}, /* 2 */
|
|
49
|
+
{0x1F,0x02,0x04,0x02,0x01,0x11,0x0E}, /* 3 */
|
|
50
|
+
{0x02,0x06,0x0A,0x12,0x1F,0x02,0x02}, /* 4 */
|
|
51
|
+
{0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E}, /* 5 */
|
|
52
|
+
{0x06,0x08,0x10,0x1E,0x11,0x11,0x0E}, /* 6 */
|
|
53
|
+
{0x1F,0x01,0x02,0x04,0x08,0x08,0x08}, /* 7 */
|
|
54
|
+
{0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E}, /* 8 */
|
|
55
|
+
{0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C} /* 9 */
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/* ---- state -------------------------------------------------------------- */
|
|
59
|
+
static int16_t p1y, p2y, bx, by;
|
|
60
|
+
static int8_t bdx, bdy;
|
|
61
|
+
static u8 score_p1, score_p2;
|
|
62
|
+
static u8 serve_timer;
|
|
63
|
+
static u8 pad;
|
|
64
|
+
static u16 tile_buf[16];
|
|
65
|
+
static u16 spr_buf[64];
|
|
66
|
+
static u8 sfx_timer;
|
|
67
|
+
|
|
68
|
+
static void make_solid_tile(u16 *t, u8 ci) {
|
|
69
|
+
u8 r;
|
|
70
|
+
u8 p0 = (ci & 1) ? 0xFF : 0x00;
|
|
71
|
+
u8 p1 = (ci & 2) ? 0xFF : 0x00;
|
|
72
|
+
u8 p2 = (ci & 4) ? 0xFF : 0x00;
|
|
73
|
+
u8 p3 = (ci & 8) ? 0xFF : 0x00;
|
|
74
|
+
for (r = 0; r < 8; ++r) {
|
|
75
|
+
t[r] = (u16)(p0 | (p1 << 8));
|
|
76
|
+
t[r + 8] = (u16)(p2 | (p3 << 8));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* net tile: green field (colour 1) with a colour-2 vertical dash centre column */
|
|
81
|
+
static void make_net_tile(u16 *t) {
|
|
82
|
+
u8 r;
|
|
83
|
+
for (r = 0; r < 8; ++r) {
|
|
84
|
+
u8 dash = (r < 5); /* dashed: top 5 rows of each tile are the dash */
|
|
85
|
+
u8 p1 = dash ? 0x18 : 0x00; /* centre 2 px -> colour 2 (plane1) */
|
|
86
|
+
t[r] = (u16)(0x00FF | (p1 << 8)); /* plane0 full (green) + dash */
|
|
87
|
+
t[r + 8] = 0x0000;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static void make_paddle_sprite(void) {
|
|
92
|
+
u8 r;
|
|
93
|
+
for (r = 0; r < 64; ++r) spr_buf[r] = 0;
|
|
94
|
+
/* a solid 8px-wide vertical bar centred in the 16px cell, colour 1 */
|
|
95
|
+
for (r = 0; r < 16; ++r) spr_buf[r] = 0x0FF0;
|
|
96
|
+
load_tiles(PADDLE_VRAM, spr_buf, 64);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static void make_ball_sprite(void) {
|
|
100
|
+
static const u16 ball[16] = {
|
|
101
|
+
0x0000, 0x0000, 0x07E0, 0x0FF0, 0x1FF8, 0x1FF8, 0x3FFC, 0x3FFC,
|
|
102
|
+
0x3FFC, 0x3FFC, 0x1FF8, 0x1FF8, 0x0FF0, 0x07E0, 0x0000, 0x0000
|
|
103
|
+
};
|
|
104
|
+
u8 r;
|
|
105
|
+
for (r = 0; r < 64; ++r) spr_buf[r] = 0;
|
|
106
|
+
for (r = 0; r < 16; ++r) spr_buf[r] = ball[r]; /* colour 1 */
|
|
107
|
+
load_tiles(BALL_VRAM, spr_buf, 64);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static void upload_font(void) {
|
|
111
|
+
u8 g, row, bits, plane0;
|
|
112
|
+
for (g = 0; g < NUM_GLYPHS; ++g) {
|
|
113
|
+
for (row = 0; row < 16; ++row) tile_buf[row] = 0;
|
|
114
|
+
for (row = 0; row < 7; ++row) {
|
|
115
|
+
bits = FONT5x7[g][row];
|
|
116
|
+
plane0 = 0;
|
|
117
|
+
if (bits & 0x10) plane0 |= 0x40;
|
|
118
|
+
if (bits & 0x08) plane0 |= 0x20;
|
|
119
|
+
if (bits & 0x04) plane0 |= 0x10;
|
|
120
|
+
if (bits & 0x02) plane0 |= 0x08;
|
|
121
|
+
if (bits & 0x01) plane0 |= 0x04;
|
|
122
|
+
tile_buf[row] = (u16)plane0;
|
|
123
|
+
}
|
|
124
|
+
load_tiles((u16)(FONT_VRAM + g * 16), tile_buf, 16);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static void draw_court(void) {
|
|
129
|
+
u8 r, c;
|
|
130
|
+
u16 g = BAT_ENTRY(0, GREEN_VRAM);
|
|
131
|
+
u16 ln = BAT_ENTRY(0, LINE_VRAM);
|
|
132
|
+
u16 nt = BAT_ENTRY(0, NET_VRAM);
|
|
133
|
+
u16 e;
|
|
134
|
+
for (r = 0; r < 32; ++r) {
|
|
135
|
+
vram_set_write_addr((u16)(BAT_VRAM + r * 32));
|
|
136
|
+
for (c = 0; c < 32; ++c) {
|
|
137
|
+
if (r <= 2 || r >= 27) e = ln; /* top/bottom border */
|
|
138
|
+
else if (c == 1 || c == 30) e = ln; /* sidelines */
|
|
139
|
+
else if (c == 16) e = nt; /* centre net */
|
|
140
|
+
else e = g; /* field */
|
|
141
|
+
VDC_DATA_LO = (u8)(e & 0xFF);
|
|
142
|
+
VDC_DATA_HI = (u8)(e >> 8);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static void put_glyph(u8 col, u8 row, u8 digit) {
|
|
148
|
+
u16 e = BAT_ENTRY(0, (u16)(FONT_VRAM + digit * 16));
|
|
149
|
+
vram_set_write_addr((u16)(BAT_VRAM + row * 32 + col));
|
|
150
|
+
VDC_DATA_LO = (u8)(e & 0xFF);
|
|
151
|
+
VDC_DATA_HI = (u8)(e >> 8);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static void draw_scores(void) {
|
|
155
|
+
put_glyph(12, 1, (u8)(score_p1 % 10));
|
|
156
|
+
put_glyph(19, 1, (u8)(score_p2 % 10));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static void serve_ball(u8 to_left) {
|
|
160
|
+
bx = 120; by = 110;
|
|
161
|
+
bdx = to_left ? -2 : 2;
|
|
162
|
+
bdy = ((score_p1 + score_p2) & 1) ? -1 : 1;
|
|
163
|
+
serve_timer = 40;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
void main(void) {
|
|
167
|
+
u8 i;
|
|
168
|
+
|
|
169
|
+
_pce_keep[0] = 0;
|
|
170
|
+
|
|
171
|
+
/* palette */
|
|
172
|
+
vce_set_color(0, PCE_RGB(0, 1, 0)); /* backdrop dark green */
|
|
173
|
+
vce_set_color(1, PCE_RGB(0, 4, 1)); /* BG c1: court green */
|
|
174
|
+
vce_set_color(2, PCE_RGB(7, 7, 7)); /* BG c2: white lines/net/digit */
|
|
175
|
+
vce_set_color(256, PCE_RGB(0, 0, 0)); /* spr pal0 transparent */
|
|
176
|
+
vce_set_color(257, PCE_RGB(7, 7, 7)); /* spr pal0 c1: white paddle */
|
|
177
|
+
vce_set_color(272, PCE_RGB(0, 0, 0)); /* spr pal1 transparent */
|
|
178
|
+
vce_set_color(273, PCE_RGB(7, 7, 0)); /* spr pal1 c1: yellow ball */
|
|
179
|
+
|
|
180
|
+
upload_font();
|
|
181
|
+
make_solid_tile(tile_buf, 1); load_tiles(GREEN_VRAM, tile_buf, 16);
|
|
182
|
+
make_solid_tile(tile_buf, 2); load_tiles(LINE_VRAM, tile_buf, 16);
|
|
183
|
+
make_net_tile(tile_buf); load_tiles(NET_VRAM, tile_buf, 16);
|
|
184
|
+
make_paddle_sprite();
|
|
185
|
+
make_ball_sprite();
|
|
186
|
+
|
|
187
|
+
draw_court();
|
|
188
|
+
|
|
189
|
+
p1y = 90; p2y = 90;
|
|
190
|
+
score_p1 = 0; score_p2 = 0;
|
|
191
|
+
sfx_timer = 0;
|
|
192
|
+
serve_ball(0);
|
|
193
|
+
draw_scores();
|
|
194
|
+
|
|
195
|
+
pce_joy_init();
|
|
196
|
+
disp_enable();
|
|
197
|
+
|
|
198
|
+
for (;;) {
|
|
199
|
+
u8 slot;
|
|
200
|
+
int16_t target;
|
|
201
|
+
waitvsync();
|
|
202
|
+
|
|
203
|
+
/* stage sprites: P1 paddle (3 segs), P2 paddle (3 segs), ball */
|
|
204
|
+
slot = 0;
|
|
205
|
+
for (i = 0; i < 3; ++i)
|
|
206
|
+
set_sprite(slot++, PADDLE_X1, (u16)(p1y + i * 16), PADDLE_VRAM >> 6, 0);
|
|
207
|
+
for (i = 0; i < 3; ++i)
|
|
208
|
+
set_sprite(slot++, PADDLE_X2, (u16)(p2y + i * 16), PADDLE_VRAM >> 6, 0);
|
|
209
|
+
set_sprite(slot++, (u16)bx, (u16)by, BALL_VRAM >> 6, 1);
|
|
210
|
+
satb_dma();
|
|
211
|
+
|
|
212
|
+
pad = pce_joy_read();
|
|
213
|
+
|
|
214
|
+
/* P1 control */
|
|
215
|
+
if ((pad & PCE_JOY_UP) && p1y > COURT_TOP) p1y -= 3;
|
|
216
|
+
if ((pad & PCE_JOY_DOWN) && p1y < COURT_BOT - PADDLE_H) p1y += 3;
|
|
217
|
+
|
|
218
|
+
/* P2 chase-AI */
|
|
219
|
+
target = (int16_t)(by - PADDLE_H / 2 + BALL_SIZE / 2);
|
|
220
|
+
if (p2y < target && p2y < COURT_BOT - PADDLE_H) p2y += 2;
|
|
221
|
+
else if (p2y > target && p2y > COURT_TOP) p2y -= 2;
|
|
222
|
+
|
|
223
|
+
if (serve_timer > 0) {
|
|
224
|
+
serve_timer--;
|
|
225
|
+
} else {
|
|
226
|
+
bx = (int16_t)(bx + bdx);
|
|
227
|
+
by = (int16_t)(by + bdy);
|
|
228
|
+
|
|
229
|
+
if (by < COURT_TOP) { by = COURT_TOP; bdy = (int8_t)(-bdy); psg_tone(1, 0x280, 18); sfx_timer = 4; }
|
|
230
|
+
if (by + BALL_SIZE > COURT_BOT) { by = (int16_t)(COURT_BOT - BALL_SIZE); bdy = (int8_t)(-bdy); psg_tone(1, 0x280, 18); sfx_timer = 4; }
|
|
231
|
+
|
|
232
|
+
/* left paddle */
|
|
233
|
+
if (bdx < 0 && bx <= PADDLE_X1 + 12 && bx + BALL_SIZE >= PADDLE_X1 &&
|
|
234
|
+
by + BALL_SIZE > p1y && by < p1y + PADDLE_H) {
|
|
235
|
+
bdx = (int8_t)(-bdx);
|
|
236
|
+
bx = PADDLE_X1 + 12;
|
|
237
|
+
psg_tone(0, 0x200, 22); sfx_timer = 4;
|
|
238
|
+
}
|
|
239
|
+
/* right paddle */
|
|
240
|
+
if (bdx > 0 && bx + BALL_SIZE >= PADDLE_X2 && bx <= PADDLE_X2 + 12 &&
|
|
241
|
+
by + BALL_SIZE > p2y && by < p2y + PADDLE_H) {
|
|
242
|
+
bdx = (int8_t)(-bdx);
|
|
243
|
+
bx = (int16_t)(PADDLE_X2 - BALL_SIZE);
|
|
244
|
+
psg_tone(0, 0x200, 22); sfx_timer = 4;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* scoring */
|
|
248
|
+
if (bx < 2) { if (score_p2 < 9) score_p2++; draw_scores(); psg_tone(0, 0x100, 24); sfx_timer = 8; serve_ball(0); }
|
|
249
|
+
if (bx > 246) { if (score_p1 < 9) score_p1++; draw_scores(); psg_tone(0, 0x100, 24); sfx_timer = 8; serve_ball(1); }
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (sfx_timer) { --sfx_timer; if (sfx_timer == 0) { psg_off(0); psg_off(1); } }
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -90,7 +90,10 @@ static void make_sprite(void) {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/* Draw a 32x32-cell checkerboard of TILE_A / TILE_B across the BAT. The default
|
|
93
|
-
* PCE virtual screen is 32x32 cells (256x256 px), which covers the display.
|
|
93
|
+
* PCE virtual screen is 32x32 cells (256x256 px), which covers the display.
|
|
94
|
+
* A two-colour checkerboard (green + dark teal) makes the whole playfield read
|
|
95
|
+
* as a real, visible background — a SOLID single-colour fill instead looks blank
|
|
96
|
+
* to a human (one colour covers >92% of the screen), so we alternate by cell. */
|
|
94
97
|
static void fill_bat(void) {
|
|
95
98
|
u16 ea = BAT_ENTRY(TILE_A_VRAM, 0);
|
|
96
99
|
u16 eb = BAT_ENTRY(TILE_B_VRAM, 0);
|
|
@@ -98,7 +101,9 @@ static void fill_bat(void) {
|
|
|
98
101
|
for (r = 0; r < 32; ++r) {
|
|
99
102
|
vram_set_write_addr((u16)(BAT_VRAM + r * 32));
|
|
100
103
|
for (col = 0; col < 32; ++col) {
|
|
101
|
-
|
|
104
|
+
/* 2x2-cell checkerboard: alternates green/teal so the background is
|
|
105
|
+
* unmistakably present while the sprite still stands out clearly. */
|
|
106
|
+
e = (((r >> 1) ^ (col >> 1)) & 1) ? eb : ea;
|
|
102
107
|
VDC_DATA_LO = (u8)(e & 0xFF);
|
|
103
108
|
VDC_DATA_HI = (u8)(e >> 8);
|
|
104
109
|
}
|
package/examples/sms/main.c
CHANGED
|
@@ -47,9 +47,10 @@ static void vdp_init(void) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/* ─── Palette + tile data ─────────────────────────────────────────── */
|
|
50
|
-
/* SMS CRAM: 2-2-2 BGR. Entry 0 = backdrop.
|
|
50
|
+
/* SMS CRAM: 2-2-2 BGR. Entry 0 = backdrop. Entries: 1 = yellow 'H',
|
|
51
|
+
* 2 = blue panel, 3 = dark-blue panel (the checkerboard fill colours). */
|
|
51
52
|
static const uint8_t palette[32] = {
|
|
52
|
-
|
|
53
|
+
0x10, 0x0F, 0x30, 0x14, 0x00, 0x00, 0x00, 0x00, /* BG: backdrop, yellow, blue, dk-blue */
|
|
53
54
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
54
55
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* sprite palette unused */
|
|
55
56
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
@@ -68,6 +69,23 @@ static const uint8_t tile_h[32] = {
|
|
|
68
69
|
0x00, 0x00, 0x00, 0x00,
|
|
69
70
|
};
|
|
70
71
|
|
|
72
|
+
/* Tile 2 = solid colour 2 (blue), tile 3 = solid colour 3 (dark blue).
|
|
73
|
+
* The whole 32×24 screen is painted as a 2-colour checkerboard so the
|
|
74
|
+
* screen is obviously not blank and no single colour dominates. Plane 1
|
|
75
|
+
* set → colour 2; planes 0+1 set → colour 3. */
|
|
76
|
+
static const uint8_t tile_fill2[32] = {
|
|
77
|
+
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
78
|
+
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
79
|
+
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
80
|
+
0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
81
|
+
};
|
|
82
|
+
static const uint8_t tile_fill3[32] = {
|
|
83
|
+
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
84
|
+
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
85
|
+
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
86
|
+
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
|
|
87
|
+
};
|
|
88
|
+
|
|
71
89
|
/* ─── Upload helpers ──────────────────────────────────────────────── */
|
|
72
90
|
static void load_palette(void) {
|
|
73
91
|
uint8_t i;
|
|
@@ -77,16 +95,27 @@ static void load_palette(void) {
|
|
|
77
95
|
|
|
78
96
|
static void load_tile(void) {
|
|
79
97
|
uint8_t i;
|
|
80
|
-
/* Tile 1 at VRAM offset 32 (= tile_idx * 32). Tile 0 left blank. */
|
|
98
|
+
/* Tile 1 = 'H' at VRAM offset 32 (= tile_idx * 32). Tile 0 left blank. */
|
|
81
99
|
vdp_set_addr(32, VDP_VRAM_WRITE);
|
|
82
100
|
for (i = 0; i < 32; i++) PORT_VDP_DATA = tile_h[i];
|
|
101
|
+
/* Tile 2 = blue fill (offset 64), tile 3 = dark-blue fill (offset 96). */
|
|
102
|
+
vdp_set_addr(64, VDP_VRAM_WRITE);
|
|
103
|
+
for (i = 0; i < 32; i++) PORT_VDP_DATA = tile_fill2[i];
|
|
104
|
+
vdp_set_addr(96, VDP_VRAM_WRITE);
|
|
105
|
+
for (i = 0; i < 32; i++) PORT_VDP_DATA = tile_fill3[i];
|
|
83
106
|
}
|
|
84
107
|
|
|
108
|
+
/* Paint the whole 32×24 visible screen as a blue/dark-blue checkerboard so
|
|
109
|
+
* the screen is obviously not blank and no single colour dominates. */
|
|
85
110
|
static void clear_name_table(void) {
|
|
86
|
-
|
|
111
|
+
uint8_t row, col;
|
|
87
112
|
vdp_set_addr(0x3800, VDP_VRAM_WRITE);
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
for (row = 0; row < 24; row++) {
|
|
114
|
+
for (col = 0; col < 32; col++) {
|
|
115
|
+
PORT_VDP_DATA = ((row ^ col) & 1) ? 2 : 3; /* checkerboard tiles 2/3 */
|
|
116
|
+
PORT_VDP_DATA = 0; /* attr: BG palette, no flip */
|
|
117
|
+
}
|
|
118
|
+
}
|
|
90
119
|
}
|
|
91
120
|
|
|
92
121
|
static void place_h(void) {
|
|
@@ -28,22 +28,37 @@ extern void sms_vdp_write_reg(uint8_t reg, uint8_t value);
|
|
|
28
28
|
extern void sms_vdp_set_addr(uint16_t addr, uint8_t prefix);
|
|
29
29
|
extern void sms_load_palette(const uint8_t *palette);
|
|
30
30
|
extern void sms_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
|
|
31
|
+
extern void sms_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
|
|
31
32
|
extern void sms_vblank_wait(void);
|
|
32
33
|
extern uint8_t sms_joypad_read(void);
|
|
33
34
|
extern void sms_sprite_init(void);
|
|
34
35
|
extern void sms_sprite_set(uint8_t slot, uint8_t x, uint8_t y, uint8_t tile);
|
|
35
36
|
extern void sms_sat_upload(void);
|
|
36
37
|
|
|
37
|
-
/* BG palette: backdrop blue
|
|
38
|
-
*
|
|
38
|
+
/* BG palette: backdrop blue, colour 1 = teal, colour 2 = navy (the two
|
|
39
|
+
* tones of the dithered BG). Sprite palette (entries 16-31) sets white at
|
|
40
|
+
* index 17 so our sprite is visible.
|
|
39
41
|
* SMS CRAM is 2-2-2 BGR: 0x00=black, 0x3F=white. */
|
|
40
42
|
static const uint8_t palette[32] = {
|
|
41
|
-
0x10,
|
|
43
|
+
0x10,0x38,0x20,0x00, 0x00,0x00,0x00,0x00,
|
|
42
44
|
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
|
|
43
45
|
0x00,0x3F,0x00,0x00, 0x00,0x00,0x00,0x00,
|
|
44
46
|
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
|
|
45
47
|
};
|
|
46
48
|
|
|
49
|
+
/* Two BG tiles in the BG bank at $0000. Tile 0 is a dithered checkerboard
|
|
50
|
+
* (plane0/plane1 alternate per row) so the whole BG fills with TWO colours
|
|
51
|
+
* and no single colour dominates the frame — a flat one-colour fill still
|
|
52
|
+
* reads as a blank screen. */
|
|
53
|
+
static const uint8_t bg_tiles[32 * 1] = {
|
|
54
|
+
/* T_BG — dither: plane0=0xAA→colour 1, plane1=0x55→colour 2, swapped
|
|
55
|
+
* each row so it reads as a fine checkerboard. */
|
|
56
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
57
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
58
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
59
|
+
0xAA,0x55,0x00,0x00, 0x55,0xAA,0x00,0x00,
|
|
60
|
+
};
|
|
61
|
+
|
|
47
62
|
/* One 8×8 sprite tile (4bpp interleaved). Filled square in color 1. */
|
|
48
63
|
static const uint8_t sprite_tile[32] = {
|
|
49
64
|
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
@@ -52,6 +67,14 @@ static const uint8_t sprite_tile[32] = {
|
|
|
52
67
|
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
53
68
|
};
|
|
54
69
|
|
|
70
|
+
/* Fill the whole 32×28 name table with the dithered BG tile. */
|
|
71
|
+
static void draw_bg(void) {
|
|
72
|
+
uint8_t row, col;
|
|
73
|
+
for (row = 0; row < 28; row++)
|
|
74
|
+
for (col = 0; col < 32; col++)
|
|
75
|
+
sms_set_tilemap_cell(row, col, 0, 0);
|
|
76
|
+
}
|
|
77
|
+
|
|
55
78
|
void main(void) {
|
|
56
79
|
uint8_t x = 124; /* mid-screen X */
|
|
57
80
|
uint8_t y = 88; /* mid-screen Y */
|
|
@@ -59,6 +82,9 @@ void main(void) {
|
|
|
59
82
|
|
|
60
83
|
sms_vdp_init();
|
|
61
84
|
sms_load_palette(palette);
|
|
85
|
+
/* BG dither tile → BG bank $0000, then paint the whole name table. */
|
|
86
|
+
sms_load_tiles(0x0000, bg_tiles, 32);
|
|
87
|
+
draw_bg();
|
|
62
88
|
/* Upload one sprite tile to VRAM $2000 (sprite tile area). */
|
|
63
89
|
sms_load_tiles(0x2000, sprite_tile, 32);
|
|
64
90
|
|
|
@@ -29,10 +29,11 @@ extern void sms_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte
|
|
|
29
29
|
extern void sms_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
|
|
30
30
|
extern void sms_vblank_wait(void);
|
|
31
31
|
|
|
32
|
-
/* BG palette: backdrop dark blue, fg colour 1 = bright cyan, colour 2 = yellow
|
|
33
|
-
*
|
|
32
|
+
/* BG palette: backdrop dark blue, fg colour 1 = bright cyan, colour 2 = yellow,
|
|
33
|
+
* colour 3 = navy (the second dither tone behind the text).
|
|
34
|
+
* SMS CRAM is 2-2-2 BGR — 0x20 = blue, 0x3F = white/cyan, 0x0F = yellow. */
|
|
34
35
|
static const uint8_t palette[32] = {
|
|
35
|
-
0x20, 0x3F, 0x0F,
|
|
36
|
+
0x20, 0x3F, 0x0F, 0x28, 0x00, 0x00, 0x00, 0x00,
|
|
36
37
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
37
38
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
38
39
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
@@ -100,8 +101,19 @@ static const uint8_t font_tiles[] = {
|
|
|
100
101
|
0xC3,0x00,0x00,0x00, 0xC3,0x00,0x00,0x00,
|
|
101
102
|
0xC3,0x00,0x00,0x00, 0xC3,0x00,0x00,0x00,
|
|
102
103
|
0x66,0x00,0x00,0x00, 0x3C,0x00,0x00,0x00,
|
|
104
|
+
|
|
105
|
+
/* tile 9 — dithered BG. plane1=0xFF (colour-2 bit always on), plane0
|
|
106
|
+
* alternates 0xAA/0x55 so pixels flip between colour 2 (yellow) and
|
|
107
|
+
* colour 3 (navy). Fills the whole field with TWO tones so no single
|
|
108
|
+
* colour dominates, while the cyan (colour 1) text stays distinct. */
|
|
109
|
+
0xAA,0xFF,0x00,0x00, 0x55,0xFF,0x00,0x00,
|
|
110
|
+
0xAA,0xFF,0x00,0x00, 0x55,0xFF,0x00,0x00,
|
|
111
|
+
0xAA,0xFF,0x00,0x00, 0x55,0xFF,0x00,0x00,
|
|
112
|
+
0xAA,0xFF,0x00,0x00, 0x55,0xFF,0x00,0x00,
|
|
103
113
|
};
|
|
104
114
|
|
|
115
|
+
#define T_DITHER 9
|
|
116
|
+
|
|
105
117
|
/* Tile indices for each char in our message — 'S' 'M' 'S' ' ' 'M' 'U'
|
|
106
118
|
* 'S' 'I' 'C' ' ' 'D' 'E' 'M' 'O'. 14 cells total. */
|
|
107
119
|
static const uint8_t message[14] = {
|
|
@@ -114,9 +126,11 @@ static const uint8_t message[14] = {
|
|
|
114
126
|
static void clear_name_table(void) {
|
|
115
127
|
uint8_t row;
|
|
116
128
|
uint8_t col;
|
|
129
|
+
/* Fill with the dithered BG tile (not blank) so the whole screen reads
|
|
130
|
+
* as a two-tone field and never as a blank backdrop. */
|
|
117
131
|
for (row = 0; row < 28; row++) {
|
|
118
132
|
for (col = 0; col < 32; col++) {
|
|
119
|
-
sms_set_tilemap_cell(row, col,
|
|
133
|
+
sms_set_tilemap_cell(row, col, T_DITHER, 0);
|
|
120
134
|
}
|
|
121
135
|
}
|
|
122
136
|
}
|
|
@@ -25,16 +25,19 @@ extern uint8_t sms_joypad_read(void);
|
|
|
25
25
|
#define T_R 1
|
|
26
26
|
#define T_G 2
|
|
27
27
|
#define T_B 3
|
|
28
|
+
#define T_WALL 4 /* well border */
|
|
29
|
+
#define T_FIELD 5 /* empty well interior */
|
|
28
30
|
|
|
29
31
|
static const uint8_t palette[32] = {
|
|
30
|
-
/* BG palette: backdrop
|
|
31
|
-
|
|
32
|
+
/* BG palette: 0 backdrop navy, 1 red, 2 green, 3 blue, 4 wall grey,
|
|
33
|
+
* 5 dim field blue */
|
|
34
|
+
0x10,0x03,0x0C,0x30, 0x15,0x14, 0x00,0x00,
|
|
32
35
|
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
|
|
33
36
|
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
|
|
34
37
|
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
|
|
35
38
|
};
|
|
36
39
|
|
|
37
|
-
static const uint8_t bg_tiles[32 *
|
|
40
|
+
static const uint8_t bg_tiles[32 * 6] = {
|
|
38
41
|
/* T_BLANK */
|
|
39
42
|
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
|
40
43
|
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
|
@@ -53,6 +56,16 @@ static const uint8_t bg_tiles[32 * 4] = {
|
|
|
53
56
|
0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
|
|
54
57
|
0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
|
|
55
58
|
0xFF,0xFF,0x00,0x00, 0xFF,0xFF,0x00,0x00,
|
|
59
|
+
/* T_WALL — colour 4 fill (plane 2 set) */
|
|
60
|
+
0x00,0x00,0xFF,0x00, 0x00,0x00,0xFF,0x00,
|
|
61
|
+
0x00,0x00,0xFF,0x00, 0x00,0x00,0xFF,0x00,
|
|
62
|
+
0x00,0x00,0xFF,0x00, 0x00,0x00,0xFF,0x00,
|
|
63
|
+
0x00,0x00,0xFF,0x00, 0x00,0x00,0xFF,0x00,
|
|
64
|
+
/* T_FIELD — colour 5 fill (planes 0+2 set) = dim field */
|
|
65
|
+
0xFF,0x00,0xFF,0x00, 0xFF,0x00,0xFF,0x00,
|
|
66
|
+
0xFF,0x00,0xFF,0x00, 0xFF,0x00,0xFF,0x00,
|
|
67
|
+
0xFF,0x00,0xFF,0x00, 0xFF,0x00,0xFF,0x00,
|
|
68
|
+
0xFF,0x00,0xFF,0x00, 0xFF,0x00,0xFF,0x00,
|
|
56
69
|
};
|
|
57
70
|
|
|
58
71
|
static uint8_t grid[ROWS][COLS];
|
|
@@ -76,7 +89,7 @@ static uint8_t tile_for(uint8_t c) {
|
|
|
76
89
|
if (c == 1) return T_R;
|
|
77
90
|
if (c == 2) return T_G;
|
|
78
91
|
if (c == 3) return T_B;
|
|
79
|
-
return
|
|
92
|
+
return T_FIELD; /* empty cell shows the dim well interior, not backdrop */
|
|
80
93
|
}
|
|
81
94
|
|
|
82
95
|
static void draw_cell(int8_t col, int8_t row, uint8_t cell) {
|
|
@@ -85,6 +98,21 @@ static void draw_cell(int8_t col, int8_t row, uint8_t cell) {
|
|
|
85
98
|
sms_set_tilemap_cell((uint8_t)(row + 1), (uint8_t)(col + 7), tile_for(cell), 0);
|
|
86
99
|
}
|
|
87
100
|
|
|
101
|
+
/* Draw the well: a grey border frame around the 6x12 play field with a dim
|
|
102
|
+
* field interior, so the playfield is clearly visible even when empty.
|
|
103
|
+
* The grid maps cell (col,row) -> tilemap (row+1, col+7), i.e. rows 1..12
|
|
104
|
+
* cols 7..12. Frame the perimeter at rows 0..13, cols 6..13. */
|
|
105
|
+
static void draw_well(void) {
|
|
106
|
+
uint8_t r, c;
|
|
107
|
+
for (r = 0; r <= 13; r++) {
|
|
108
|
+
for (c = 6; c <= 13; c++) {
|
|
109
|
+
uint8_t t = T_FIELD;
|
|
110
|
+
if (r == 0 || r == 13 || c == 6 || c == 13) t = T_WALL;
|
|
111
|
+
sms_set_tilemap_cell(r, c, t, 0);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
88
116
|
static void draw_grid(void) {
|
|
89
117
|
int8_t r, c;
|
|
90
118
|
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) draw_cell(c, r, grid[r][c]);
|
|
@@ -151,7 +179,7 @@ void main(void) {
|
|
|
151
179
|
|
|
152
180
|
sms_vdp_init();
|
|
153
181
|
sms_load_palette(palette);
|
|
154
|
-
sms_load_tiles(0x0000, bg_tiles, 32 *
|
|
182
|
+
sms_load_tiles(0x0000, bg_tiles, 32 * 6);
|
|
155
183
|
|
|
156
184
|
for (r = 0; r < 24; r++) for (c = 0; c < 32; c++) sms_set_tilemap_cell(r, c, T_BLANK, 0);
|
|
157
185
|
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) grid[r][c] = 0;
|
|
@@ -159,6 +187,7 @@ void main(void) {
|
|
|
159
187
|
score = 0;
|
|
160
188
|
fall_timer = 0;
|
|
161
189
|
new_piece();
|
|
190
|
+
draw_well();
|
|
162
191
|
draw_grid();
|
|
163
192
|
|
|
164
193
|
sfx_init();
|
|
@@ -18,6 +18,7 @@ extern void sms_vdp_init(void);
|
|
|
18
18
|
extern void sms_vdp_display_on(void);
|
|
19
19
|
extern void sms_load_palette(const uint8_t *palette);
|
|
20
20
|
extern void sms_load_tiles(uint16_t vram_dest, const uint8_t *src, uint16_t byte_count);
|
|
21
|
+
extern void sms_set_tilemap_cell(uint8_t row, uint8_t col, uint8_t tile_idx, uint8_t attr);
|
|
21
22
|
extern void sms_vblank_wait(void);
|
|
22
23
|
extern uint8_t sms_joypad_read(void);
|
|
23
24
|
extern void sms_sprite_init(void);
|
|
@@ -31,13 +32,47 @@ extern void sms_sat_upload(void);
|
|
|
31
32
|
#define MAX_OBSTACLES 4
|
|
32
33
|
|
|
33
34
|
static const uint8_t palette[32] = {
|
|
34
|
-
|
|
35
|
+
/* BG: 0 = dark navy backdrop, 1 = grass green, 2 = road grey */
|
|
36
|
+
0x10, 0x08, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
35
37
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
36
38
|
/* Sprite palette: white (1), red (2) */
|
|
37
39
|
0x00, 0x3F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
38
40
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
39
41
|
};
|
|
40
42
|
|
|
43
|
+
/* Three BG tiles for the track, loaded into the BG tile bank at $0000:
|
|
44
|
+
* tile 0 = blank (backdrop), tile 1 = grass (colour 1), tile 2 = road
|
|
45
|
+
* (colour 2). The track fills the whole 32x24 SMS screen so the display
|
|
46
|
+
* is a clear road scene, not a flat backdrop. */
|
|
47
|
+
static const uint8_t bg_tiles[96] = {
|
|
48
|
+
/* BG tile 0 = blank */
|
|
49
|
+
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
|
50
|
+
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
|
|
51
|
+
/* BG tile 1 = grass (colour 1 -> plane 0 set) */
|
|
52
|
+
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
53
|
+
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
54
|
+
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
55
|
+
0xFF,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,
|
|
56
|
+
/* BG tile 2 = road (colour 2 -> plane 1 set) */
|
|
57
|
+
0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
|
|
58
|
+
0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
|
|
59
|
+
0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
|
|
60
|
+
0x00,0xFF,0x00,0x00, 0x00,0xFF,0x00,0x00,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/* Paint the whole 32x24 SMS screen: grey road down the centre lanes,
|
|
64
|
+
* green grass on the shoulders. BG tile bank is $0000. The road spans the
|
|
65
|
+
* three lanes (player X 72..184 -> roughly cols 8..23). */
|
|
66
|
+
static void draw_track(void) {
|
|
67
|
+
uint8_t row, col;
|
|
68
|
+
for (row = 0; row < 24; row++) {
|
|
69
|
+
for (col = 0; col < 32; col++) {
|
|
70
|
+
uint8_t road = (col >= 8 && col <= 23);
|
|
71
|
+
sms_set_tilemap_cell(row, col, road ? 2 : 1, 0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
41
76
|
/* Two sprite tiles — player (colour 1) + enemy (colour 2). */
|
|
42
77
|
static const uint8_t tiles[64] = {
|
|
43
78
|
/* Tile 0 = player car (colour 1 → plane 0 set) */
|
|
@@ -97,7 +132,9 @@ void main(void) {
|
|
|
97
132
|
uint8_t i;
|
|
98
133
|
sms_vdp_init();
|
|
99
134
|
sms_load_palette(palette);
|
|
100
|
-
sms_load_tiles(
|
|
135
|
+
sms_load_tiles(0x0000, bg_tiles, 96); /* BG tiles -> BG bank $0000 */
|
|
136
|
+
sms_load_tiles(0x2000, tiles, 64); /* sprite tiles -> sprite bank $2000 */
|
|
137
|
+
draw_track();
|
|
101
138
|
sms_sprite_init();
|
|
102
139
|
sfx_init();
|
|
103
140
|
sms_vdp_display_on();
|