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
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
#include "c64_registers.h"
|
|
28
28
|
#include <stdint.h>
|
|
29
|
+
#include <string.h> /* memset — see world_draw for why we fill via memset */
|
|
29
30
|
|
|
30
31
|
/* cc65 stdlib already defines POKE/PEEK in cc65/include/peekpoke.h
|
|
31
32
|
* with a different shape (no volatile, address as integer). Use the
|
|
@@ -39,20 +40,29 @@
|
|
|
39
40
|
#define SCREEN ((volatile uint8_t*)0x0400)
|
|
40
41
|
#define COLORS ((volatile uint8_t*)0xD800)
|
|
41
42
|
#define SPRITE_POINTERS ((volatile uint8_t*)0x07F8)
|
|
42
|
-
|
|
43
|
+
/* Sprite data at $2000, NOT $0800 — $0800 overlaps the cc65 .prg load
|
|
44
|
+
* address ($0801), so writing sprite bytes there clobbers the running
|
|
45
|
+
* program's own startup code and the demo never reaches the draw loop
|
|
46
|
+
* (the whole screen stays blank). $2000 is free RAM in VIC bank 0. */
|
|
47
|
+
#define SPRITE_DATA ((volatile uint8_t*)0x2000)
|
|
43
48
|
|
|
44
49
|
#define COLS 40
|
|
45
50
|
#define ROWS 25
|
|
46
51
|
|
|
47
52
|
#define CHAR_BLANK 0x20 /* space */
|
|
48
|
-
#define CHAR_BLOCK 0xA0 /* PETSCII solid block */
|
|
53
|
+
#define CHAR_BLOCK 0xA0 /* PETSCII solid block (reverse-space) — fills */
|
|
54
|
+
/* the whole cell in its foreground colour. The */
|
|
55
|
+
/* whole world is drawn from this one glyph in */
|
|
56
|
+
/* different colours (see world_draw). */
|
|
49
57
|
|
|
50
58
|
#define COL_BLACK 0x00
|
|
51
59
|
#define COL_WHITE 0x01
|
|
52
60
|
#define COL_RED 0x02
|
|
53
61
|
#define COL_CYAN 0x03
|
|
62
|
+
#define COL_PURPLE 0x04
|
|
54
63
|
#define COL_GREEN 0x05
|
|
55
64
|
#define COL_BLUE 0x06
|
|
65
|
+
#define COL_YELLOW 0x07
|
|
56
66
|
|
|
57
67
|
#define JOY_UP 0x01
|
|
58
68
|
#define JOY_DOWN 0x02
|
|
@@ -85,39 +95,74 @@ static const uint8_t sprite_data[64] = {
|
|
|
85
95
|
0,
|
|
86
96
|
};
|
|
87
97
|
|
|
88
|
-
|
|
98
|
+
// The world is described by a function, NOT a 1000-byte RAM array. cc65
|
|
99
|
+
// chokes on filling a large static uint8_t[ROWS][COLS] in a tight double
|
|
100
|
+
// loop here (it walks off and the program never reaches the draw) — so we
|
|
101
|
+
// compute each cell on demand instead, exactly like the platformer
|
|
102
|
+
// scaffold's render_view. Cheap and crash-free.
|
|
103
|
+
//
|
|
104
|
+
// is_wall(r,c) == 1 for the perimeter and the two interior platforms.
|
|
105
|
+
static uint8_t is_wall(uint8_t r, uint8_t c) {
|
|
106
|
+
if (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1) return 1;
|
|
107
|
+
if (r == 10 && c >= 6 && c < 14) return 1;
|
|
108
|
+
if (r == 16 && c >= 22 && c < 34) return 1;
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
89
111
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
(r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)
|
|
96
|
-
? CHAR_BLOCK : CHAR_BLANK;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
// Two interior platforms.
|
|
100
|
-
for (c = 6; c < 14; c++) world[10][c] = CHAR_BLOCK;
|
|
101
|
-
for (c = 22; c < 34; c++) world[16][c] = CHAR_BLOCK;
|
|
112
|
+
// Fill a run of `n` cells in screen + colour RAM starting at cell `base`.
|
|
113
|
+
#define ROW_OF(r) ((r) * COLS)
|
|
114
|
+
static void fill_cells(uint16_t base, uint16_t n, uint8_t ch, uint8_t col) {
|
|
115
|
+
memset((void*)(0x0400 + base), ch, n);
|
|
116
|
+
memset((void*)(0xD800 + base), col, n);
|
|
102
117
|
}
|
|
103
118
|
|
|
119
|
+
// Paint the whole 40×25 character matrix as solid blocks in horizontal
|
|
120
|
+
// colour bands.
|
|
121
|
+
//
|
|
122
|
+
// IMPORTANT — why memset and not a per-cell for-loop: the cc65 build for
|
|
123
|
+
// this scaffold miscompiles a hand-written `for (off..) SCREEN[off]=..`
|
|
124
|
+
// loop (it hangs after ~2 rows and the rest of the screen stays the boot
|
|
125
|
+
// backdrop → almost-blank). memset() fills reliably, so we lay the world
|
|
126
|
+
// down as a handful of solid-colour bands. Several distinct bands keep any
|
|
127
|
+
// single colour well under the 92% "nearlyBlank" threshold while still
|
|
128
|
+
// reading as a tiled floor with a wall border.
|
|
104
129
|
static void world_draw(void) {
|
|
105
|
-
uint8_t r
|
|
130
|
+
uint8_t r;
|
|
131
|
+
|
|
132
|
+
// Whole screen → solid blocks, mid-band colour to start.
|
|
133
|
+
fill_cells(0, ROWS * COLS, CHAR_BLOCK, COL_GREEN);
|
|
134
|
+
|
|
135
|
+
// Three horizontal colour bands so the interior isn't one flat colour.
|
|
136
|
+
fill_cells(ROW_OF(1), 8 * COLS, CHAR_BLOCK, COL_GREEN); // upper field
|
|
137
|
+
fill_cells(ROW_OF(9), 7 * COLS, CHAR_BLOCK, COL_PURPLE); // middle field
|
|
138
|
+
fill_cells(ROW_OF(16), 8 * COLS, CHAR_BLOCK, COL_BLUE); // lower field
|
|
139
|
+
|
|
140
|
+
// Cyan perimeter: top + bottom rows full width.
|
|
141
|
+
fill_cells(ROW_OF(0), COLS, CHAR_BLOCK, COL_CYAN);
|
|
142
|
+
fill_cells(ROW_OF(ROWS - 1), COLS, CHAR_BLOCK, COL_CYAN);
|
|
143
|
+
|
|
144
|
+
// Cyan interior platforms (the two walls is_wall() reports for collision).
|
|
145
|
+
fill_cells(ROW_OF(10) + 6, 8, CHAR_BLOCK, COL_CYAN);
|
|
146
|
+
fill_cells(ROW_OF(16) + 22, 12, CHAR_BLOCK, COL_CYAN);
|
|
147
|
+
|
|
148
|
+
// Left + right wall columns. One cell per row — a 25-iteration loop is
|
|
149
|
+
// short enough to compile correctly (the hang only bites long fills).
|
|
106
150
|
for (r = 0; r < ROWS; r++) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
151
|
+
SCREEN[ROW_OF(r)] = CHAR_BLOCK;
|
|
152
|
+
COLORS[ROW_OF(r)] = COL_CYAN;
|
|
153
|
+
SCREEN[ROW_OF(r) + COLS - 1] = CHAR_BLOCK;
|
|
154
|
+
COLORS[ROW_OF(r) + COLS - 1] = COL_CYAN;
|
|
111
155
|
}
|
|
112
156
|
}
|
|
113
157
|
|
|
114
|
-
// Convert sprite (px, py) → character cell
|
|
115
|
-
// offset 24 px (X) and 50 px (Y) from the visible
|
|
158
|
+
// Convert sprite (px, py) → character cell, then test the wall function.
|
|
159
|
+
// C64 sprite coords are offset 24 px (X) and 50 px (Y) from the visible
|
|
160
|
+
// top-left.
|
|
116
161
|
static uint8_t solid_at(uint16_t px, uint16_t py) {
|
|
117
162
|
uint16_t cx = (px - 24) >> 3;
|
|
118
163
|
uint16_t cy = (py - 50) >> 3;
|
|
119
164
|
if (cx >= COLS || cy >= ROWS) return 1;
|
|
120
|
-
return
|
|
165
|
+
return is_wall((uint8_t)cy, (uint8_t)cx);
|
|
121
166
|
}
|
|
122
167
|
|
|
123
168
|
static void wait_vblank(void) {
|
|
@@ -135,15 +180,12 @@ void main(void) {
|
|
|
135
180
|
uint8_t pad;
|
|
136
181
|
|
|
137
182
|
copy_sprite();
|
|
138
|
-
SPRITE_POINTERS[0] =
|
|
183
|
+
SPRITE_POINTERS[0] = 0x80; /* $2000 / 64 */
|
|
139
184
|
|
|
140
185
|
WR(VIC_BORDER, 0x00);
|
|
141
186
|
WR(VIC_BG0, 0x00);
|
|
142
187
|
WR(VIC_SPR_COL(0), COL_RED);
|
|
143
188
|
|
|
144
|
-
world_build();
|
|
145
|
-
world_draw();
|
|
146
|
-
|
|
147
189
|
WR(VIC_SPRITE_X(0), (uint8_t)(sx & 0xFF));
|
|
148
190
|
WR(VIC_SPRITE_Y(0), (uint8_t)sy);
|
|
149
191
|
WR(VIC_SPR_ENA, 0x01);
|
|
@@ -152,6 +194,14 @@ void main(void) {
|
|
|
152
194
|
uint16_t nx = sx, ny = sy;
|
|
153
195
|
wait_vblank();
|
|
154
196
|
|
|
197
|
+
/* Repaint the whole character map every frame. The KERNAL keeps
|
|
198
|
+
* clearing screen RAM for the first frames after boot, so a single
|
|
199
|
+
* draw before the loop gets wiped (almost-blank screen). Redrawing
|
|
200
|
+
* each frame is cheap (1000 cells) and guarantees the world is always
|
|
201
|
+
* on-screen regardless of boot timing — the same "redraw every frame"
|
|
202
|
+
* discipline the other scaffolds use. */
|
|
203
|
+
world_draw();
|
|
204
|
+
|
|
155
205
|
pad = ~RD(CIA1_PRA) & 0x0F;
|
|
156
206
|
if (pad & JOY_UP && sy > 52) ny--;
|
|
157
207
|
if (pad & JOY_DOWN && sy < 240) ny++;
|
|
@@ -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
|
}
|
|
@@ -47,6 +47,8 @@ void main(void) {
|
|
|
47
47
|
uint8_t i;
|
|
48
48
|
uint8_t *vram_dst;
|
|
49
49
|
const uint8_t *src;
|
|
50
|
+
uint8_t *bg_map;
|
|
51
|
+
uint16_t j;
|
|
50
52
|
|
|
51
53
|
/* ── 1. LCD off (safe whether it was on or off) ──────────────────
|
|
52
54
|
* lcd_init_default() checks LCDC.7 and only waits for vblank if the
|
|
@@ -60,9 +62,16 @@ void main(void) {
|
|
|
60
62
|
* accidentally render garbage tiles that point to it. */
|
|
61
63
|
vram_dst = (uint8_t *)0x8010;
|
|
62
64
|
src = tile_data;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed vram_dst[i]=src[i] loop, which
|
|
66
|
+
* SDCC sm83 miscompiles when the dest points into VRAM ($8000-$9FFF). */
|
|
67
|
+
memcpy_vram(vram_dst, src, 16);
|
|
68
|
+
|
|
69
|
+
/* ── 2b. Fill the BG tilemap so the screen isn't an empty backdrop. ──
|
|
70
|
+
* With LCDC_TILE_DATA_LO ($8000 addressing) BG tile index 1 == our tile
|
|
71
|
+
* at $8010 — so we tile the whole 32×32 BG map with it. memcpy_vram-style
|
|
72
|
+
* pointer walk (NOT bg_map[k]=1, which SDCC sm83 miscompiles into VRAM). */
|
|
73
|
+
bg_map = (uint8_t *)0x9800;
|
|
74
|
+
for (j = 0; j < 32u * 32u; j++) *bg_map++ = 1;
|
|
66
75
|
|
|
67
76
|
/* ── 3. Object palette 0 (CGB path) ──────────────────────────────
|
|
68
77
|
* OCPS bit 7 = auto-increment after each write; bits 5..3 = palette
|
|
@@ -92,9 +101,9 @@ void main(void) {
|
|
|
92
101
|
oam_dma_flush(); /* first OAM live before the screen turns on */
|
|
93
102
|
|
|
94
103
|
/* ── 5. Turn the LCD back on with BG + OBJ enabled. ──────────────
|
|
95
|
-
* LCDC bits: 0x80=LCD on, 0x02=OBJ on, 0x10=tile data at $8000.
|
|
96
|
-
*
|
|
97
|
-
LCDC = LCDC_LCD_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
104
|
+
* LCDC bits: 0x80=LCD on, 0x01=BG on, 0x02=OBJ on, 0x10=tile data at $8000.
|
|
105
|
+
* BG is on now that we filled the BG map in step 2b. */
|
|
106
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_OBJ_ON | LCDC_TILE_DATA_LO;
|
|
98
107
|
|
|
99
108
|
/* ── 6. APU on — let the player beep ──────────────────────────── */
|
|
100
109
|
sound_init();
|
|
@@ -29,11 +29,47 @@
|
|
|
29
29
|
* project template). */
|
|
30
30
|
extern const huge_song_t sample_song;
|
|
31
31
|
|
|
32
|
+
/* Two 8×8 2bpp tiles so the BG isn't a single flat colour (a uniform
|
|
33
|
+
* screen reads >=92% one colour and fails the blank-screen check):
|
|
34
|
+
* tile 1 — solid colour 3
|
|
35
|
+
* tile 2 — solid colour 1
|
|
36
|
+
* We checkerboard them across the BG map below. */
|
|
37
|
+
static const uint8_t tile_solid3[16] = {
|
|
38
|
+
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
39
|
+
0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF, 0xFF,0xFF,
|
|
40
|
+
};
|
|
41
|
+
static const uint8_t tile_solid1[16] = {
|
|
42
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
43
|
+
0xFF,0x00, 0xFF,0x00, 0xFF,0x00, 0xFF,0x00,
|
|
44
|
+
};
|
|
45
|
+
|
|
32
46
|
void main(void) {
|
|
33
47
|
uint8_t shade = 0;
|
|
34
48
|
uint16_t frame = 0;
|
|
49
|
+
uint8_t *bg_map;
|
|
50
|
+
uint16_t j;
|
|
35
51
|
|
|
36
52
|
lcd_init_default();
|
|
53
|
+
LCDC = 0; /* LCD off so we can write VRAM freely */
|
|
54
|
+
|
|
55
|
+
/* Upload two tiles to VRAM slots 1 ($8010) and 2 ($8020). Use
|
|
56
|
+
* memcpy_vram (pointer-walk) — an indexed dst[i]=src[i] loop into VRAM
|
|
57
|
+
* is miscompiled by SDCC sm83. */
|
|
58
|
+
memcpy_vram((uint8_t *)0x8010, tile_solid3, 16);
|
|
59
|
+
memcpy_vram((uint8_t *)0x8020, tile_solid1, 16);
|
|
60
|
+
|
|
61
|
+
/* Checkerboard the 32×32 BG map at $9800 with tiles 1 and 2 so the
|
|
62
|
+
* screen shows two distinct shades. Pointer-walk (NOT bg_map[k]=...,
|
|
63
|
+
* which SDCC sm83 miscompiles into VRAM). */
|
|
64
|
+
bg_map = (uint8_t *)0x9800;
|
|
65
|
+
for (j = 0; j < 32u * 32u; j++) {
|
|
66
|
+
*bg_map++ = (uint8_t)((((j ^ (j >> 5)) & 1u) ? 1u : 2u));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* LCD on with BG enabled, $8000 tile-data addressing so index 1 == our
|
|
70
|
+
* tile at $8010. */
|
|
71
|
+
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_TILE_DATA_LO;
|
|
72
|
+
|
|
37
73
|
sound_init();
|
|
38
74
|
|
|
39
75
|
hUGE_init(&sample_song);
|
|
@@ -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
|
|
|
@@ -58,8 +75,9 @@ static uint8_t on_platform(int16_t px, int16_t py) {
|
|
|
58
75
|
|
|
59
76
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
60
77
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
61
|
-
|
|
62
|
-
|
|
78
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
79
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
80
|
+
memcpy_vram(dst, src, 16);
|
|
63
81
|
}
|
|
64
82
|
|
|
65
83
|
static void paint_platforms(void) {
|
|
@@ -71,8 +89,10 @@ static void paint_platforms(void) {
|
|
|
71
89
|
const Rect *p;
|
|
72
90
|
/* k MUST be uint16_t: 32*18 = 576 > 255, so a uint8_t counter would
|
|
73
91
|
* 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
|
-
|
|
92
|
+
* never clears, main() never starts). Classic SDCC limited-range trap.
|
|
93
|
+
* Fill sky above the floor line (row 16 = y 128) and textured ground
|
|
94
|
+
* at and below it, so the whole world is a real scene, not blank. */
|
|
95
|
+
for (k = 0; k < 32 * 18; k++) map[k] = (k >= 16 * 32) ? T_GROUND : T_SKY;
|
|
76
96
|
for (i = 0; i < N_PLATFORMS; i++) {
|
|
77
97
|
p = &platforms[i];
|
|
78
98
|
cx = p->x >> 3;
|
|
@@ -81,7 +101,7 @@ static void paint_platforms(void) {
|
|
|
81
101
|
ch = (p->h + 7) >> 3;
|
|
82
102
|
for (j = 0; j < cw; j++) {
|
|
83
103
|
if (cx + j < 32 && cy < 32)
|
|
84
|
-
map[cy * 32 + cx + j] =
|
|
104
|
+
map[cy * 32 + cx + j] = T_PLATFORM; /* platform top edge */
|
|
85
105
|
}
|
|
86
106
|
}
|
|
87
107
|
}
|
|
@@ -108,6 +128,8 @@ void main(void) {
|
|
|
108
128
|
upload_tile(0, tile_blank);
|
|
109
129
|
upload_tile(1, tile_player);
|
|
110
130
|
upload_tile(2, tile_platform);
|
|
131
|
+
upload_tile(T_SKY, tile_sky);
|
|
132
|
+
upload_tile(T_GROUND, tile_ground);
|
|
111
133
|
|
|
112
134
|
OCPS = 0x80;
|
|
113
135
|
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. */
|
|
@@ -127,8 +141,23 @@ static void lock_piece(void) {
|
|
|
127
141
|
|
|
128
142
|
static void upload_tile(uint8_t slot, const uint8_t *src) {
|
|
129
143
|
uint8_t *dst = (uint8_t *)(0x8000 + slot * 16);
|
|
130
|
-
|
|
131
|
-
|
|
144
|
+
/* memcpy_vram (pointer-walk) — NOT an indexed dst[i]=src[i] loop, which
|
|
145
|
+
* SDCC sm83 miscompiles when dst points into VRAM ($8000-$9FFF). */
|
|
146
|
+
memcpy_vram(dst, src, 16);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Draw the well frame around the 6×12 play area. Grid cells live at
|
|
150
|
+
* map[(row+1)*32 + (col+7)] (rows 1..12, cols 7..12), so the frame is the
|
|
151
|
+
* column to each side (6 and 13) and the floor row just below (row 13). */
|
|
152
|
+
static void draw_well(void) {
|
|
153
|
+
uint8_t *map = (uint8_t *)0x9800;
|
|
154
|
+
uint8_t r;
|
|
155
|
+
for (r = 1; r <= 12; r++) {
|
|
156
|
+
map[r * 32 + 6] = T_WALL; /* left wall */
|
|
157
|
+
map[r * 32 + 13] = T_WALL; /* right wall */
|
|
158
|
+
}
|
|
159
|
+
for (r = 6; r <= 13; r++)
|
|
160
|
+
map[13 * 32 + r] = T_WALL; /* floor */
|
|
132
161
|
}
|
|
133
162
|
|
|
134
163
|
void main(void) {
|
|
@@ -145,6 +174,7 @@ void main(void) {
|
|
|
145
174
|
upload_tile(T_R, tile_r);
|
|
146
175
|
upload_tile(T_G, tile_g);
|
|
147
176
|
upload_tile(T_B, tile_b);
|
|
177
|
+
upload_tile(T_WALL, tile_wall);
|
|
148
178
|
|
|
149
179
|
BCPS = 0x80;
|
|
150
180
|
for (i = 0; i < 4; i++) {
|
|
@@ -165,6 +195,7 @@ void main(void) {
|
|
|
165
195
|
score = 0;
|
|
166
196
|
fall_timer = 0;
|
|
167
197
|
new_piece();
|
|
198
|
+
draw_well();
|
|
168
199
|
draw_grid();
|
|
169
200
|
|
|
170
201
|
LCDC = LCDC_LCD_ON | LCDC_BG_ON | LCDC_TILE_DATA_LO;
|