romdevtools 0.16.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +75 -16
- package/CHANGELOG.md +316 -0
- package/examples/README.md +2 -0
- package/examples/atari2600/templates/platformer.asm +460 -0
- package/examples/atari2600/templates/racing.asm +463 -0
- package/examples/atari2600/templates/shmup.asm +386 -0
- package/examples/atari2600/templates/sports.asm +362 -0
- package/examples/atari7800/templates/default.c +49 -5
- package/examples/atari7800/templates/hello_sprite.c +48 -4
- package/examples/atari7800/templates/music_demo.c +47 -2
- package/examples/atari7800/templates/platformer.c +43 -4
- package/examples/atari7800/templates/puzzle.c +39 -4
- package/examples/atari7800/templates/racing.c +39 -4
- package/examples/atari7800/templates/shmup.c +40 -2
- package/examples/atari7800/templates/sports.c +36 -5
- package/examples/c64/templates/platformer.c +19 -5
- package/examples/c64/templates/puzzle.c +32 -2
- package/examples/c64/templates/shmup.c +28 -2
- package/examples/c64/templates/sports.c +30 -2
- package/examples/c64/templates/tile_engine.c +77 -27
- package/examples/gb/templates/default.c +110 -16
- package/examples/gb/templates/hello_sprite.c +15 -6
- package/examples/gb/templates/music_demo.c +36 -0
- package/examples/gb/templates/platformer.c +28 -6
- package/examples/gb/templates/puzzle.c +35 -4
- package/examples/gb/templates/racing.c +75 -10
- package/examples/gb/templates/shmup.c +41 -3
- package/examples/gb/templates/sports.c +51 -3
- package/examples/gb/templates/tile_engine.c +3 -2
- package/examples/gba/templates/gba_hello.c +29 -11
- package/examples/gba/templates/maxmod_demo.c +36 -2
- package/examples/gba/templates/platformer.c +3 -1
- package/examples/gba/templates/puzzle.c +15 -3
- package/examples/gba/templates/racing.c +65 -3
- package/examples/gba/templates/shmup.c +41 -4
- package/examples/gba/templates/sports.c +36 -2
- package/examples/gba/templates/tonc_hello.c +41 -5
- package/examples/gba/templates/tonc_hello_sprite.c +35 -1
- package/examples/gbc/templates/default.c +103 -26
- package/examples/gbc/templates/hello_sprite.c +12 -3
- package/examples/gbc/templates/music_demo.c +56 -12
- package/examples/gbc/templates/platformer.c +28 -6
- package/examples/gbc/templates/puzzle.c +35 -4
- package/examples/gbc/templates/racing.c +88 -21
- package/examples/gbc/templates/shmup.c +37 -3
- package/examples/gbc/templates/sports.c +48 -3
- package/examples/gbc/templates/tile_engine.c +3 -2
- package/examples/genesis/main.s +53 -1
- package/examples/genesis/templates/hello_sprite.c +25 -3
- package/examples/genesis/templates/puzzle.c +37 -3
- package/examples/genesis/templates/racing.c +44 -11
- package/examples/genesis/templates/sgdk_hello.c +34 -1
- package/examples/genesis/templates/shmup.c +31 -1
- package/examples/genesis/templates/shmup_2p.c +31 -0
- package/examples/genesis/templates/xgm2_demo.c +20 -0
- package/examples/gg/templates/default.c +56 -18
- package/examples/gg/templates/hello_sprite.c +25 -2
- package/examples/gg/templates/music_demo.c +24 -2
- package/examples/gg/templates/platformer.c +18 -12
- package/examples/gg/templates/puzzle.c +38 -7
- package/examples/gg/templates/racing.c +58 -9
- package/examples/gg/templates/shmup.c +47 -3
- package/examples/gg/templates/sports.c +57 -16
- package/examples/gg/templates/tile_engine.c +12 -6
- package/examples/lynx/templates/default.c +39 -8
- package/examples/lynx/templates/hello_sprite.c +15 -1
- package/examples/lynx/templates/music_demo.c +13 -1
- package/examples/lynx/templates/puzzle.c +28 -1
- package/examples/lynx/templates/racing.c +34 -7
- package/examples/lynx/templates/shmup.c +42 -3
- package/examples/lynx/templates/sports.c +29 -2
- package/examples/msx/platformer/main.c +213 -0
- package/examples/msx/puzzle/main.c +250 -0
- package/examples/msx/racing/main.c +249 -0
- package/examples/msx/shmup/main.c +288 -0
- package/examples/msx/sports/main.c +182 -0
- package/examples/nes/templates/default.c +67 -19
- package/examples/nes/templates/hello_sprite.c +35 -0
- package/examples/nes/templates/music_demo.c +40 -0
- package/examples/nes/templates/platformer.c +65 -6
- package/examples/nes/templates/puzzle.c +67 -6
- package/examples/nes/templates/racing.c +45 -13
- package/examples/nes/templates/shmup.c +51 -2
- package/examples/nes/templates/sports.c +51 -6
- package/examples/pce/catch_game/main.c +22 -3
- package/examples/pce/music_sfx/main.c +28 -1
- package/examples/pce/platformer/main.c +283 -0
- package/examples/pce/puzzle/main.c +304 -0
- package/examples/pce/racing/main.c +304 -0
- package/examples/pce/shmup/main.c +346 -0
- package/examples/pce/sports/main.c +254 -0
- package/examples/pce/sprite_move/main.c +7 -2
- package/examples/sms/main.c +35 -6
- package/examples/sms/templates/hello_sprite.c +29 -3
- package/examples/sms/templates/music_demo.c +18 -4
- package/examples/sms/templates/puzzle.c +34 -5
- package/examples/sms/templates/racing.c +39 -2
- package/examples/sms/templates/shmup.c +41 -2
- package/examples/sms/templates/shmup_2p.c +24 -1
- package/examples/sms/templates/sports.c +47 -4
- package/examples/snes/main.asm +108 -17
- package/examples/snes/templates/c-hello-data.asm +23 -0
- package/examples/snes/templates/c-hello.c +18 -1
- package/examples/snes/templates/default.c +50 -28
- package/examples/snes/templates/hello_sprite-data.asm +23 -0
- package/examples/snes/templates/hello_sprite.c +17 -1
- package/examples/snes/templates/music_demo-data.asm +23 -0
- package/examples/snes/templates/music_demo.c +22 -4
- package/examples/snes/templates/platformer-data.asm +22 -0
- package/examples/snes/templates/platformer.c +20 -2
- package/examples/snes/templates/puzzle-data.asm +22 -0
- package/examples/snes/templates/puzzle.c +21 -2
- package/examples/snes/templates/racing-data.asm +22 -0
- package/examples/snes/templates/racing.c +17 -1
- package/examples/snes/templates/shmup-data.asm +22 -0
- package/examples/snes/templates/shmup.c +20 -1
- package/examples/snes/templates/sports-data.asm +22 -0
- package/examples/snes/templates/sports.c +16 -1
- package/package.json +1 -1
- package/src/cheats/gamegenie.js +0 -1
- package/src/cli/smoke.js +1 -3
- package/src/cores/wasm/vice_x64_libretro.js +1 -1
- package/src/cores/wasm/vice_x64_libretro.wasm +0 -0
- package/src/host/LibretroHost.js +191 -16
- package/src/host/callbacks.js +9 -1
- package/src/host/chafa-render.js +2 -0
- package/src/host/dsp-state.js +2 -2
- package/src/host/gpgx-state.js +4 -0
- package/src/host/types.js +15 -8
- package/src/http/routes.js +1 -1
- package/src/http/tool-registry.js +26 -1
- package/src/mcp/server.js +1 -1
- package/src/mcp/state.js +36 -0
- package/src/mcp/tools/address-to-symbol.js +0 -1
- package/src/mcp/tools/art-loaders.js +1 -1
- package/src/mcp/tools/cart-parts.js +75 -4
- package/src/mcp/tools/classify-region.js +1 -1
- package/src/mcp/tools/diff-roms.js +1 -1
- package/src/mcp/tools/disasm-rebuild.js +507 -0
- package/src/mcp/tools/disasm.js +97 -9
- package/src/mcp/tools/find-references.js +1 -2
- package/src/mcp/tools/font-map.js +1 -1
- package/src/mcp/tools/frame.js +168 -3
- package/src/mcp/tools/index.js +0 -49
- package/src/mcp/tools/input-layout.js +0 -1
- package/src/mcp/tools/input.js +33 -3
- package/src/mcp/tools/lifecycle.js +18 -4
- package/src/mcp/tools/lospec.js +0 -19
- package/src/mcp/tools/platform-docs.js +1 -1
- package/src/mcp/tools/platform-tools.js +4 -4
- package/src/mcp/tools/project.js +54 -11
- package/src/mcp/tools/reinject.js +0 -1
- package/src/mcp/tools/rom-id.js +2 -2
- package/src/mcp/tools/snippets.js +2 -2
- package/src/mcp/tools/sprite-pipeline.js +1 -2
- package/src/mcp/tools/state.js +201 -14
- package/src/mcp/tools/tile-inspect.js +1 -1
- package/src/mcp/tools/toolchain.js +105 -12
- package/src/mcp/tools/watch-memory.js +137 -16
- package/src/platforms/_guides/ROMHACKING_PLAYBOOK.md +34 -0
- package/src/platforms/atari2600/TROUBLESHOOTING.md +6 -0
- package/src/platforms/atari7800/TROUBLESHOOTING.md +6 -0
- package/src/platforms/c64/MENTAL_MODEL.md +45 -1
- package/src/platforms/c64/TROUBLESHOOTING.md +6 -0
- package/src/platforms/c64/d64.js +280 -0
- package/src/platforms/c64/sid.js +0 -2
- package/src/platforms/common/metasprite-adapters.js +1 -1
- package/src/platforms/common/metasprite-codegen.js +3 -3
- package/src/platforms/common/registers.js +5 -3
- package/src/platforms/gb/MENTAL_MODEL.md +10 -0
- package/src/platforms/gb/TROUBLESHOOTING.md +6 -0
- package/src/platforms/gb/lib/c/gb_runtime.c +4 -4
- package/src/platforms/gba/TROUBLESHOOTING.md +6 -0
- package/src/platforms/gbc/TROUBLESHOOTING.md +6 -0
- package/src/platforms/gbc/lib/c/gb_runtime.c +4 -4
- package/src/platforms/genesis/TROUBLESHOOTING.md +6 -0
- package/src/platforms/gg/TROUBLESHOOTING.md +6 -0
- package/src/platforms/lynx/TROUBLESHOOTING.md +6 -0
- package/src/platforms/msx/MENTAL_MODEL.md +10 -6
- package/src/platforms/msx/TROUBLESHOOTING.md +6 -0
- package/src/platforms/nes/MENTAL_MODEL.md +63 -2
- package/src/platforms/nes/TROUBLESHOOTING.md +6 -0
- package/src/platforms/nes/image-to-tilemap.js +3 -0
- package/src/platforms/nes/lib/asm/famitone2.s +5 -1
- package/src/platforms/pce/MENTAL_MODEL.md +9 -4
- package/src/platforms/pce/TROUBLESHOOTING.md +6 -0
- package/src/platforms/pce/lib/c/pce_video.c +1 -1
- package/src/platforms/sms/TROUBLESHOOTING.md +6 -0
- package/src/platforms/snes/TROUBLESHOOTING.md +6 -0
- package/src/platforms/snes/brr.js +0 -2
- package/src/playtest/playtest.js +0 -7
- package/src/rom-id/identifier.js +15 -0
- package/src/toolchains/asar/asar.js +0 -9
- package/src/toolchains/assemble-snippet.js +30 -12
- package/src/toolchains/cc65/ines.js +145 -0
- package/src/toolchains/cc65/presets/nes/chr-ram-runtime.cfg +14 -1
- package/src/toolchains/cc65/presets/nes/chr-rom.cfg +83 -0
- package/src/toolchains/cc65/presets/nes/chr-rom.crt0.s +153 -0
- package/src/toolchains/common/reassemble.js +10 -3
- package/src/toolchains/common/sdk-cache.js +1 -1
- package/src/toolchains/genesis-c/genesis-c.js +5 -3
- package/src/toolchains/index.js +27 -3
- package/src/toolchains/parse-errors.js +78 -1
- package/src/toolchains/sdcc/preflight-lint.js +5 -1
- package/src/toolchains/sdcc/sdcc.js +1 -1
- package/src/toolchains/sjasm/sjasm.js +1 -1
- package/src/toolchains/snes-c/snes-c.js +2 -2
- package/src/toolchains/vasm68k/vasm68k.js +2 -4
- package/src/toolchains/wladx/wladx.js +1 -1
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
; ── sports.asm — Atari 2600 SPORTS genre scaffold (Pong) ──────────────
|
|
2
|
+
;
|
|
3
|
+
; Pong IS the 2600's sport — the console shipped with Combat and Video
|
|
4
|
+
; Olympics; the paddle-and-ball game is the genre's archetype and a
|
|
5
|
+
; flawless fit for the TIA's two players + ball. Identical in spirit to
|
|
6
|
+
; the verified `paddle` template.
|
|
7
|
+
;
|
|
8
|
+
; Two paddles (player 0 = left, player 1 = right), one ball (BL),
|
|
9
|
+
; symmetric playfield with top + bottom walls. Joystick port A up/down
|
|
10
|
+
; moves the left paddle; the right paddle does simple AI (chases ball Y).
|
|
11
|
+
;
|
|
12
|
+
; 2600 architecture refresher:
|
|
13
|
+
; - No frame buffer. Every scanline you write TIA registers describing
|
|
14
|
+
; what THAT line looks like, then STA WSYNC to advance.
|
|
15
|
+
; - 5 graphics objects: P0, P1, M0, M1, BL (+ PF tiles via PF0/1/2).
|
|
16
|
+
; - "Race the beam": the CPU has ~76 cycles between WSYNCs.
|
|
17
|
+
;
|
|
18
|
+
; This scaffold uses:
|
|
19
|
+
; P0 → left paddle (8-pixel-tall vertical bar)
|
|
20
|
+
; P1 → right paddle (8-pixel-tall vertical bar)
|
|
21
|
+
; BL → 2-pixel-wide ball
|
|
22
|
+
; PF0 → top + bottom playfield walls (drawn for the top + bottom rows)
|
|
23
|
+
|
|
24
|
+
processor 6502
|
|
25
|
+
org $F000
|
|
26
|
+
|
|
27
|
+
VSYNC = $00
|
|
28
|
+
VBLANK = $01
|
|
29
|
+
WSYNC = $02
|
|
30
|
+
COLUP0 = $06
|
|
31
|
+
COLUP1 = $07
|
|
32
|
+
COLUPF = $08
|
|
33
|
+
COLUBK = $09
|
|
34
|
+
PF0 = $0D
|
|
35
|
+
PF1 = $0E
|
|
36
|
+
PF2 = $0F
|
|
37
|
+
RESP0 = $10
|
|
38
|
+
RESP1 = $11
|
|
39
|
+
RESBL = $14
|
|
40
|
+
GRP0 = $1B
|
|
41
|
+
GRP1 = $1C
|
|
42
|
+
ENABL = $1F
|
|
43
|
+
HMP0 = $20
|
|
44
|
+
HMP1 = $21
|
|
45
|
+
HMBL = $24
|
|
46
|
+
HMOVE = $2A
|
|
47
|
+
CTRLPF = $0A
|
|
48
|
+
SWCHA = $280
|
|
49
|
+
; ── TIA audio (R41) ────────────────────────────────────────────────
|
|
50
|
+
AUDC0 = $15
|
|
51
|
+
AUDF0 = $17
|
|
52
|
+
AUDV0 = $19
|
|
53
|
+
|
|
54
|
+
; ── Zero-page state ──────────────────────────────────────────────────
|
|
55
|
+
P0_Y = $80 ; left paddle top scanline (16..168)
|
|
56
|
+
P1_Y = $81 ; right paddle top scanline
|
|
57
|
+
BALL_X = $82 ; ball horizontal (TIA pixel 0..159)
|
|
58
|
+
BALL_Y = $83 ; ball vertical (scanline)
|
|
59
|
+
BALL_DX = $84 ; +1 or -1
|
|
60
|
+
BALL_DY = $85 ; +1 or -1
|
|
61
|
+
FRAME = $86
|
|
62
|
+
SFX_LEFT = $87 ; frames remaining on active sfx (0 = silent)
|
|
63
|
+
|
|
64
|
+
START:
|
|
65
|
+
SEI
|
|
66
|
+
CLD
|
|
67
|
+
LDX #$FF
|
|
68
|
+
TXS
|
|
69
|
+
LDA #0
|
|
70
|
+
.clr:
|
|
71
|
+
STA $00,X
|
|
72
|
+
DEX
|
|
73
|
+
BNE .clr
|
|
74
|
+
|
|
75
|
+
LDA #88
|
|
76
|
+
STA P0_Y
|
|
77
|
+
STA P1_Y
|
|
78
|
+
LDA #80
|
|
79
|
+
STA BALL_X
|
|
80
|
+
LDA #90
|
|
81
|
+
STA BALL_Y
|
|
82
|
+
LDA #1
|
|
83
|
+
STA BALL_DX
|
|
84
|
+
STA BALL_DY
|
|
85
|
+
|
|
86
|
+
; Boot chime — confirms TIA audio is wired.
|
|
87
|
+
LDA #$04
|
|
88
|
+
STA AUDC0
|
|
89
|
+
LDA #$0C
|
|
90
|
+
STA AUDF0
|
|
91
|
+
LDA #$0F
|
|
92
|
+
STA AUDV0
|
|
93
|
+
LDA #20
|
|
94
|
+
STA SFX_LEFT
|
|
95
|
+
|
|
96
|
+
LDA #$80 ; blue background
|
|
97
|
+
STA COLUBK
|
|
98
|
+
LDA #$0F ; white paddles
|
|
99
|
+
STA COLUP0
|
|
100
|
+
STA COLUP1
|
|
101
|
+
LDA #$48 ; cyan playfield
|
|
102
|
+
STA COLUPF
|
|
103
|
+
LDA #$05 ; PF symmetric reflect + ball priority
|
|
104
|
+
STA CTRLPF
|
|
105
|
+
|
|
106
|
+
MAIN:
|
|
107
|
+
INC FRAME
|
|
108
|
+
|
|
109
|
+
; ── VSYNC ─────────────────────────────────────────────────────────
|
|
110
|
+
LDA #2
|
|
111
|
+
STA VSYNC
|
|
112
|
+
STA WSYNC
|
|
113
|
+
STA WSYNC
|
|
114
|
+
STA WSYNC
|
|
115
|
+
LDA #0
|
|
116
|
+
STA VSYNC
|
|
117
|
+
|
|
118
|
+
; ── VBLANK (37 lines) — game logic ────────────────────────────────
|
|
119
|
+
; 34 here + the 3 STA WSYNC in the P0/P1 positioning block below = 37 VBLANK
|
|
120
|
+
; lines total. (Bug fix: this loop used to be 37 AND the positioning added 3
|
|
121
|
+
; more → 265 scanlines/frame → the TV/emulator can't lock vsync → rolling /
|
|
122
|
+
; black picture. Exactly 262 lines = 3 VSYNC + 37 VBLANK + 192 visible + 30
|
|
123
|
+
; overscan; the positioning WSYNCs MUST be counted against the 37.)
|
|
124
|
+
LDA #2
|
|
125
|
+
STA VBLANK
|
|
126
|
+
LDX #34
|
|
127
|
+
.vb:
|
|
128
|
+
STA WSYNC
|
|
129
|
+
DEX
|
|
130
|
+
BNE .vb
|
|
131
|
+
|
|
132
|
+
; Move left paddle from joystick (every 2 frames to throttle).
|
|
133
|
+
LDA FRAME
|
|
134
|
+
AND #$01
|
|
135
|
+
BNE .skip_pad
|
|
136
|
+
LDA SWCHA
|
|
137
|
+
ASL ; right (unused)
|
|
138
|
+
ASL ; left (unused)
|
|
139
|
+
ASL ; down
|
|
140
|
+
BCS .nd
|
|
141
|
+
INC P0_Y
|
|
142
|
+
.nd:
|
|
143
|
+
ASL ; up
|
|
144
|
+
BCS .nu
|
|
145
|
+
DEC P0_Y
|
|
146
|
+
.nu:
|
|
147
|
+
; Clamp paddle within bounds
|
|
148
|
+
LDA P0_Y
|
|
149
|
+
CMP #16
|
|
150
|
+
BCS .nopaddmin
|
|
151
|
+
LDA #16
|
|
152
|
+
STA P0_Y
|
|
153
|
+
.nopaddmin:
|
|
154
|
+
CMP #168
|
|
155
|
+
BCC .nopaddmax
|
|
156
|
+
LDA #168
|
|
157
|
+
STA P0_Y
|
|
158
|
+
.nopaddmax:
|
|
159
|
+
.skip_pad:
|
|
160
|
+
|
|
161
|
+
; Right-paddle AI — chase the ball's Y
|
|
162
|
+
LDA BALL_Y
|
|
163
|
+
CMP P1_Y
|
|
164
|
+
BCC .ai_up
|
|
165
|
+
INC P1_Y
|
|
166
|
+
JMP .ai_done
|
|
167
|
+
.ai_up:
|
|
168
|
+
DEC P1_Y
|
|
169
|
+
.ai_done:
|
|
170
|
+
|
|
171
|
+
; Move ball
|
|
172
|
+
LDA BALL_DX
|
|
173
|
+
CLC
|
|
174
|
+
ADC BALL_X
|
|
175
|
+
STA BALL_X
|
|
176
|
+
LDA BALL_DY
|
|
177
|
+
CLC
|
|
178
|
+
ADC BALL_Y
|
|
179
|
+
STA BALL_Y
|
|
180
|
+
|
|
181
|
+
; Bounce off top/bottom — short blip on each bounce.
|
|
182
|
+
LDA BALL_Y
|
|
183
|
+
CMP #20
|
|
184
|
+
BCS .nb_top
|
|
185
|
+
LDA #1
|
|
186
|
+
STA BALL_DY
|
|
187
|
+
JSR sfx_wall
|
|
188
|
+
.nb_top:
|
|
189
|
+
LDA BALL_Y
|
|
190
|
+
CMP #180
|
|
191
|
+
BCC .nb_bot
|
|
192
|
+
LDA #$FF ; -1
|
|
193
|
+
STA BALL_DY
|
|
194
|
+
JSR sfx_wall
|
|
195
|
+
.nb_bot:
|
|
196
|
+
; Bounce off left/right (and respawn near centre on miss) — pew on miss.
|
|
197
|
+
LDA BALL_X
|
|
198
|
+
CMP #4
|
|
199
|
+
BCS .nb_l
|
|
200
|
+
LDA #1
|
|
201
|
+
STA BALL_DX
|
|
202
|
+
LDA #80
|
|
203
|
+
STA BALL_X
|
|
204
|
+
JSR sfx_score
|
|
205
|
+
.nb_l:
|
|
206
|
+
LDA BALL_X
|
|
207
|
+
CMP #156
|
|
208
|
+
BCC .nb_r
|
|
209
|
+
LDA #$FF
|
|
210
|
+
STA BALL_DX
|
|
211
|
+
LDA #80
|
|
212
|
+
STA BALL_X
|
|
213
|
+
JSR sfx_score
|
|
214
|
+
.nb_r:
|
|
215
|
+
|
|
216
|
+
; sfx_update: tick the countdown, silence on zero.
|
|
217
|
+
LDA SFX_LEFT
|
|
218
|
+
BEQ .sfx_done
|
|
219
|
+
DEC SFX_LEFT
|
|
220
|
+
BNE .sfx_done
|
|
221
|
+
LDA #0
|
|
222
|
+
STA AUDV0
|
|
223
|
+
.sfx_done:
|
|
224
|
+
|
|
225
|
+
; ── Position P0 / P1 / HMOVE — exactly 3 WSYNC-bounded lines ───────
|
|
226
|
+
; CRITICAL: every RESPx write AND the STA HMOVE must complete inside
|
|
227
|
+
; the 76-cycle scanline that began with its STA WSYNC. A DEX/BNE delay
|
|
228
|
+
; loop costs 5 cycles/iteration, so the loop count must be small enough
|
|
229
|
+
; that RESPx still lands before the line ends. The old code used
|
|
230
|
+
; LDX #38 (~189 cycles = 2.5 scanlines!) with no WSYNC before RESP1/
|
|
231
|
+
; HMOVE, so it emitted ~2-3 UNCOUNTED scanlines past the 262 budget →
|
|
232
|
+
; ~265 lines/frame → vsync never locks (rolling magenta band). HMOVE
|
|
233
|
+
; was also issued mid-line; it must follow a fresh WSYNC.
|
|
234
|
+
|
|
235
|
+
; Line 1 of 3: coarse-position P0 (left, ~column 16)
|
|
236
|
+
STA WSYNC
|
|
237
|
+
LDX #5
|
|
238
|
+
.p0d:
|
|
239
|
+
DEX
|
|
240
|
+
BNE .p0d ; ~24 cycles in → P0 lands near the left edge
|
|
241
|
+
STA RESP0
|
|
242
|
+
; Line 2 of 3: coarse-position P1 (right, ~column 132)
|
|
243
|
+
STA WSYNC
|
|
244
|
+
LDX #13
|
|
245
|
+
.p1d:
|
|
246
|
+
DEX
|
|
247
|
+
BNE .p1d ; ~64 cycles in (< 76) → P1 lands near the right
|
|
248
|
+
STA RESP1
|
|
249
|
+
; Line 3 of 3: apply HMOVE on a FRESH line, right after WSYNC
|
|
250
|
+
STA WSYNC
|
|
251
|
+
STA HMOVE
|
|
252
|
+
|
|
253
|
+
LDA #0
|
|
254
|
+
STA VBLANK
|
|
255
|
+
|
|
256
|
+
; ── Visible (192 lines) — TWO-LINE KERNEL ─────────────────────────
|
|
257
|
+
; CRITICAL: a single scanline is only 76 CPU cycles. The full per-line
|
|
258
|
+
; render here (playfield walls + P0 + P1 + ball, each a SEC/SBC/CMP +
|
|
259
|
+
; conditional store) is ~88 cycles — it does NOT fit in one line. In a
|
|
260
|
+
; 1-line kernel each WSYNC iteration then spills past the line boundary,
|
|
261
|
+
; so 192 iterations stretch to ~232 emitted lines → ~250-line frame →
|
|
262
|
+
; vsync never locks (rolling magenta band — THE bug).
|
|
263
|
+
;
|
|
264
|
+
; The fix is the standard 2600 "2-line kernel": each loop pass renders
|
|
265
|
+
; TWO scanlines and splits the work across two WSYNCs, doubling the
|
|
266
|
+
; budget to ~152 cycles. 96 passes × 2 lines = 192 visible lines.
|
|
267
|
+
; Y counts 192→2 in steps of 2; paddles/ball move in 2px steps (fine
|
|
268
|
+
; for Pong). The branchless "LDA #off / CMP / BCS skip / LDA #on" form
|
|
269
|
+
; also drops the JMPs the old code paid every line.
|
|
270
|
+
LDY #192
|
|
271
|
+
.draw:
|
|
272
|
+
; ---- first line of the pair: playfield walls + left paddle ----
|
|
273
|
+
STA WSYNC
|
|
274
|
+
; Top + bottom walls: full-width PF on the outer rows (Y>=189 / Y<5)
|
|
275
|
+
LDA #0
|
|
276
|
+
CPY #189
|
|
277
|
+
BCS .wall
|
|
278
|
+
CPY #5
|
|
279
|
+
BCS .nowall
|
|
280
|
+
.wall:
|
|
281
|
+
LDA #$FF
|
|
282
|
+
.nowall:
|
|
283
|
+
STA PF0
|
|
284
|
+
STA PF1
|
|
285
|
+
STA PF2
|
|
286
|
+
; Left paddle: 8 lines starting at P0_Y
|
|
287
|
+
TYA
|
|
288
|
+
SEC
|
|
289
|
+
SBC P0_Y
|
|
290
|
+
CMP #8
|
|
291
|
+
LDA #0
|
|
292
|
+
BCS .p0off
|
|
293
|
+
LDA #$FF
|
|
294
|
+
.p0off:
|
|
295
|
+
STA GRP0
|
|
296
|
+
; ---- second line of the pair: right paddle + ball ----
|
|
297
|
+
STA WSYNC
|
|
298
|
+
; Right paddle: 8 lines starting at P1_Y
|
|
299
|
+
TYA
|
|
300
|
+
SEC
|
|
301
|
+
SBC P1_Y
|
|
302
|
+
CMP #8
|
|
303
|
+
LDA #0
|
|
304
|
+
BCS .p1off
|
|
305
|
+
LDA #$FF
|
|
306
|
+
.p1off:
|
|
307
|
+
STA GRP1
|
|
308
|
+
; Ball: 2 lines starting at BALL_Y
|
|
309
|
+
TYA
|
|
310
|
+
SEC
|
|
311
|
+
SBC BALL_Y
|
|
312
|
+
CMP #2
|
|
313
|
+
LDA #0
|
|
314
|
+
BCS .bloff
|
|
315
|
+
LDA #2
|
|
316
|
+
.bloff:
|
|
317
|
+
STA ENABL
|
|
318
|
+
DEY
|
|
319
|
+
DEY
|
|
320
|
+
BNE .draw
|
|
321
|
+
|
|
322
|
+
; ── Overscan (30 lines) ───────────────────────────────────────────
|
|
323
|
+
LDA #2
|
|
324
|
+
STA VBLANK
|
|
325
|
+
LDX #30
|
|
326
|
+
.os:
|
|
327
|
+
STA WSYNC
|
|
328
|
+
DEX
|
|
329
|
+
BNE .os
|
|
330
|
+
|
|
331
|
+
JMP MAIN
|
|
332
|
+
|
|
333
|
+
; ── TIA sfx helpers (R41) ─────────────────────────────────────────
|
|
334
|
+
; sfx_wall — short blip on wall bounce (4-frame ringing tone)
|
|
335
|
+
sfx_wall:
|
|
336
|
+
LDA #$04
|
|
337
|
+
STA AUDC0
|
|
338
|
+
LDA #$10
|
|
339
|
+
STA AUDF0
|
|
340
|
+
LDA #$0F
|
|
341
|
+
STA AUDV0
|
|
342
|
+
LDA #4
|
|
343
|
+
STA SFX_LEFT
|
|
344
|
+
RTS
|
|
345
|
+
|
|
346
|
+
; sfx_score — longer chime when a player scores (16 frames)
|
|
347
|
+
sfx_score:
|
|
348
|
+
LDA #$04
|
|
349
|
+
STA AUDC0
|
|
350
|
+
LDA #$06 ; higher pitch
|
|
351
|
+
STA AUDF0
|
|
352
|
+
LDA #$0F
|
|
353
|
+
STA AUDV0
|
|
354
|
+
LDA #16
|
|
355
|
+
STA SFX_LEFT
|
|
356
|
+
RTS
|
|
357
|
+
|
|
358
|
+
; ── Vector table ──────────────────────────────────────────────────
|
|
359
|
+
org $FFFA
|
|
360
|
+
.word START
|
|
361
|
+
.word START
|
|
362
|
+
.word START
|
|
@@ -43,6 +43,8 @@
|
|
|
43
43
|
#define P0C1 (*(volatile uint8_t*)0x21)
|
|
44
44
|
#define P0C2 (*(volatile uint8_t*)0x22)
|
|
45
45
|
#define P0C3 (*(volatile uint8_t*)0x23)
|
|
46
|
+
#define P1C1 (*(volatile uint8_t*)0x25)
|
|
47
|
+
#define P2C1 (*(volatile uint8_t*)0x29)
|
|
46
48
|
#define MSTAT (*(volatile uint8_t*)0x28)
|
|
47
49
|
#define DPPH (*(volatile uint8_t*)0x2C)
|
|
48
50
|
#define DPPL (*(volatile uint8_t*)0x30)
|
|
@@ -91,6 +93,43 @@ MK_DL(dl_row4); MK_DL(dl_row5); MK_DL(dl_row6); MK_DL(dl_row7);
|
|
|
91
93
|
* at dp+1; if it's 0, the parse loop exits immediately. */
|
|
92
94
|
static uint8_t dl_empty[2] = { 0, 0 };
|
|
93
95
|
|
|
96
|
+
/* ── Background playfield ─────────────────────────────────────────
|
|
97
|
+
* Without a full-screen drawable the display list emits only the one
|
|
98
|
+
* sprite above and ~99% of the screen stays the flat BACKGRND colour
|
|
99
|
+
* (reads as "blank"). These full-width bands fill every non-sprite
|
|
100
|
+
* zone with scenery so the frame has real content.
|
|
101
|
+
*
|
|
102
|
+
* One scanline of solid pixels lives in ROM (band_pix). A single DL
|
|
103
|
+
* drawable is at most 32 bytes = 128 px wide, so a full 160-px line
|
|
104
|
+
* needs TWO drawables. Width encoding (byte[3] low 5 bits) = 32-bytes;
|
|
105
|
+
* high 3 bits = palette: field uses palette 1, ground uses palette 2.
|
|
106
|
+
*/
|
|
107
|
+
static const uint8_t band_pix[32] = {
|
|
108
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
|
|
109
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
|
|
110
|
+
};
|
|
111
|
+
#define MK_BAND(name, pal) static uint8_t name[11] = { \
|
|
112
|
+
0, 0x40, 0, ((pal) << 5) | 0, 0, /* 128 px @ x0 */ \
|
|
113
|
+
0, 0x40, 0, ((pal) << 5) | 24, 128, /* 32 px @ x128 */ \
|
|
114
|
+
0 }
|
|
115
|
+
MK_BAND(dl_field, 1);
|
|
116
|
+
MK_BAND(dl_ground, 2);
|
|
117
|
+
#define GROUND_ZONE 188
|
|
118
|
+
|
|
119
|
+
static void set_band_addr(uint8_t* dl) {
|
|
120
|
+
uint16_t a = (uint16_t)(uintptr_t)band_pix;
|
|
121
|
+
dl[0] = dl[5] = (uint8_t)(a & 0xFF);
|
|
122
|
+
dl[2] = dl[7] = (uint8_t)(a >> 8);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Background DL for a non-sprite zone: sky (empty) up top, field in the
|
|
126
|
+
* middle, ground at the bottom. */
|
|
127
|
+
static uint16_t bg_zone_dl(int zone) {
|
|
128
|
+
if (zone >= GROUND_ZONE) return (uint16_t)(uintptr_t)dl_ground;
|
|
129
|
+
if (zone >= 28) return (uint16_t)(uintptr_t)dl_field;
|
|
130
|
+
return (uint16_t)(uintptr_t)dl_empty;
|
|
131
|
+
}
|
|
132
|
+
|
|
94
133
|
/* DLL: one entry per visible scanline + the 10-line top overscan
|
|
95
134
|
* MARIA walks BEFORE the visible area begins. NTSC display area =
|
|
96
135
|
* scanlines 16..258 = 243 lines.
|
|
@@ -130,9 +169,12 @@ static void vblank_wait(void) {
|
|
|
130
169
|
|
|
131
170
|
void main(void) {
|
|
132
171
|
uint16_t dll_addr;
|
|
133
|
-
uint16_t empty_dl = (uint16_t)(uintptr_t)dl_empty;
|
|
134
172
|
int i;
|
|
135
173
|
|
|
174
|
+
/* Point the background bands at their shared ROM pixel row. */
|
|
175
|
+
set_band_addr(dl_field);
|
|
176
|
+
set_band_addr(dl_ground);
|
|
177
|
+
|
|
136
178
|
/* Patch each row's DL to point at its sprite-data row. */
|
|
137
179
|
set_dl_addr(dl_row0, sprite_row0);
|
|
138
180
|
set_dl_addr(dl_row1, sprite_row1);
|
|
@@ -143,9 +185,9 @@ void main(void) {
|
|
|
143
185
|
set_dl_addr(dl_row6, sprite_row6);
|
|
144
186
|
set_dl_addr(dl_row7, sprite_row7);
|
|
145
187
|
|
|
146
|
-
/* Build the DLL:
|
|
188
|
+
/* Build the DLL: background scenery everywhere except the 8 sprite rows. */
|
|
147
189
|
for (i = 0; i < DLL_ZONES; i++) {
|
|
148
|
-
uint16_t dl_ptr =
|
|
190
|
+
uint16_t dl_ptr = bg_zone_dl(i);
|
|
149
191
|
if (i == SPRITE_Y + 0) dl_ptr = (uint16_t)(uintptr_t)dl_row0;
|
|
150
192
|
else if (i == SPRITE_Y + 1) dl_ptr = (uint16_t)(uintptr_t)dl_row1;
|
|
151
193
|
else if (i == SPRITE_Y + 2) dl_ptr = (uint16_t)(uintptr_t)dl_row2;
|
|
@@ -158,10 +200,12 @@ void main(void) {
|
|
|
158
200
|
}
|
|
159
201
|
|
|
160
202
|
/* Palette + background. Atari NTSC palette: HHHL nibble form. */
|
|
161
|
-
BACKGRND = 0x88; /* light blue */
|
|
162
|
-
P0C1 = 0x46; /* orange-red */
|
|
203
|
+
BACKGRND = 0x88; /* light blue sky */
|
|
204
|
+
P0C1 = 0x46; /* orange-red (sprite) */
|
|
163
205
|
P0C2 = 0x0F; /* white */
|
|
164
206
|
P0C3 = 0x36; /* pink */
|
|
207
|
+
P1C1 = 0xC8; /* field green (background band) */
|
|
208
|
+
P2C1 = 0x14; /* ground brown (background band) */
|
|
165
209
|
CHARBASE = 0;
|
|
166
210
|
OFFSET = 0;
|
|
167
211
|
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
#define P0C1 (*(volatile uint8_t*)0x21)
|
|
17
17
|
#define P0C2 (*(volatile uint8_t*)0x22)
|
|
18
18
|
#define P0C3 (*(volatile uint8_t*)0x23)
|
|
19
|
+
#define P1C1 (*(volatile uint8_t*)0x25)
|
|
20
|
+
#define P2C1 (*(volatile uint8_t*)0x29)
|
|
19
21
|
#define MSTAT (*(volatile uint8_t*)0x28)
|
|
20
22
|
#define DPPH (*(volatile uint8_t*)0x2C)
|
|
21
23
|
#define DPPL (*(volatile uint8_t*)0x30)
|
|
@@ -47,6 +49,42 @@ MK_DL(dl_row4); MK_DL(dl_row5); MK_DL(dl_row6); MK_DL(dl_row7);
|
|
|
47
49
|
|
|
48
50
|
static uint8_t dl_empty[2] = { 0, 0 };
|
|
49
51
|
|
|
52
|
+
/* ── Background playfield ─────────────────────────────────────────────
|
|
53
|
+
* Without a full-screen drawable the display list emits only the one
|
|
54
|
+
* sprite and ~99% of the screen stays the flat BACKGRND colour (reads as
|
|
55
|
+
* "blank"). These full-width bands fill every non-sprite zone with scenery
|
|
56
|
+
* so the frame has real content (same machinery as default.c).
|
|
57
|
+
*
|
|
58
|
+
* One scanline of solid pixels lives in ROM (band_pix). A single DL
|
|
59
|
+
* drawable is at most 32 bytes = 128 px wide, so a full 160-px line needs
|
|
60
|
+
* TWO drawables. Width (byte[3] low 5 bits) = 32-bytes; high 3 bits =
|
|
61
|
+
* palette: field uses palette 1, ground uses palette 2. */
|
|
62
|
+
static const uint8_t band_pix[32] = {
|
|
63
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
|
|
64
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
|
|
65
|
+
};
|
|
66
|
+
#define MK_BAND(name, pal) static uint8_t name[11] = { \
|
|
67
|
+
0, 0x40, 0, ((pal) << 5) | 0, 0, /* 128 px @ x0 */ \
|
|
68
|
+
0, 0x40, 0, ((pal) << 5) | 24, 128, /* 32 px @ x128 */ \
|
|
69
|
+
0 }
|
|
70
|
+
MK_BAND(dl_field, 1);
|
|
71
|
+
MK_BAND(dl_ground, 2);
|
|
72
|
+
#define GROUND_ZONE 188
|
|
73
|
+
|
|
74
|
+
static void set_band_addr(uint8_t* dl) {
|
|
75
|
+
uint16_t a = (uint16_t)(uintptr_t)band_pix;
|
|
76
|
+
dl[0] = dl[5] = (uint8_t)(a & 0xFF);
|
|
77
|
+
dl[2] = dl[7] = (uint8_t)(a >> 8);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Background DL for a non-sprite zone: sky (empty) up top, field in the
|
|
81
|
+
* middle, ground at the bottom. */
|
|
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
|
+
|
|
50
88
|
#define DLL_ZONES 243
|
|
51
89
|
static uint8_t dll[DLL_ZONES * 3];
|
|
52
90
|
|
|
@@ -62,9 +100,9 @@ static void set_dll_entry(int idx, uint16_t dl_ptr) {
|
|
|
62
100
|
dll[idx * 3 + 2] = (uint8_t)(dl_ptr & 0xFF);
|
|
63
101
|
}
|
|
64
102
|
|
|
65
|
-
/* Build the DLL with the sprite's 8 rows placed at DLL index sprite_y
|
|
103
|
+
/* Build the DLL with the sprite's 8 rows placed at DLL index sprite_y;
|
|
104
|
+
* every other zone gets the background scenery band for its row. */
|
|
66
105
|
static void build_dll(uint8_t sprite_y) {
|
|
67
|
-
uint16_t empty = (uint16_t)(uintptr_t)dl_empty;
|
|
68
106
|
int i;
|
|
69
107
|
for (i = 0; i < DLL_ZONES; i++) {
|
|
70
108
|
uint16_t dl;
|
|
@@ -78,7 +116,7 @@ static void build_dll(uint8_t sprite_y) {
|
|
|
78
116
|
case 5: dl = (uint16_t)(uintptr_t)dl_row5; break;
|
|
79
117
|
case 6: dl = (uint16_t)(uintptr_t)dl_row6; break;
|
|
80
118
|
case 7: dl = (uint16_t)(uintptr_t)dl_row7; break;
|
|
81
|
-
default: dl =
|
|
119
|
+
default: dl = bg_zone_dl(i); break; /* field/ground scenery */
|
|
82
120
|
}
|
|
83
121
|
set_dll_entry(i, dl);
|
|
84
122
|
}
|
|
@@ -99,6 +137,10 @@ void main(void) {
|
|
|
99
137
|
uint8_t x = 80;
|
|
100
138
|
uint8_t y = 110;
|
|
101
139
|
|
|
140
|
+
/* Point the background bands at their shared ROM pixel row. */
|
|
141
|
+
set_band_addr(dl_field);
|
|
142
|
+
set_band_addr(dl_ground);
|
|
143
|
+
|
|
102
144
|
/* Wire each DL's address bytes to its sprite-row data. */
|
|
103
145
|
set_dl_addr(dl_row0, sprite_row0);
|
|
104
146
|
set_dl_addr(dl_row1, sprite_row1);
|
|
@@ -112,10 +154,12 @@ void main(void) {
|
|
|
112
154
|
set_x(x);
|
|
113
155
|
build_dll(y);
|
|
114
156
|
|
|
115
|
-
BACKGRND = 0x88;
|
|
157
|
+
BACKGRND = 0x88; /* light blue sky */
|
|
116
158
|
P0C1 = 0x46;
|
|
117
159
|
P0C2 = 0x0F;
|
|
118
160
|
P0C3 = 0x36;
|
|
161
|
+
P1C1 = 0xC8; /* field green (background band) */
|
|
162
|
+
P2C1 = 0x14; /* ground brown (background band) */
|
|
119
163
|
CHARBASE = 0;
|
|
120
164
|
OFFSET = 0;
|
|
121
165
|
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
#define P0C1 (*(volatile uint8_t*)0x21)
|
|
20
20
|
#define P0C2 (*(volatile uint8_t*)0x22)
|
|
21
21
|
#define P0C3 (*(volatile uint8_t*)0x23)
|
|
22
|
+
#define P1C1 (*(volatile uint8_t*)0x25)
|
|
23
|
+
#define P2C1 (*(volatile uint8_t*)0x29)
|
|
22
24
|
#define MSTAT (*(volatile uint8_t*)0x28)
|
|
23
25
|
#define DPPH (*(volatile uint8_t*)0x2C)
|
|
24
26
|
#define DPPL (*(volatile uint8_t*)0x30)
|
|
@@ -47,6 +49,42 @@ MK_DL(dl_row4); MK_DL(dl_row5); MK_DL(dl_row6); MK_DL(dl_row7);
|
|
|
47
49
|
|
|
48
50
|
static uint8_t dl_empty[2] = { 0, 0 };
|
|
49
51
|
|
|
52
|
+
/* ── Background playfield ─────────────────────────────────────────────
|
|
53
|
+
* Without a full-screen drawable the display list emits only the banner
|
|
54
|
+
* and ~99% of the screen stays the flat BACKGRND colour (reads as
|
|
55
|
+
* "blank"). These full-width bands fill every non-banner zone with scenery
|
|
56
|
+
* so the frame has real content (same machinery as default.c).
|
|
57
|
+
*
|
|
58
|
+
* One scanline of solid pixels lives in ROM (band_pix). A 160-px line needs
|
|
59
|
+
* TWO drawables (a DL drawable is at most 32 bytes = 128 px). Width
|
|
60
|
+
* (byte[3] low 5 bits) = 32-bytes; high 3 bits = palette: field = palette
|
|
61
|
+
* 1, ground = palette 2. */
|
|
62
|
+
static const uint8_t band_pix[32] = {
|
|
63
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
|
|
64
|
+
0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
|
|
65
|
+
};
|
|
66
|
+
#define MK_BAND(name, pal) static uint8_t name[11] = { \
|
|
67
|
+
0, 0x40, 0, ((pal) << 5) | 0, 0, /* 128 px @ x0 */ \
|
|
68
|
+
0, 0x40, 0, ((pal) << 5) | 24, 128, /* 32 px @ x128 */ \
|
|
69
|
+
0 }
|
|
70
|
+
MK_BAND(dl_field, 1);
|
|
71
|
+
MK_BAND(dl_ground, 2);
|
|
72
|
+
#define GROUND_ZONE 188
|
|
73
|
+
|
|
74
|
+
static void set_band_addr(uint8_t* dl) {
|
|
75
|
+
uint16_t a = (uint16_t)(uintptr_t)band_pix;
|
|
76
|
+
dl[0] = dl[5] = (uint8_t)(a & 0xFF);
|
|
77
|
+
dl[2] = dl[7] = (uint8_t)(a >> 8);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Background DL for a non-banner zone: sky (empty) up top, field in the
|
|
81
|
+
* middle, ground at the bottom. */
|
|
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
|
+
|
|
50
88
|
#define DLL_ZONES 243
|
|
51
89
|
static uint8_t dll[DLL_ZONES * 3];
|
|
52
90
|
|
|
@@ -71,9 +109,12 @@ static void vblank_wait(void) {
|
|
|
71
109
|
|
|
72
110
|
void main(void) {
|
|
73
111
|
uint16_t dll_addr;
|
|
74
|
-
uint16_t empty = (uint16_t)(uintptr_t)dl_empty;
|
|
75
112
|
int i;
|
|
76
113
|
|
|
114
|
+
/* Point the background bands at their shared ROM pixel row. */
|
|
115
|
+
set_band_addr(dl_field);
|
|
116
|
+
set_band_addr(dl_ground);
|
|
117
|
+
|
|
77
118
|
set_dl_addr(dl_row0, banner_row0);
|
|
78
119
|
set_dl_addr(dl_row1, banner_row1);
|
|
79
120
|
set_dl_addr(dl_row2, banner_row2);
|
|
@@ -83,6 +124,8 @@ void main(void) {
|
|
|
83
124
|
set_dl_addr(dl_row6, banner_row6);
|
|
84
125
|
set_dl_addr(dl_row7, banner_row7);
|
|
85
126
|
|
|
127
|
+
/* Build the DLL: banner rows at BANNER_Y, background scenery everywhere
|
|
128
|
+
* else so the screen isn't an almost-blank flat field. */
|
|
86
129
|
for (i = 0; i < DLL_ZONES; i++) {
|
|
87
130
|
uint16_t dl;
|
|
88
131
|
int d = i - BANNER_Y;
|
|
@@ -95,7 +138,7 @@ void main(void) {
|
|
|
95
138
|
case 5: dl = (uint16_t)(uintptr_t)dl_row5; break;
|
|
96
139
|
case 6: dl = (uint16_t)(uintptr_t)dl_row6; break;
|
|
97
140
|
case 7: dl = (uint16_t)(uintptr_t)dl_row7; break;
|
|
98
|
-
default: dl =
|
|
141
|
+
default: dl = bg_zone_dl(i); break; /* field/ground scenery */
|
|
99
142
|
}
|
|
100
143
|
set_dll_entry(i, dl);
|
|
101
144
|
}
|
|
@@ -104,6 +147,8 @@ void main(void) {
|
|
|
104
147
|
P0C1 = 0x0F; /* white text (palette index 1) */
|
|
105
148
|
P0C2 = 0x0F;
|
|
106
149
|
P0C3 = 0x0F;
|
|
150
|
+
P1C1 = 0xC8; /* field green (background band) */
|
|
151
|
+
P2C1 = 0x14; /* ground brown (background band) */
|
|
107
152
|
CHARBASE = 0;
|
|
108
153
|
OFFSET = 0;
|
|
109
154
|
|