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
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
/* ── racing.c — Game Boy top-down racing scaffold
|
|
1
|
+
/* ── racing.c — Game Boy Color top-down racing scaffold ────────────
|
|
2
2
|
*
|
|
3
|
-
* Endless 3-lane top-down dodge for the Game Boy. LEFT/RIGHT
|
|
4
|
-
* lanes (edge-detected), obstacles slide down at speed =
|
|
5
|
-
* (capped). Collision triggers a 60-frame freeze +
|
|
3
|
+
* Endless 3-lane top-down dodge for the Game Boy Color. LEFT/RIGHT
|
|
4
|
+
* switches lanes (edge-detected), obstacles slide down at speed =
|
|
5
|
+
* 2 + score/500 (capped). Collision triggers a 60-frame freeze +
|
|
6
|
+
* auto-reset.
|
|
6
7
|
*
|
|
7
|
-
* Game Boy screen is 160×144 — 3 lanes centred around x = {
|
|
8
|
+
* Game Boy screen is 160×144 — 3 lanes centred around x = {40, 76, 112}.
|
|
9
|
+
*
|
|
10
|
+
* The road is a real CGB-coloured background: green grass shoulders down
|
|
11
|
+
* each side, grey asphalt across the playfield, dashed white lane lines
|
|
12
|
+
* between the lanes (BG palette via BCPS/BCPD; LCDC bit 0 = BG ON — drop
|
|
13
|
+
* it and the screen is a flat colour, the #1 GB "why is it blank"
|
|
14
|
+
* footgun). Cars are colour sprites (OCPS/OCPD) on top.
|
|
8
15
|
*/
|
|
9
16
|
|
|
10
17
|
#include "gb_hardware.h"
|
|
@@ -16,7 +23,7 @@
|
|
|
16
23
|
#define PLAYER_Y 120
|
|
17
24
|
#define MAX_OBSTACLES 4
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
/* ── Sprite tiles (cars) ──────────────────────────────────────────── */
|
|
20
27
|
static const uint8_t tile_car_p1[16] = {
|
|
21
28
|
0x3C,0x00, 0x7E,0x00, 0x42,0x00, 0x7E,0x00,
|
|
22
29
|
0x7E,0x00, 0x42,0x00, 0x7E,0x00, 0x66,0x00,
|
|
@@ -26,8 +33,43 @@ static const uint8_t tile_car_en[16] = {
|
|
|
26
33
|
0x00,0x7E, 0x00,0x42, 0x00,0x7E, 0x00,0x66,
|
|
27
34
|
};
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
/* ── BG tiles (road) ──────────────────────────────────────────────── */
|
|
37
|
+
/* 2bpp: row N = byte 2N (low plane) + byte 2N+1 (high plane).
|
|
38
|
+
* asphalt — all colour 2 (grey)
|
|
39
|
+
* grass — all colour 1 (green)
|
|
40
|
+
* laneA/B — dashed colour-3 (white) lane line, two phases for dashes */
|
|
41
|
+
static const uint8_t tile_asphalt[16] = {
|
|
42
|
+
0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF,
|
|
43
|
+
0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF,
|
|
44
|
+
};
|
|
45
|
+
static const uint8_t tile_grass[16] = {
|
|
46
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
47
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
48
|
+
};
|
|
49
|
+
/* lane line = a 2px-wide colour-3 stripe down the centre of the cell;
|
|
50
|
+
* phase A draws the top half, phase B the bottom half → dashes. */
|
|
51
|
+
static const uint8_t tile_laneA[16] = {
|
|
52
|
+
0x18,0x18, 0x18,0x18, 0x18,0x18, 0x18,0x18,
|
|
53
|
+
0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF,
|
|
54
|
+
};
|
|
55
|
+
static const uint8_t tile_laneB[16] = {
|
|
56
|
+
0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF,
|
|
57
|
+
0x18,0x18, 0x18,0x18, 0x18,0x18, 0x18,0x18,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/* CGB palettes (BGR555).
|
|
61
|
+
* BG palette 0: 0 unused, 1 green grass, 2 grey asphalt, 3 white line. */
|
|
62
|
+
static const uint16_t bg_palette[4] = { 0x0000, 0x0320, 0x4210, 0x7FFF };
|
|
63
|
+
/* OBJ palette 0: 0 transparent, 1 white (player), 2 red (enemy), 3 green. */
|
|
64
|
+
static const uint16_t obj_palette[4] = { 0x0000, 0x7FFF, 0x001F, 0x03E0 };
|
|
65
|
+
|
|
66
|
+
/* Tile indices in VRAM. Sprites and BG share the $8000 table here. */
|
|
67
|
+
#define T_CAR_P1 1
|
|
68
|
+
#define T_CAR_EN 2
|
|
69
|
+
#define T_ASPHALT 3
|
|
70
|
+
#define T_GRASS 4
|
|
71
|
+
#define T_LANE_A 5
|
|
72
|
+
#define T_LANE_B 6
|
|
31
73
|
|
|
32
74
|
typedef struct { int16_t x, y; uint8_t alive; } Car;
|
|
33
75
|
|
|
@@ -72,8 +114,27 @@ static void spawn_obstacle(void) {
|
|
|
72
114
|
|
|
73
115
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
74
116
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
75
|
-
|
|
76
|
-
|
|
117
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
118
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
119
|
+
memcpy_vram(dst, src, 16);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Paint the road into BG map 0 ($9800). 20×18 visible cells:
|
|
123
|
+
* col 0 = grass shoulder (left)
|
|
124
|
+
* col 19 = grass shoulder (right)
|
|
125
|
+
* cols 1..18 = asphalt, with dashed lane lines at the two lane
|
|
126
|
+
* boundaries (cols 6 and 12). Dashes alternate per row. */
|
|
127
|
+
static void draw_road(void) {
|
|
128
|
+
uint8_t *bg = BG_MAP_0;
|
|
129
|
+
uint8_t r, c, t;
|
|
130
|
+
for (r = 0; r < 18; r++) {
|
|
131
|
+
for (c = 0; c < 20; c++) {
|
|
132
|
+
if (c == 0 || c == 19) t = T_GRASS;
|
|
133
|
+
else if (c == 6 || c == 12) t = (r & 1) ? T_LANE_A : T_LANE_B;
|
|
134
|
+
else t = T_ASPHALT;
|
|
135
|
+
bg[r * 32 + c] = t;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
77
138
|
}
|
|
78
139
|
|
|
79
140
|
void main(void) {
|
|
@@ -84,23 +145,29 @@ void main(void) {
|
|
|
84
145
|
lcd_init_default();
|
|
85
146
|
LCDC = 0;
|
|
86
147
|
|
|
87
|
-
upload_tile(
|
|
88
|
-
upload_tile(
|
|
89
|
-
upload_tile(
|
|
148
|
+
upload_tile(T_CAR_P1, tile_car_p1);
|
|
149
|
+
upload_tile(T_CAR_EN, tile_car_en);
|
|
150
|
+
upload_tile(T_ASPHALT, tile_asphalt);
|
|
151
|
+
upload_tile(T_GRASS, tile_grass);
|
|
152
|
+
upload_tile(T_LANE_A, tile_laneA);
|
|
153
|
+
upload_tile(T_LANE_B, tile_laneB);
|
|
90
154
|
|
|
91
|
-
|
|
92
|
-
for (i = 0; i < 4; i++) {
|
|
93
|
-
OCPD = (uint8_t)(obj_palette[i] & 0xFF);
|
|
94
|
-
OCPD = (uint8_t)((obj_palette[i] >> 8) & 0xFF);
|
|
95
|
-
}
|
|
155
|
+
/* CGB palettes. */
|
|
96
156
|
BCPS = 0x80;
|
|
97
157
|
for (i = 0; i < 4; i++) {
|
|
98
158
|
BCPD = (uint8_t)(bg_palette[i] & 0xFF);
|
|
99
159
|
BCPD = (uint8_t)((bg_palette[i] >> 8) & 0xFF);
|
|
100
160
|
}
|
|
161
|
+
OCPS = 0x80;
|
|
162
|
+
for (i = 0; i < 4; i++) {
|
|
163
|
+
OCPD = (uint8_t)(obj_palette[i] & 0xFF);
|
|
164
|
+
OCPD = (uint8_t)((obj_palette[i] >> 8) & 0xFF);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
draw_road();
|
|
101
168
|
|
|
102
169
|
oam_clear();
|
|
103
|
-
LCDC = LCDC_LCD_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
170
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
104
171
|
sound_init();
|
|
105
172
|
|
|
106
173
|
reset_run();
|
|
@@ -111,13 +178,13 @@ void main(void) {
|
|
|
111
178
|
|
|
112
179
|
/* Stage OAM — player + obstacles. */
|
|
113
180
|
for (i = 0; i < 40; i++) oam_set(i, 0, 0, 0, 0);
|
|
114
|
-
oam_set(0, (uint8_t)(player.y + 16), (uint8_t)(player.x + 8),
|
|
181
|
+
oam_set(0, (uint8_t)(player.y + 16), (uint8_t)(player.x + 8), T_CAR_P1, 0);
|
|
115
182
|
for (i = 0; i < MAX_OBSTACLES; i++) {
|
|
116
183
|
if (obstacles[i].alive) {
|
|
117
184
|
oam_set((uint8_t)(1 + i),
|
|
118
185
|
(uint8_t)(obstacles[i].y + 16),
|
|
119
186
|
(uint8_t)(obstacles[i].x + 8),
|
|
120
|
-
|
|
187
|
+
T_CAR_EN, 0);
|
|
121
188
|
}
|
|
122
189
|
}
|
|
123
190
|
oam_dma_flush();
|
|
@@ -25,6 +25,24 @@ static const uint8_t tile_ship[16] = {
|
|
|
25
25
|
0x18,0x18, 0x3C,0x3C, 0x7E,0x7E, 0xFF,0xFF,
|
|
26
26
|
0xFF,0xFF, 0x7E,0x7E, 0x3C,0x3C, 0x18,0x18,
|
|
27
27
|
};
|
|
28
|
+
/* ── BG tiles (starfield) ─────────────────────────────────────────────
|
|
29
|
+
* The background is a real starfield so the screen is never one flat
|
|
30
|
+
* colour (LCDC_BG_ON below — drop it and the screen reads as blank, the
|
|
31
|
+
* #1 GB "why is it blank" footgun).
|
|
32
|
+
* tile_space — a 50/50 dither of palette colours 0 (deep space blue) +
|
|
33
|
+
* 1 (mid blue), so even an empty patch of space mixes two
|
|
34
|
+
* shades and never lets one colour dominate the frame.
|
|
35
|
+
* tile_star — a bright colour-3 (white) "+" star on the dithered field. */
|
|
36
|
+
static const uint8_t tile_space[16] = {
|
|
37
|
+
0x55,0x00, 0xAA,0x00, 0x55,0x00, 0xAA,0x00,
|
|
38
|
+
0x55,0x00, 0xAA,0x00, 0x55,0x00, 0xAA,0x00,
|
|
39
|
+
};
|
|
40
|
+
static const uint8_t tile_star[16] = {
|
|
41
|
+
0x10,0x10, 0x10,0x10, 0x54,0x54, 0x38,0x38,
|
|
42
|
+
0x54,0x54, 0x10,0x10, 0x10,0x10, 0x00,0x00,
|
|
43
|
+
};
|
|
44
|
+
#define T_SPACE 4
|
|
45
|
+
#define T_STAR 5
|
|
28
46
|
static const uint8_t tile_bullet[16] = {
|
|
29
47
|
0x00,0x00, 0x18,0x18, 0x3C,0x3C, 0x3C,0x3C,
|
|
30
48
|
0x3C,0x3C, 0x3C,0x3C, 0x18,0x18, 0x00,0x00,
|
|
@@ -90,8 +108,20 @@ static void spawn(void) {
|
|
|
90
108
|
|
|
91
109
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
92
110
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
93
|
-
|
|
94
|
-
|
|
111
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
112
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
113
|
+
memcpy_vram(dst, src, 16);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Paint a starfield into BG map 0 ($9800): fill the visible 20×18 with the
|
|
117
|
+
* dithered space tile, then scatter bright stars on a fixed pseudo-pattern
|
|
118
|
+
* so the field reads as deep space rather than a flat colour. */
|
|
119
|
+
static void draw_starfield(void) {
|
|
120
|
+
uint8_t *bg = BG_MAP_0;
|
|
121
|
+
uint8_t r, c;
|
|
122
|
+
for (r = 0; r < 18; r++)
|
|
123
|
+
for (c = 0; c < 20; c++)
|
|
124
|
+
bg[r * 32 + c] = ((r * 7 + c * 5) % 11 == 0) ? T_STAR : T_SPACE;
|
|
95
125
|
}
|
|
96
126
|
|
|
97
127
|
void main(void) {
|
|
@@ -105,6 +135,8 @@ void main(void) {
|
|
|
105
135
|
upload_tile(1, tile_ship);
|
|
106
136
|
upload_tile(2, tile_bullet);
|
|
107
137
|
upload_tile(3, tile_enemy);
|
|
138
|
+
upload_tile(T_SPACE, tile_space);
|
|
139
|
+
upload_tile(T_STAR, tile_star);
|
|
108
140
|
|
|
109
141
|
/* Sprite palette 0 — uploaded to OCPS/OCPD (CGB-only registers). */
|
|
110
142
|
OCPS = 0x80;
|
|
@@ -120,6 +152,8 @@ void main(void) {
|
|
|
120
152
|
BCPD = (uint8_t)((bg_palette[i] >> 8) & 0xFF);
|
|
121
153
|
}
|
|
122
154
|
|
|
155
|
+
draw_starfield();
|
|
156
|
+
|
|
123
157
|
player.x = 76; player.y = 130; player.alive = 1;
|
|
124
158
|
for (i = 0; i < MAX_BULLETS; i++) bullets[i].alive = 0;
|
|
125
159
|
for (i = 0; i < MAX_ENEMIES; i++) enemies[i].alive = 0;
|
|
@@ -127,7 +161,7 @@ void main(void) {
|
|
|
127
161
|
spawn_timer = 0;
|
|
128
162
|
|
|
129
163
|
oam_clear();
|
|
130
|
-
LCDC = LCDC_LCD_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
164
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
131
165
|
sound_init();
|
|
132
166
|
|
|
133
167
|
while (1) {
|
|
@@ -28,6 +28,31 @@ static const uint8_t tile_solid[16] = {
|
|
|
28
28
|
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
/* ── BG tiles (the court) ──────────────────────────────────────────────
|
|
32
|
+
* A real playfield behind the paddles so the screen is never one flat
|
|
33
|
+
* colour (LCDC_BG_ON below — drop it and it reads as blank, the #1 GB
|
|
34
|
+
* "why is it blank" footgun).
|
|
35
|
+
* tile_court — a 50/50 dither of palette colours 0 + 1 (the green turf),
|
|
36
|
+
* so even an empty patch mixes two shades and never
|
|
37
|
+
* dominates the frame.
|
|
38
|
+
* tile_net — a dashed vertical centre-net stripe (colour 2).
|
|
39
|
+
* tile_wall — a solid colour-2 border for the top / bottom rails. */
|
|
40
|
+
static const uint8_t tile_court[16] = {
|
|
41
|
+
0x55,0x55, 0xAA,0xAA, 0x55,0x55, 0xAA,0xAA,
|
|
42
|
+
0x55,0x55, 0xAA,0xAA, 0x55,0x55, 0xAA,0xAA,
|
|
43
|
+
};
|
|
44
|
+
static const uint8_t tile_net[16] = {
|
|
45
|
+
0x18,0x18, 0x18,0x18, 0x00,0x00, 0x00,0x00,
|
|
46
|
+
0x18,0x18, 0x18,0x18, 0x00,0x00, 0x00,0x00,
|
|
47
|
+
};
|
|
48
|
+
static const uint8_t tile_wall[16] = {
|
|
49
|
+
0x00,0x00, 0xFF,0xFF, 0xFF,0xFF, 0x00,0x00,
|
|
50
|
+
0x00,0x00, 0xFF,0xFF, 0xFF,0xFF, 0x00,0x00,
|
|
51
|
+
};
|
|
52
|
+
#define T_COURT 2
|
|
53
|
+
#define T_NET 3
|
|
54
|
+
#define T_WALL 4
|
|
55
|
+
|
|
31
56
|
static const uint16_t obj_palette[4] = { 0x7FFF, 0x001F, 0x03E0, 0x7C00 };
|
|
32
57
|
static const uint16_t bg_palette[4] = { 0x2104, 0x294A, 0x4631, 0x7FFF }; /* deep court green */
|
|
33
58
|
|
|
@@ -54,8 +79,24 @@ static void reset_match(void) {
|
|
|
54
79
|
|
|
55
80
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
56
81
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
57
|
-
|
|
58
|
-
|
|
82
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
83
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
84
|
+
memcpy_vram(dst, src, 16);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Paint the Pong court into BG map 0 ($9800): dithered turf everywhere,
|
|
88
|
+
* solid top/bottom rails, and a dashed net down the centre column. */
|
|
89
|
+
static void draw_court(void) {
|
|
90
|
+
uint8_t *bg = BG_MAP_0;
|
|
91
|
+
uint8_t r, c, t;
|
|
92
|
+
for (r = 0; r < 18; r++) {
|
|
93
|
+
for (c = 0; c < 20; c++) {
|
|
94
|
+
if (r == 0 || r == 17) t = T_WALL; /* top / bottom rail */
|
|
95
|
+
else if (c == 9 || c == 10) t = (r & 1) ? T_NET : T_COURT; /* net dashes */
|
|
96
|
+
else t = T_COURT;
|
|
97
|
+
bg[r * 32 + c] = t;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
59
100
|
}
|
|
60
101
|
|
|
61
102
|
void main(void) {
|
|
@@ -68,6 +109,9 @@ void main(void) {
|
|
|
68
109
|
|
|
69
110
|
upload_tile(0, tile_blank);
|
|
70
111
|
upload_tile(1, tile_solid);
|
|
112
|
+
upload_tile(T_COURT, tile_court);
|
|
113
|
+
upload_tile(T_NET, tile_net);
|
|
114
|
+
upload_tile(T_WALL, tile_wall);
|
|
71
115
|
|
|
72
116
|
OCPS = 0x80;
|
|
73
117
|
for (i = 0; i < 4; i++) {
|
|
@@ -80,8 +124,9 @@ void main(void) {
|
|
|
80
124
|
BCPD = (uint8_t)((bg_palette[i] >> 8) & 0xFF);
|
|
81
125
|
}
|
|
82
126
|
|
|
127
|
+
draw_court();
|
|
83
128
|
oam_clear();
|
|
84
|
-
LCDC = LCDC_LCD_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
129
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
85
130
|
sound_init();
|
|
86
131
|
|
|
87
132
|
reset_match();
|
|
@@ -143,8 +143,9 @@ static const uint8_t rooms[ROOMS][ROWS * COLS] = {
|
|
|
143
143
|
|
|
144
144
|
/* ── Helpers ────────────────────────────────────────────────────── */
|
|
145
145
|
static void copy_to_vram(uint8_t *dst, const uint8_t *src, uint16_t n) {
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
/* Delegate to the runtime's pointer-walk copy — an indexed dst[i]=src[i]
|
|
147
|
+
* loop into VRAM is miscompiled by SDCC sm83. */
|
|
148
|
+
memcpy_vram(dst, src, n);
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
static void load_bg_palette(void) {
|
package/examples/genesis/main.s
CHANGED
|
@@ -89,7 +89,7 @@ Start:
|
|
|
89
89
|
move.w #$0000,VDP_DATA ; color 0: transparent / backdrop
|
|
90
90
|
move.w #$0EEE,VDP_DATA ; color 1: white-ish ($0E in each nybble)
|
|
91
91
|
move.w #$00EE,VDP_DATA ; color 2: yellow
|
|
92
|
-
move.w #$
|
|
92
|
+
move.w #$0840,VDP_DATA ; color 3: dark teal (backdrop fill)
|
|
93
93
|
|
|
94
94
|
; -----------------------------------------------------------
|
|
95
95
|
; Upload one 4bpp tile (8x8 'H', color 2 = yellow) to VRAM at
|
|
@@ -104,6 +104,34 @@ Start:
|
|
|
104
104
|
move.l (a1)+,VDP_DATA ; each row = one longword (4 bytes)
|
|
105
105
|
dbra d0,.tile_loop
|
|
106
106
|
|
|
107
|
+
; -----------------------------------------------------------
|
|
108
|
+
; Upload two PATTERNED backdrop tiles (#2 and #3) to VRAM at
|
|
109
|
+
; byte offsets $40 and $60 (VRAM cmd $40400000). Each is a teal
|
|
110
|
+
; (color 3) field sprinkled with white (color 1) dots, with the
|
|
111
|
+
; roles swapped between the two so that when we checkerboard them
|
|
112
|
+
; across the plane no single colour dominates the screen — a
|
|
113
|
+
; uniform fill still reads as "blank" to a human, so we vary it.
|
|
114
|
+
; 16 longwords total (8 rows × 2 tiles) written back-to-back.
|
|
115
|
+
move.l #$40400000,VDP_CTRL
|
|
116
|
+
lea TileBgA(pc),a1
|
|
117
|
+
moveq #16-1,d0 ; 8 rows × 2 tiles
|
|
118
|
+
.bg_tile_loop:
|
|
119
|
+
move.l (a1)+,VDP_DATA
|
|
120
|
+
dbra d0,.bg_tile_loop
|
|
121
|
+
|
|
122
|
+
; -----------------------------------------------------------
|
|
123
|
+
; Fill the ENTIRE plane A name table, checkerboarding tiles #2
|
|
124
|
+
; and #3 so there's a varied visible background behind the 'H'.
|
|
125
|
+
; Plane A base is $C000; plane size is 64x32 = 2048 cells. VRAM
|
|
126
|
+
; write at $C000 = cmd $40000003. d1 toggles 2↔3 each cell.
|
|
127
|
+
move.l #$40000003,VDP_CTRL
|
|
128
|
+
move.w #2048-1,d0 ; 2048 cells to write
|
|
129
|
+
moveq #2,d1 ; start with tile #2
|
|
130
|
+
.bg_fill_loop:
|
|
131
|
+
move.w d1,VDP_DATA ; tile d1 (pal 0, no flip, low pri)
|
|
132
|
+
eor.w #$0001,d1 ; toggle 2↔3 (tile index xor 1)
|
|
133
|
+
dbra d0,.bg_fill_loop
|
|
134
|
+
|
|
107
135
|
; -----------------------------------------------------------
|
|
108
136
|
; Place tile #1 in plane A near screen center.
|
|
109
137
|
; Plane A base is at VRAM $C000 (default after VDP init).
|
|
@@ -159,3 +187,27 @@ TileH:
|
|
|
159
187
|
dc.l $20000020
|
|
160
188
|
dc.l $20000020
|
|
161
189
|
dc.l $00000000 ; row 7: blank
|
|
190
|
+
|
|
191
|
+
; -----------------------------------------------------------------------
|
|
192
|
+
; Two patterned backdrop tiles, 4bpp, uploaded back-to-back as #2 and #3.
|
|
193
|
+
; TileBgA = teal (color 3) field with white (color 1) dots; TileBgB swaps
|
|
194
|
+
; the roles (white field, teal dots). Checkerboarding them across the
|
|
195
|
+
; plane keeps any single colour well under the "blank" threshold.
|
|
196
|
+
TileBgA: ; tile #2 — teal field, white dots
|
|
197
|
+
dc.l $33333333
|
|
198
|
+
dc.l $33133313
|
|
199
|
+
dc.l $33333333
|
|
200
|
+
dc.l $13333331
|
|
201
|
+
dc.l $33333333
|
|
202
|
+
dc.l $33133313
|
|
203
|
+
dc.l $33333333
|
|
204
|
+
dc.l $13333331
|
|
205
|
+
TileBgB: ; tile #3 — white field, teal dots
|
|
206
|
+
dc.l $11111111
|
|
207
|
+
dc.l $11311131
|
|
208
|
+
dc.l $11111111
|
|
209
|
+
dc.l $31111113
|
|
210
|
+
dc.l $11111111
|
|
211
|
+
dc.l $11311131
|
|
212
|
+
dc.l $11111111
|
|
213
|
+
dc.l $31111113
|
|
@@ -29,6 +29,17 @@ static const u32 tile_data[8] = {
|
|
|
29
29
|
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
/* A checkered backdrop block tiled across plane B so the screen isn't a
|
|
33
|
+
* flat black void (a lone sprite on black reads as "blank" to a human).
|
|
34
|
+
* Colour index 4 with a thin colour-5 frame — we set both below. */
|
|
35
|
+
static const u32 tile_bg[8] = {
|
|
36
|
+
0x44444444, 0x45555554, 0x45000054, 0x45000054,
|
|
37
|
+
0x45000054, 0x45000054, 0x45555554, 0x44444444,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
#define T_SPRITE (TILE_USER_INDEX + 0)
|
|
41
|
+
#define T_BG (TILE_USER_INDEX + 1)
|
|
42
|
+
|
|
32
43
|
int main(bool hard) {
|
|
33
44
|
(void)hard;
|
|
34
45
|
|
|
@@ -38,11 +49,22 @@ int main(bool hard) {
|
|
|
38
49
|
|
|
39
50
|
/* Make sure sprite palette 0 entry 1 is white so we can see our
|
|
40
51
|
* tile. SGDK uses 0RRR0GGG0BBB packed words (BGR, 3 bits each). */
|
|
41
|
-
PAL_setColor(1, 0x0EEE); /* near-white */
|
|
52
|
+
PAL_setColor(1, 0x0EEE); /* near-white sprite */
|
|
53
|
+
/* Plane-B backdrop colours (palette 1). */
|
|
54
|
+
PAL_setColor(16 + 4, 0x0640); /* dark teal field */
|
|
55
|
+
PAL_setColor(16 + 5, 0x0860); /* lighter frame */
|
|
42
56
|
|
|
43
57
|
/* Upload the user tile to VRAM at TILE_USER_INDEX (everything
|
|
44
58
|
* below that is reserved for SGDK's font + system tiles). */
|
|
45
|
-
VDP_loadTileData(tile_data,
|
|
59
|
+
VDP_loadTileData(tile_data, T_SPRITE, 1, DMA);
|
|
60
|
+
VDP_loadTileData(tile_bg, T_BG, 1, DMA);
|
|
61
|
+
|
|
62
|
+
/* Tile plane B with the backdrop block so there's a visible
|
|
63
|
+
* background behind the sprite + text. Sprites + the font plane (A)
|
|
64
|
+
* always draw above plane B, so the d-pad sprite reads on top. */
|
|
65
|
+
for (u16 cy = 0; cy < 28; cy++)
|
|
66
|
+
for (u16 cx = 0; cx < 40; cx++)
|
|
67
|
+
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL1, 0, 0, 0, T_BG), cx, cy);
|
|
46
68
|
|
|
47
69
|
VDP_drawText("D-PAD MOVES THE SPRITE", 8, 2);
|
|
48
70
|
VDP_drawText("START FOR SOFT RESET", 9, 4);
|
|
@@ -66,7 +88,7 @@ int main(bool hard) {
|
|
|
66
88
|
* SPRITE_SIZE(1,1) = 8×8. TILE_ATTR_FULL(palette,prio,vflip,
|
|
67
89
|
* hflip,tile_index). */
|
|
68
90
|
VDP_setSprite(0, px, py, SPRITE_SIZE(1, 1),
|
|
69
|
-
TILE_ATTR_FULL(PAL0,
|
|
91
|
+
TILE_ATTR_FULL(PAL0, 1, 0, 0, T_SPRITE));
|
|
70
92
|
VDP_updateSprites(1, DMA);
|
|
71
93
|
|
|
72
94
|
SYS_doVBlankProcess();
|
|
@@ -29,8 +29,23 @@
|
|
|
29
29
|
#define T_RED (TILE_USER_INDEX + 1)
|
|
30
30
|
#define T_GREEN (TILE_USER_INDEX + 2)
|
|
31
31
|
#define T_BLUE (TILE_USER_INDEX + 3)
|
|
32
|
+
#define T_BG (TILE_USER_INDEX + 4) /* full-screen backdrop (BG_A) */
|
|
33
|
+
#define T_WELL (TILE_USER_INDEX + 5) /* play-well backdrop (BG_A) */
|
|
32
34
|
|
|
33
35
|
static const u32 tile_blank[8] = { 0,0,0,0,0,0,0,0 };
|
|
36
|
+
/* Backdrop block for the far plane: a framed cell (colour 4 border /
|
|
37
|
+
* colour 5 fill) tiled across the whole screen so the playfield no
|
|
38
|
+
* longer floats on a flat black backdrop. */
|
|
39
|
+
static const u32 tile_bg[8] = {
|
|
40
|
+
0x44444444, 0x45555554, 0x45555554, 0x45555554,
|
|
41
|
+
0x45555554, 0x45555554, 0x45555554, 0x44444444,
|
|
42
|
+
};
|
|
43
|
+
/* A darker, recessed cell drawn behind the play column so the well reads
|
|
44
|
+
* as an inset board rather than part of the surrounding wall. */
|
|
45
|
+
static const u32 tile_well[8] = {
|
|
46
|
+
0x44444444, 0x40000004, 0x40000004, 0x40000004,
|
|
47
|
+
0x40000004, 0x40000004, 0x40000004, 0x44444444,
|
|
48
|
+
};
|
|
34
49
|
static const u32 tile_red[8] = {
|
|
35
50
|
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
36
51
|
0x11111111, 0x11111111, 0x11111111, 0x11111111,
|
|
@@ -91,8 +106,11 @@ static void draw_cell(s16 col, s16 row) {
|
|
|
91
106
|
/* Each grid cell is CELL_PX/8 = 2 tiles square. */
|
|
92
107
|
for (u16 dy = 0; dy < 2; dy++) {
|
|
93
108
|
for (u16 dx = 0; dx < 2; dx++) {
|
|
109
|
+
/* Cells use the EMPTY-or-coloured tile. Empty cells stay
|
|
110
|
+
* transparent so the BG_A well backdrop shows through; filled
|
|
111
|
+
* cells are HIGH priority so they sit above that backdrop. */
|
|
94
112
|
VDP_setTileMapXY(BG_B,
|
|
95
|
-
TILE_ATTR_FULL(pal_for(v), 0, 0, 0, tile_for(v)),
|
|
113
|
+
TILE_ATTR_FULL(pal_for(v), v ? 1 : 0, 0, 0, tile_for(v)),
|
|
96
114
|
col * 2 + dx + 6,
|
|
97
115
|
row * 2 + dy + 1);
|
|
98
116
|
}
|
|
@@ -116,7 +134,7 @@ static void draw_piece(s16 col, s16 row, bool clear) {
|
|
|
116
134
|
for (u16 dy = 0; dy < 2; dy++)
|
|
117
135
|
for (u16 dx = 0; dx < 2; dx++)
|
|
118
136
|
VDP_setTileMapXY(BG_B,
|
|
119
|
-
TILE_ATTR_FULL(pal_for(v), 0, 0, 0, tile_for(v)),
|
|
137
|
+
TILE_ATTR_FULL(pal_for(v), v ? 1 : 0, 0, 0, tile_for(v)),
|
|
120
138
|
col * 2 + dx + 6,
|
|
121
139
|
r * 2 + dy + 1);
|
|
122
140
|
}
|
|
@@ -166,15 +184,31 @@ static void render_score(void) {
|
|
|
166
184
|
int main(bool hard) {
|
|
167
185
|
(void)hard;
|
|
168
186
|
|
|
169
|
-
/* Palette 1: tile colours for red/green/blue cells. */
|
|
187
|
+
/* Palette 1: tile colours for red/green/blue cells + the backdrop. */
|
|
170
188
|
PAL_setColor(16 + 1, 0x000E); /* red */
|
|
171
189
|
PAL_setColor(16 + 2, 0x00E0); /* green */
|
|
172
190
|
PAL_setColor(16 + 3, 0x0E00); /* blue */
|
|
191
|
+
PAL_setColor(16 + 4, 0x0420); /* backdrop wall border */
|
|
192
|
+
PAL_setColor(16 + 5, 0x0610); /* backdrop wall fill */
|
|
173
193
|
|
|
174
194
|
VDP_loadTileData(tile_blank, T_BLANK, 1, DMA);
|
|
175
195
|
VDP_loadTileData(tile_red, T_RED, 1, DMA);
|
|
176
196
|
VDP_loadTileData(tile_green, T_GREEN, 1, DMA);
|
|
177
197
|
VDP_loadTileData(tile_blue, T_BLUE, 1, DMA);
|
|
198
|
+
VDP_loadTileData(tile_bg, T_BG, 1, DMA);
|
|
199
|
+
VDP_loadTileData(tile_well, T_WELL, 1, DMA);
|
|
200
|
+
|
|
201
|
+
/* Far plane (BG_A): tile the whole 40x28 screen with the wall block,
|
|
202
|
+
* then recess the 12x24-cell play column so the grid sits in an inset
|
|
203
|
+
* well. The grid (BG_B) draws over this with HIGH priority. */
|
|
204
|
+
for (u16 cy = 0; cy < 28; cy++)
|
|
205
|
+
for (u16 cx = 0; cx < 40; cx++)
|
|
206
|
+
VDP_setTileMapXY(BG_A,
|
|
207
|
+
TILE_ATTR_FULL(PAL1, 0, 0, 0, T_BG), cx, cy);
|
|
208
|
+
for (u16 cy = 1; cy <= 24; cy++)
|
|
209
|
+
for (u16 cx = 6; cx <= 17; cx++)
|
|
210
|
+
VDP_setTileMapXY(BG_A,
|
|
211
|
+
TILE_ATTR_FULL(PAL1, 0, 0, 0, T_WELL), cx, cy);
|
|
178
212
|
|
|
179
213
|
for (s16 r = 0; r < ROWS; r++)
|
|
180
214
|
for (s16 c = 0; c < COLS; c++)
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
#define T_CAR_EN (TILE_USER_INDEX + 1)
|
|
30
30
|
#define T_LANE (TILE_USER_INDEX + 2) /* dashed lane divider (BG_B) */
|
|
31
31
|
#define T_EDGE (TILE_USER_INDEX + 3) /* solid road edge (BG_B) */
|
|
32
|
+
#define T_GRASS (TILE_USER_INDEX + 4) /* roadside backdrop (BG_A) */
|
|
33
|
+
#define T_ASPHALT (TILE_USER_INDEX + 5) /* road surface backdrop(BG_A) */
|
|
32
34
|
|
|
33
35
|
static const u32 tile_car_p1[8] = {
|
|
34
36
|
0x01111110, 0x11111111, 0x12222221, 0x11111111,
|
|
@@ -51,6 +53,18 @@ static const u32 tile_edge[8] = {
|
|
|
51
53
|
0x00000022, 0x00000022, 0x00000022, 0x00000022,
|
|
52
54
|
0x00000022, 0x00000022, 0x00000022, 0x00000022,
|
|
53
55
|
};
|
|
56
|
+
/* Roadside grass (colour 5) with a couple of darker tufts (colour 6) so
|
|
57
|
+
* it isn't a flat fill — tiled down both shoulders on BG_A. */
|
|
58
|
+
static const u32 tile_grass[8] = {
|
|
59
|
+
0x55555555, 0x55556555, 0x55555555, 0x65555555,
|
|
60
|
+
0x55555555, 0x55555565, 0x55555555, 0x55655555,
|
|
61
|
+
};
|
|
62
|
+
/* Road asphalt (colour 6) with faint speckle (colour 5) — tiled across
|
|
63
|
+
* the driving surface on BG_A, behind the BG_B lane markings + cars. */
|
|
64
|
+
static const u32 tile_asphalt[8] = {
|
|
65
|
+
0x66666666, 0x66666566, 0x66666666, 0x56666666,
|
|
66
|
+
0x66666666, 0x66666656, 0x66666666, 0x66566666,
|
|
67
|
+
};
|
|
54
68
|
|
|
55
69
|
/* The road lives on BG_B (8×8 cells). Two dashed dividers sit between the
|
|
56
70
|
* three lanes; solid edges frame the outermost lanes. */
|
|
@@ -61,15 +75,28 @@ static const u32 tile_edge[8] = {
|
|
|
61
75
|
#define ROAD_EDGE_L ((LANE_LEFT_X - 12) / 8)
|
|
62
76
|
#define ROAD_EDGE_R ((LANE_RIGHT_X + 12) / 8)
|
|
63
77
|
|
|
78
|
+
/* Far plane (BG_A): grass shoulders + asphalt driving surface, tiled
|
|
79
|
+
* across the whole 40x28 screen so the road no longer floats on black.
|
|
80
|
+
* Drawn at low priority; the BG_B markings + sprite cars sit on top. */
|
|
81
|
+
static void draw_backdrop(void) {
|
|
82
|
+
s16 r, c;
|
|
83
|
+
for (r = 0; r < 28; r++)
|
|
84
|
+
for (c = 0; c < 40; c++) {
|
|
85
|
+
u16 t = (c >= ROAD_EDGE_L && c <= ROAD_EDGE_R) ? T_ASPHALT : T_GRASS;
|
|
86
|
+
VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(PAL0, 0, 0, 0, t), c, r);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
64
90
|
static void draw_road(void) {
|
|
65
91
|
s16 r;
|
|
66
92
|
for (r = ROAD_TOP_ROW; r <= ROAD_BOT_ROW; r++) {
|
|
67
|
-
/* Left edge (stripe on its right), right edge (hflipped).
|
|
68
|
-
|
|
69
|
-
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL0,
|
|
93
|
+
/* Left edge (stripe on its right), right edge (hflipped). HIGH
|
|
94
|
+
* priority so the markings sit above the BG_A asphalt backdrop. */
|
|
95
|
+
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL0, 1, 0, 0, T_EDGE), ROAD_EDGE_L, r);
|
|
96
|
+
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL0, 1, 0, 1, T_EDGE), ROAD_EDGE_R, r);
|
|
70
97
|
/* Two dashed lane dividers. */
|
|
71
|
-
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL0,
|
|
72
|
-
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL0,
|
|
98
|
+
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL0, 1, 0, 0, T_LANE), LANE_DIV1_COL, r);
|
|
99
|
+
VDP_setTileMapXY(BG_B, TILE_ATTR_FULL(PAL0, 1, 0, 0, T_LANE), LANE_DIV2_COL, r);
|
|
73
100
|
}
|
|
74
101
|
}
|
|
75
102
|
|
|
@@ -125,18 +152,24 @@ static void render_score(void) {
|
|
|
125
152
|
int main(bool hard) {
|
|
126
153
|
(void)hard;
|
|
127
154
|
|
|
128
|
-
/* PAL0 = player (white + blue trim). PAL1 = enemy
|
|
155
|
+
/* PAL0 = player (white + blue trim) + road backdrop. PAL1 = enemy. */
|
|
129
156
|
PAL_setColor(0 + 1, 0x0EEE); /* white body */
|
|
130
157
|
PAL_setColor(0 + 2, 0x0AAA); /* roof grey */
|
|
158
|
+
PAL_setColor(0 + 5, 0x0260); /* roadside grass */
|
|
159
|
+
PAL_setColor(0 + 6, 0x0222); /* asphalt grey */
|
|
131
160
|
PAL_setColor(16 + 3, 0x000E); /* enemy red */
|
|
132
161
|
PAL_setColor(16 + 4, 0x0666); /* enemy roof */
|
|
133
162
|
|
|
134
|
-
VDP_loadTileData(tile_car_p1, T_CAR_P1,
|
|
135
|
-
VDP_loadTileData(tile_car_enemy, T_CAR_EN,
|
|
136
|
-
VDP_loadTileData(tile_lane, T_LANE,
|
|
137
|
-
VDP_loadTileData(tile_edge, T_EDGE,
|
|
163
|
+
VDP_loadTileData(tile_car_p1, T_CAR_P1, 1, DMA);
|
|
164
|
+
VDP_loadTileData(tile_car_enemy, T_CAR_EN, 1, DMA);
|
|
165
|
+
VDP_loadTileData(tile_lane, T_LANE, 1, DMA);
|
|
166
|
+
VDP_loadTileData(tile_edge, T_EDGE, 1, DMA);
|
|
167
|
+
VDP_loadTileData(tile_grass, T_GRASS, 1, DMA);
|
|
168
|
+
VDP_loadTileData(tile_asphalt, T_ASPHALT, 1, DMA);
|
|
138
169
|
|
|
139
|
-
/* Draw the
|
|
170
|
+
/* Draw the grass+asphalt backdrop (BG_A) then the road markings on
|
|
171
|
+
* BG_B over it. */
|
|
172
|
+
draw_backdrop();
|
|
140
173
|
draw_road();
|
|
141
174
|
|
|
142
175
|
VDP_drawText("SCORE", 28, 2);
|
|
@@ -21,6 +21,23 @@
|
|
|
21
21
|
|
|
22
22
|
#include <genesis.h>
|
|
23
23
|
|
|
24
|
+
/* Two simple 8x8 background tiles for the far plane (BG_B). Tiling them
|
|
25
|
+
* in a checkerboard fills the whole screen so it doesn't read as a blank
|
|
26
|
+
* black backdrop. T_BG_A is a framed block (colour 1 border, colour 2
|
|
27
|
+
* fill); T_BG_B is the same block in colour 3 — alternating them gives a
|
|
28
|
+
* two-tone grid with a clear majority of non-backdrop pixels. */
|
|
29
|
+
#define T_BG_A (TILE_USER_INDEX + 0)
|
|
30
|
+
#define T_BG_B (TILE_USER_INDEX + 1)
|
|
31
|
+
|
|
32
|
+
static const u32 tile_bg_a[8] = {
|
|
33
|
+
0x11111111, 0x12222221, 0x12222221, 0x12222221,
|
|
34
|
+
0x12222221, 0x12222221, 0x12222221, 0x11111111,
|
|
35
|
+
};
|
|
36
|
+
static const u32 tile_bg_b[8] = {
|
|
37
|
+
0x11111111, 0x13333331, 0x13333331, 0x13333331,
|
|
38
|
+
0x13333331, 0x13333331, 0x13333331, 0x11111111,
|
|
39
|
+
};
|
|
40
|
+
|
|
24
41
|
int main(bool hard) {
|
|
25
42
|
/* Boot info: hard == TRUE on power-on, FALSE on soft reset (we
|
|
26
43
|
* could re-init differently in each case; this minimum starter
|
|
@@ -28,7 +45,23 @@ int main(bool hard) {
|
|
|
28
45
|
(void)hard;
|
|
29
46
|
|
|
30
47
|
/* SGDK initialized the VDP + default palette in sega.s + libmd
|
|
31
|
-
* before main() ran.
|
|
48
|
+
* before main() ran. We add a tiled background so the screen isn't a
|
|
49
|
+
* flat black backdrop, then draw the text on top. */
|
|
50
|
+
PAL_setColor(16 + 1, 0x0444); /* dark grey grid border */
|
|
51
|
+
PAL_setColor(16 + 2, 0x0A22); /* deep blue fill */
|
|
52
|
+
PAL_setColor(16 + 3, 0x022A); /* deep red fill (2nd block) */
|
|
53
|
+
|
|
54
|
+
VDP_loadTileData(tile_bg_a, T_BG_A, 1, DMA);
|
|
55
|
+
VDP_loadTileData(tile_bg_b, T_BG_B, 1, DMA);
|
|
56
|
+
|
|
57
|
+
/* Fill the far plane (BG_B) with a checkerboard of the two blocks so
|
|
58
|
+
* a clear majority of the visible 40x28 cells are non-backdrop. */
|
|
59
|
+
for (u16 cy = 0; cy < 28; cy++)
|
|
60
|
+
for (u16 cx = 0; cx < 40; cx++)
|
|
61
|
+
VDP_setTileMapXY(BG_B,
|
|
62
|
+
TILE_ATTR_FULL(PAL1, 0, 0, 0, ((cx ^ cy) & 1) ? T_BG_A : T_BG_B),
|
|
63
|
+
cx, cy);
|
|
64
|
+
|
|
32
65
|
VDP_drawText("HELLO SEGA GENESIS", 10, 12);
|
|
33
66
|
VDP_drawText("BUILT WITH ROM-DEV-MCP", 8, 14);
|
|
34
67
|
|