romdevtools 0.16.0 → 0.21.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 +60 -12
- package/CHANGELOG.md +258 -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/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/gb/templates/default.c +110 -16
- package/examples/gb/templates/platformer.c +25 -4
- package/examples/gb/templates/puzzle.c +32 -2
- package/examples/gb/templates/racing.c +72 -8
- package/examples/gb/templates/shmup.c +38 -1
- package/examples/gb/templates/sports.c +48 -1
- package/examples/gba/templates/gba_hello.c +29 -11
- 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/gbc/templates/default.c +103 -26
- package/examples/gbc/templates/platformer.c +25 -4
- package/examples/gbc/templates/puzzle.c +32 -2
- package/examples/gbc/templates/racing.c +85 -19
- package/examples/gbc/templates/shmup.c +34 -1
- package/examples/gbc/templates/sports.c +45 -1
- 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/gg/templates/default.c +56 -18
- package/examples/gg/templates/platformer.c +18 -12
- package/examples/gg/templates/puzzle.c +38 -7
- package/examples/gg/templates/racing.c +51 -5
- package/examples/gg/templates/shmup.c +47 -3
- package/examples/gg/templates/sports.c +46 -3
- package/examples/lynx/templates/default.c +39 -8
- 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/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/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/sms/main.c +35 -6
- 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/sports.c +43 -2
- package/examples/snes/templates/default.c +50 -28
- package/examples/snes/templates/platformer-data.asm +22 -0
- package/examples/snes/templates/platformer.c +16 -1
- package/examples/snes/templates/puzzle-data.asm +22 -0
- package/examples/snes/templates/puzzle.c +17 -1
- 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/cores/wasm/vice_x64_libretro.js +1 -1
- package/src/cores/wasm/vice_x64_libretro.wasm +0 -0
- package/src/host/LibretroHost.js +122 -1
- package/src/host/callbacks.js +9 -1
- package/src/host/types.js +15 -8
- package/src/http/tool-registry.js +26 -1
- package/src/mcp/tools/cart-parts.js +75 -3
- package/src/mcp/tools/disasm-rebuild.js +507 -0
- package/src/mcp/tools/disasm.js +95 -6
- package/src/mcp/tools/frame.js +168 -3
- package/src/mcp/tools/lifecycle.js +4 -2
- package/src/mcp/tools/project.js +54 -9
- package/src/mcp/tools/state.js +201 -14
- package/src/mcp/tools/toolchain.js +76 -3
- package/src/mcp/tools/watch-memory.js +125 -14
- package/src/platforms/c64/MENTAL_MODEL.md +45 -1
- package/src/platforms/c64/d64.js +281 -0
- package/src/platforms/gb/MENTAL_MODEL.md +10 -0
- package/src/platforms/msx/MENTAL_MODEL.md +10 -6
- package/src/platforms/nes/MENTAL_MODEL.md +63 -2
- package/src/platforms/pce/MENTAL_MODEL.md +9 -4
- package/src/platforms/pce/lib/c/pce_video.c +1 -1
- package/src/rom-id/identifier.js +15 -0
- package/src/toolchains/cc65/ines.js +145 -0
- 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 -2
|
@@ -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);
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/* ── default.c — minimal Game Boy (DMG) starter ───────────────────
|
|
2
2
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* A "hello, it works!" screen: a tiled background (a dithered field with
|
|
4
|
+
* two bands + a centre box) plus a sprite that bounces around. The very
|
|
5
|
+
* first build shows recognizable content — not a flat colour. The DMG
|
|
6
|
+
* background palette (BGP, $FF47) also cycles through 4 shade
|
|
7
|
+
* arrangements so you can SEE the palette path is alive. Use this as the
|
|
8
|
+
* starting point when you're not yet sure what you want to build.
|
|
7
9
|
*
|
|
8
10
|
* GB-specific notes for the agent:
|
|
11
|
+
* - You MUST put tiles in VRAM *and* enable the BG (LCDC bit 0) or the
|
|
12
|
+
* screen stays one flat colour — the #1 GB "why is it blank" footgun.
|
|
13
|
+
* We upload tiles to $8000 and select LCDC_TILE_DATA_LO (unsigned
|
|
14
|
+
* $8000 addressing) so tile index N lives at $8000 + N*16.
|
|
9
15
|
* - DMG uses the BGP/OBP0/OBP1 registers — NOT the CGB BCPS/BCPD
|
|
10
16
|
* palette RAM. The GBC tree's default uses BCPS; don't copy that
|
|
11
17
|
* into a DMG project or your screen will stay one shade.
|
|
@@ -28,31 +34,119 @@
|
|
|
28
34
|
#include "gb_hardware.h"
|
|
29
35
|
#include "gb_runtime.h"
|
|
30
36
|
|
|
31
|
-
/*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
+
/* Six 8×8 tiles, 2bpp (16 bytes each: row N = byte 2N low-plane, 2N+1
|
|
38
|
+
* high-plane). The colour index per pixel (0..3) selects a shade through
|
|
39
|
+
* BGP. We spread indices 1, 2 and 3 across the screen spatially so no
|
|
40
|
+
* single shade ever fills the frame — regardless of the BGP arrangement.
|
|
41
|
+
* tile 0 — blank (all index 0)
|
|
42
|
+
* tile 1 — solid index 1 (top band)
|
|
43
|
+
* tile 2 — solid index 2 (bottom band)
|
|
44
|
+
* tile 3 — dither idx1/idx2 (the textured backdrop — mixes two shades
|
|
45
|
+
* inside every cell so the field is never flat)
|
|
46
|
+
* tile 4 — solid index 3 (centre box + border)
|
|
47
|
+
* tile 5 — sprite diamond (index 3) */
|
|
48
|
+
static const uint8_t tiles[6 * 16] = {
|
|
49
|
+
/* 0: blank */
|
|
50
|
+
0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
|
|
51
|
+
/* 1: solid index 1 (low plane on, high plane off) */
|
|
52
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
53
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
54
|
+
/* 2: solid index 2 (low plane off, high plane on) */
|
|
55
|
+
0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF,
|
|
56
|
+
0x00,0xFF, 0x00,0xFF, 0x00,0xFF, 0x00,0xFF,
|
|
57
|
+
/* 3: dither — checkerboard of index 1 and index 2 */
|
|
58
|
+
0x55,0xAA, 0xAA,0x55, 0x55,0xAA, 0xAA,0x55,
|
|
59
|
+
0x55,0xAA, 0xAA,0x55, 0x55,0xAA, 0xAA,0x55,
|
|
60
|
+
/* 4: solid index 3 (both planes on) */
|
|
61
|
+
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
62
|
+
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
63
|
+
/* 5: diamond in index 3 (both planes set on the diamond pixels) */
|
|
64
|
+
0x18,0x18, 0x3C,0x3C, 0x7E,0x7E, 0xFF,0xFF,
|
|
65
|
+
0xFF,0xFF, 0x7E,0x7E, 0x3C,0x3C, 0x18,0x18,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
#define T_BLANK 0
|
|
69
|
+
#define T_BAND1 1
|
|
70
|
+
#define T_BAND2 2
|
|
71
|
+
#define T_FIELD 3
|
|
72
|
+
#define T_BOX 4
|
|
73
|
+
#define T_SPRITE 5
|
|
74
|
+
|
|
75
|
+
/* Four BGP arrangements; each byte packs 4 colour indices, 2 bits each:
|
|
76
|
+
* bits 7-6 = shade for index 3, bits 5-4 = index 2,
|
|
77
|
+
* bits 3-2 = index 1, bits 1-0 = index 0.
|
|
78
|
+
* Shade: 0 = white, 1 = light grey, 2 = dark grey, 3 = black.
|
|
79
|
+
* Every entry keeps index 1 != index 2 so the dithered field always shows
|
|
80
|
+
* two distinct shades (the screen is never one flat colour, even mid-cycle). */
|
|
37
81
|
static const uint8_t bgp_shades[4] = {
|
|
38
|
-
0xE4, /* normal:
|
|
39
|
-
0x90, /*
|
|
40
|
-
0x39, /* shifted:
|
|
41
|
-
|
|
82
|
+
0xE4, /* normal: 3=black 2=dark 1=light 0=white */
|
|
83
|
+
0x90, /* dim: 3=dark 2=light 1=white 0=white */
|
|
84
|
+
0x39, /* shifted: 3=white 2=dark 1=dark 0=light */
|
|
85
|
+
0x1B, /* inverted: 3=white 2=light 1=dark 0=black */
|
|
42
86
|
};
|
|
43
87
|
|
|
88
|
+
static void upload_tiles(void) {
|
|
89
|
+
memcpy_vram((void *)0x8000, tiles, sizeof(tiles));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Paint the BG map (32×32; we fill the visible 20×18). A dithered field
|
|
93
|
+
* everywhere, two solid bands, a solid border and a centre box, so the
|
|
94
|
+
* screen reads as real content rather than a flat shade. */
|
|
95
|
+
static void draw_backdrop(void) {
|
|
96
|
+
uint8_t *bg = BG_MAP_0; /* $9800 */
|
|
97
|
+
uint8_t x, y;
|
|
98
|
+
for (y = 0; y < 18; y++)
|
|
99
|
+
for (x = 0; x < 20; x++)
|
|
100
|
+
bg[y * 32 + x] = T_FIELD;
|
|
101
|
+
for (x = 0; x < 20; x++) {
|
|
102
|
+
bg[0 * 32 + x] = T_BOX; /* top border */
|
|
103
|
+
bg[17 * 32 + x] = T_BOX; /* bottom border */
|
|
104
|
+
bg[3 * 32 + x] = T_BAND1; /* upper band */
|
|
105
|
+
bg[14 * 32 + x] = T_BAND2; /* lower band */
|
|
106
|
+
}
|
|
107
|
+
for (y = 0; y < 18; y++) {
|
|
108
|
+
bg[y * 32 + 0] = T_BOX; /* left border */
|
|
109
|
+
bg[y * 32 + 19] = T_BOX; /* right border */
|
|
110
|
+
}
|
|
111
|
+
for (y = 7; y < 11; y++)
|
|
112
|
+
for (x = 7; x < 13; x++)
|
|
113
|
+
bg[y * 32 + x] = T_BOX; /* centre box */
|
|
114
|
+
}
|
|
115
|
+
|
|
44
116
|
void main(void) {
|
|
45
117
|
uint8_t shade = 0;
|
|
46
118
|
uint16_t frame = 0;
|
|
119
|
+
uint8_t sx = 76, sy = 64; /* sprite screen position */
|
|
120
|
+
int8_t dx = 1, dy = 1; /* sprite velocity */
|
|
121
|
+
|
|
122
|
+
lcd_init_default(); /* LCD on, BGP=0xE4, BG+OBJ enabled */
|
|
123
|
+
LCDC = 0;
|
|
124
|
+
|
|
125
|
+
upload_tiles();
|
|
126
|
+
BGP = bgp_shades[0];
|
|
127
|
+
draw_backdrop();
|
|
47
128
|
|
|
48
|
-
|
|
129
|
+
oam_clear();
|
|
130
|
+
oam_set(0, (uint8_t)(sy + 16), (uint8_t)(sx + 8), T_SPRITE, 0);
|
|
131
|
+
|
|
132
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
49
133
|
|
|
50
134
|
for (;;) {
|
|
51
135
|
wait_vblank();
|
|
136
|
+
oam_dma_flush();
|
|
137
|
+
|
|
52
138
|
frame++;
|
|
53
|
-
if ((frame & 0x1F) == 0) { /* every 32 frames */
|
|
139
|
+
if ((frame & 0x1F) == 0) { /* every 32 frames: cycle BGP */
|
|
54
140
|
shade = (uint8_t)((shade + 1) & 0x03);
|
|
55
141
|
BGP = bgp_shades[shade];
|
|
56
142
|
}
|
|
143
|
+
|
|
144
|
+
/* Bounce the sprite around the 160×144 visible area. */
|
|
145
|
+
sx = (uint8_t)(sx + dx);
|
|
146
|
+
sy = (uint8_t)(sy + dy);
|
|
147
|
+
if (sx < 1 || sx > 152) dx = (int8_t)-dx;
|
|
148
|
+
if (sy < 1 || sy > 136) dy = (int8_t)-dy;
|
|
149
|
+
oam_clear();
|
|
150
|
+
oam_set(0, (uint8_t)(sy + 16), (uint8_t)(sx + 8), T_SPRITE, 0);
|
|
57
151
|
}
|
|
58
152
|
}
|
|
@@ -25,9 +25,26 @@ static const uint8_t tile_platform[16] = {
|
|
|
25
25
|
0xFF,0xFF, 0x80,0x80, 0x80,0x80, 0x80,0x80,
|
|
26
26
|
0x80,0x80, 0x80,0x80, 0x80,0x80, 0xFF,0xFF,
|
|
27
27
|
};
|
|
28
|
+
/* ── Backdrop tiles ───────────────────────────────────────────────────
|
|
29
|
+
* Fill the whole world so the screen is never one flat colour (the #1 GB
|
|
30
|
+
* "why is it blank" footgun). tile_sky is a sparse dot pattern over the
|
|
31
|
+
* sky; tile_ground is a textured dirt fill under the floor line. */
|
|
32
|
+
static const uint8_t tile_sky[16] = {
|
|
33
|
+
0x00,0x00, 0x00,0x00, 0x00,0x00, 0x20,0x20,
|
|
34
|
+
0x00,0x00, 0x00,0x00, 0x02,0x02, 0x00,0x00,
|
|
35
|
+
};
|
|
36
|
+
static const uint8_t tile_ground[16] = {
|
|
37
|
+
0xFF,0x00, 0xDB,0x24, 0xFF,0x00, 0x6D,0x92,
|
|
38
|
+
0xFF,0x00, 0xDB,0x24, 0xFF,0x00, 0x6D,0x92,
|
|
39
|
+
};
|
|
40
|
+
#define T_BLANK 0
|
|
41
|
+
#define T_PLATFORM 2
|
|
42
|
+
#define T_SKY 3
|
|
43
|
+
#define T_GROUND 4
|
|
28
44
|
|
|
29
45
|
static const uint16_t obj_palette[4] = { 0x7FFF, 0x001F, 0x03E0, 0x7C00 };
|
|
30
|
-
|
|
46
|
+
/* BG palette: 0 sky-blue, 1 mid, 2 dirt-dark, 3 near-black detail. */
|
|
47
|
+
static const uint16_t bg_palette[4] = { 0x7E10, 0x5294, 0x114A, 0x0000 };
|
|
31
48
|
|
|
32
49
|
typedef struct { int16_t x, y, w, h; } Rect;
|
|
33
50
|
|
|
@@ -71,8 +88,10 @@ static void paint_platforms(void) {
|
|
|
71
88
|
const Rect *p;
|
|
72
89
|
/* k MUST be uint16_t: 32*18 = 576 > 255, so a uint8_t counter would
|
|
73
90
|
* never reach the bound and this loop would spin forever (the BG map
|
|
74
|
-
* never clears, main() never starts). Classic SDCC limited-range trap.
|
|
75
|
-
|
|
91
|
+
* never clears, main() never starts). Classic SDCC limited-range trap.
|
|
92
|
+
* Fill sky above the floor line (row 16 = y 128) and textured ground
|
|
93
|
+
* at and below it, so the whole world is a real scene, not blank. */
|
|
94
|
+
for (k = 0; k < 32 * 18; k++) map[k] = (k >= 16 * 32) ? T_GROUND : T_SKY;
|
|
76
95
|
for (i = 0; i < N_PLATFORMS; i++) {
|
|
77
96
|
p = &platforms[i];
|
|
78
97
|
cx = p->x >> 3;
|
|
@@ -81,7 +100,7 @@ static void paint_platforms(void) {
|
|
|
81
100
|
ch = (p->h + 7) >> 3;
|
|
82
101
|
for (j = 0; j < cw; j++) {
|
|
83
102
|
if (cx + j < 32 && cy < 32)
|
|
84
|
-
map[cy * 32 + cx + j] =
|
|
103
|
+
map[cy * 32 + cx + j] = T_PLATFORM; /* platform top edge */
|
|
85
104
|
}
|
|
86
105
|
}
|
|
87
106
|
}
|
|
@@ -108,6 +127,8 @@ void main(void) {
|
|
|
108
127
|
upload_tile(0, tile_blank);
|
|
109
128
|
upload_tile(1, tile_player);
|
|
110
129
|
upload_tile(2, tile_platform);
|
|
130
|
+
upload_tile(T_SKY, tile_sky);
|
|
131
|
+
upload_tile(T_GROUND, tile_ground);
|
|
111
132
|
|
|
112
133
|
OCPS = 0x80;
|
|
113
134
|
for (i = 0; i < 4; i++) {
|
|
@@ -21,8 +21,22 @@
|
|
|
21
21
|
#define T_R 1
|
|
22
22
|
#define T_G 2
|
|
23
23
|
#define T_B 3
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
#define T_WALL 4
|
|
25
|
+
|
|
26
|
+
/* tile_blank is the EMPTY-cell / backdrop tile. It is NOT all-zero: a
|
|
27
|
+
* subtle dither (colour 0 + faint colour 1) so the empty playfield and the
|
|
28
|
+
* area around the well read as a textured surface, never one flat colour
|
|
29
|
+
* (the #1 GB "why is it blank" footgun). Locked blocks / the active piece
|
|
30
|
+
* overdraw it with the R/G/B shape tiles. */
|
|
31
|
+
static const uint8_t tile_blank[16] = {
|
|
32
|
+
0x00,0x00, 0x22,0x00, 0x00,0x00, 0x88,0x00,
|
|
33
|
+
0x00,0x00, 0x22,0x00, 0x00,0x00, 0x88,0x00,
|
|
34
|
+
};
|
|
35
|
+
/* Well frame: a solid colour-2 border drawn around the play area. */
|
|
36
|
+
static const uint8_t tile_wall[16] = {
|
|
37
|
+
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
38
|
+
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
39
|
+
};
|
|
26
40
|
/* Three distinct tile shapes (since GB BG is 2bpp, we differentiate
|
|
27
41
|
* by *shape*, not colour-on-CGB). The CGB palette path could give us
|
|
28
42
|
* real colours; for DMG-compatibility we use shape. */
|
|
@@ -131,6 +145,20 @@ static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
|
131
145
|
for (i = 0; i < 16; i++) dst[i] = src[i];
|
|
132
146
|
}
|
|
133
147
|
|
|
148
|
+
/* Draw the well frame around the 6×12 play area. Grid cells live at
|
|
149
|
+
* map[(row+1)*32 + (col+7)] (rows 1..12, cols 7..12), so the frame is the
|
|
150
|
+
* column to each side (6 and 13) and the floor row just below (row 13). */
|
|
151
|
+
static void draw_well(void) {
|
|
152
|
+
uint8_t *map = (uint8_t *)0x9800;
|
|
153
|
+
uint8_t r;
|
|
154
|
+
for (r = 1; r <= 12; r++) {
|
|
155
|
+
map[r * 32 + 6] = T_WALL; /* left wall */
|
|
156
|
+
map[r * 32 + 13] = T_WALL; /* right wall */
|
|
157
|
+
}
|
|
158
|
+
for (r = 6; r <= 13; r++)
|
|
159
|
+
map[13 * 32 + r] = T_WALL; /* floor */
|
|
160
|
+
}
|
|
161
|
+
|
|
134
162
|
void main(void) {
|
|
135
163
|
uint8_t pad, prev = 0, fall_rate, t;
|
|
136
164
|
int16_t r, c;
|
|
@@ -145,6 +173,7 @@ void main(void) {
|
|
|
145
173
|
upload_tile(T_R, tile_r);
|
|
146
174
|
upload_tile(T_G, tile_g);
|
|
147
175
|
upload_tile(T_B, tile_b);
|
|
176
|
+
upload_tile(T_WALL, tile_wall);
|
|
148
177
|
|
|
149
178
|
BCPS = 0x80;
|
|
150
179
|
for (i = 0; i < 4; i++) {
|
|
@@ -165,6 +194,7 @@ void main(void) {
|
|
|
165
194
|
score = 0;
|
|
166
195
|
fall_timer = 0;
|
|
167
196
|
new_piece();
|
|
197
|
+
draw_well();
|
|
168
198
|
draw_grid();
|
|
169
199
|
|
|
170
200
|
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_TILE_DATA_LO;
|