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
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
; ── racing.asm — Atari 2600 RACING genre scaffold (top-down) ──────────
|
|
2
|
+
;
|
|
3
|
+
; The 2600 had a deep racing catalogue — Enduro, Indy 500, Night Driver,
|
|
4
|
+
; Pole Position, Grand Prix. This scaffold is the HONEST 2600 racer: a
|
|
5
|
+
; TOP-DOWN, vertically-scrolling lane racer (the same idiom Enduro uses),
|
|
6
|
+
; NOT a pseudo-3D road — projecting a 3D road needs a per-line table the
|
|
7
|
+
; 4 KB/76-cycle budget can't spare in a starter, and a top-down racer is
|
|
8
|
+
; a fully period-correct, recognizable racing game on its own.
|
|
9
|
+
;
|
|
10
|
+
; TIA object roles:
|
|
11
|
+
; P0 = the player's car (8-px sprite) near the bottom, steers L/R.
|
|
12
|
+
; PF = the road: reflected playfield draws the two ROAD EDGES (left
|
|
13
|
+
; rail mirrors to the right rail) plus a dashed CENTRE LINE that
|
|
14
|
+
; scrolls upward every frame to convey forward speed.
|
|
15
|
+
; P1 = an oncoming/lead car you must avoid (8-px sprite) that drifts
|
|
16
|
+
; down the road; reused each time it passes the bottom, dropped
|
|
17
|
+
; back to the top in a (deterministic) new lane.
|
|
18
|
+
; M0 = a second smaller hazard (a cone / debris) in another lane.
|
|
19
|
+
;
|
|
20
|
+
; Gameplay: hold LEFT/RIGHT on the joystick to weave between the rails;
|
|
21
|
+
; survive the descending traffic. Your SPEED (and the score) ramps up
|
|
22
|
+
; the longer you last — the centre-line dashes scroll faster and the
|
|
23
|
+
; traffic descends faster. A collision (TIA P0-vs-P1 / P0-vs-M0) flashes
|
|
24
|
+
; the screen red and resets your speed. Extend it with M1 as a 3rd
|
|
25
|
+
; hazard, a fuel gauge via a PF bar, or NUSIZ1 for two-abreast traffic.
|
|
26
|
+
;
|
|
27
|
+
; TIMING DISCIPLINE (learned the hard way in paddle.asm):
|
|
28
|
+
; * 262 lines EXACTLY = 3 VSYNC + 37 VBLANK + 192 visible + 30 overscan.
|
|
29
|
+
; * The 3 object-positioning WSYNCs are COUNTED against the 37 VBLANK
|
|
30
|
+
; lines (so the VBLANK delay loop is #34, not #37).
|
|
31
|
+
; * The visible region is a TWO-LINE KERNEL: one scanline of render
|
|
32
|
+
; work (road edges + centre line + one car test) is ~80+ cycles and
|
|
33
|
+
; does NOT fit in 76; splitting across two WSYNCs doubles the budget.
|
|
34
|
+
; 96 passes x 2 lines = 192 visible lines.
|
|
35
|
+
|
|
36
|
+
processor 6502
|
|
37
|
+
org $F000
|
|
38
|
+
|
|
39
|
+
VSYNC = $00
|
|
40
|
+
VBLANK = $01
|
|
41
|
+
WSYNC = $02
|
|
42
|
+
NUSIZ0 = $04
|
|
43
|
+
NUSIZ1 = $05
|
|
44
|
+
COLUP0 = $06
|
|
45
|
+
COLUP1 = $07
|
|
46
|
+
COLUPF = $08
|
|
47
|
+
COLUBK = $09
|
|
48
|
+
CTRLPF = $0A
|
|
49
|
+
PF0 = $0D
|
|
50
|
+
PF1 = $0E
|
|
51
|
+
PF2 = $0F
|
|
52
|
+
RESP0 = $10
|
|
53
|
+
RESP1 = $11
|
|
54
|
+
RESM0 = $12
|
|
55
|
+
GRP0 = $1B
|
|
56
|
+
GRP1 = $1C
|
|
57
|
+
ENAM0 = $1D
|
|
58
|
+
HMP0 = $20
|
|
59
|
+
HMP1 = $21
|
|
60
|
+
HMM0 = $22
|
|
61
|
+
HMOVE = $2A
|
|
62
|
+
HMCLR = $2B
|
|
63
|
+
CXPPMM = $07 ; READ: bit7 = P0/P1 collided
|
|
64
|
+
CXP0FB = $02 ; READ: bit6 = P0/missile-or-ball... we use CXM0P
|
|
65
|
+
CXM0P = $00 ; READ: bit6 = M0/P0 collided
|
|
66
|
+
CXCLR = $2C
|
|
67
|
+
SWCHA = $280
|
|
68
|
+
INPT4 = $0C ; P0 fire (active-low, bit7) — unused here but handy
|
|
69
|
+
; TIA audio
|
|
70
|
+
AUDC0 = $15
|
|
71
|
+
AUDF0 = $17
|
|
72
|
+
AUDV0 = $19
|
|
73
|
+
|
|
74
|
+
; ── Zero-page state ───────────────────────────────────────────────────
|
|
75
|
+
P_X = $80 ; player car X (visible column 0..159)
|
|
76
|
+
E1_X = $81 ; enemy car P1 X
|
|
77
|
+
E1_Y = $82 ; enemy car P1 top scanline (counts with the beam)
|
|
78
|
+
E2_X = $83 ; hazard M0 X
|
|
79
|
+
E2_Y = $84 ; hazard M0 top scanline
|
|
80
|
+
SPEED = $85 ; current speed (1..6) — drives scroll + descent
|
|
81
|
+
SCROLL = $86 ; centre-line dash phase (0..7)
|
|
82
|
+
FRAME = $87
|
|
83
|
+
SCORE = $88 ; distance survived / ramps speed
|
|
84
|
+
SFX_LEFT = $89 ; frames remaining on active sfx
|
|
85
|
+
FLASH = $8A ; >0 = crash flash frames remaining
|
|
86
|
+
TMP = $8B
|
|
87
|
+
|
|
88
|
+
START:
|
|
89
|
+
SEI
|
|
90
|
+
CLD
|
|
91
|
+
LDX #$FF
|
|
92
|
+
TXS
|
|
93
|
+
LDA #0
|
|
94
|
+
.clr:
|
|
95
|
+
STA $00,X
|
|
96
|
+
DEX
|
|
97
|
+
BNE .clr
|
|
98
|
+
|
|
99
|
+
; Initial positions
|
|
100
|
+
LDA #76
|
|
101
|
+
STA P_X ; player mid-road, near bottom
|
|
102
|
+
LDA #50
|
|
103
|
+
STA E1_X
|
|
104
|
+
LDA #170
|
|
105
|
+
STA E1_Y ; enemy car starts up top
|
|
106
|
+
LDA #104
|
|
107
|
+
STA E2_X
|
|
108
|
+
LDA #150
|
|
109
|
+
STA E2_Y
|
|
110
|
+
LDA #1
|
|
111
|
+
STA SPEED
|
|
112
|
+
|
|
113
|
+
; Colours
|
|
114
|
+
LDA #$00 ; black "tarmac" background
|
|
115
|
+
STA COLUBK
|
|
116
|
+
LDA #$1E ; yellow player car
|
|
117
|
+
STA COLUP0
|
|
118
|
+
LDA #$36 ; pink/red oncoming car (also colours M0 hazard)
|
|
119
|
+
STA COLUP1
|
|
120
|
+
LDA #$0E ; white road markings
|
|
121
|
+
STA COLUPF
|
|
122
|
+
|
|
123
|
+
; M0 hazard shares P0 colour normally; we want it to read as debris.
|
|
124
|
+
; Make it 2px wide.
|
|
125
|
+
LDA #%00010000 ; NUSIZ0: missile 2x wide (bits 4-5), P0 single
|
|
126
|
+
STA NUSIZ0
|
|
127
|
+
|
|
128
|
+
; Playfield: reflected so the left rail mirrors to a right rail, and
|
|
129
|
+
; SCORE_COLOR priority not needed. CTRLPF bit0 = reflect.
|
|
130
|
+
LDA #%00000001
|
|
131
|
+
STA CTRLPF
|
|
132
|
+
|
|
133
|
+
; Boot chime — confirms TIA audio is wired (engine "rev").
|
|
134
|
+
LDA #$03
|
|
135
|
+
STA AUDC0
|
|
136
|
+
LDA #$0A
|
|
137
|
+
STA AUDF0
|
|
138
|
+
LDA #$0C
|
|
139
|
+
STA AUDV0
|
|
140
|
+
LDA #18
|
|
141
|
+
STA SFX_LEFT
|
|
142
|
+
|
|
143
|
+
MAIN:
|
|
144
|
+
INC FRAME
|
|
145
|
+
|
|
146
|
+
; ── VSYNC (3 lines) ──
|
|
147
|
+
LDA #2
|
|
148
|
+
STA VSYNC
|
|
149
|
+
STA WSYNC
|
|
150
|
+
STA WSYNC
|
|
151
|
+
STA WSYNC
|
|
152
|
+
LDA #0
|
|
153
|
+
STA VSYNC
|
|
154
|
+
|
|
155
|
+
; ── VBLANK (37 lines: 34 here + 3 positioning WSYNCs below) ──
|
|
156
|
+
LDA #2
|
|
157
|
+
STA VBLANK
|
|
158
|
+
LDX #34
|
|
159
|
+
.vb:
|
|
160
|
+
STA WSYNC
|
|
161
|
+
DEX
|
|
162
|
+
BNE .vb
|
|
163
|
+
|
|
164
|
+
; ── Steering: joystick port A left/right, every 2nd frame ──
|
|
165
|
+
LDA FRAME
|
|
166
|
+
AND #$01
|
|
167
|
+
BNE .skipmove
|
|
168
|
+
LDA SWCHA
|
|
169
|
+
ASL ; bit7 = P0 Right
|
|
170
|
+
BCS .nr
|
|
171
|
+
LDA P_X
|
|
172
|
+
CMP #128
|
|
173
|
+
BCS .nr
|
|
174
|
+
INC P_X
|
|
175
|
+
INC P_X
|
|
176
|
+
.nr:
|
|
177
|
+
ASL ; bit6 = P0 Left
|
|
178
|
+
BCS .nl
|
|
179
|
+
LDA P_X
|
|
180
|
+
CMP #28
|
|
181
|
+
BCC .nl
|
|
182
|
+
DEC P_X
|
|
183
|
+
DEC P_X
|
|
184
|
+
.nl:
|
|
185
|
+
.skipmove:
|
|
186
|
+
|
|
187
|
+
; ── Crash flash countdown ──
|
|
188
|
+
LDA FLASH
|
|
189
|
+
BEQ .noflash
|
|
190
|
+
DEC FLASH
|
|
191
|
+
.noflash:
|
|
192
|
+
|
|
193
|
+
; ── Scroll the dashed centre line upward at SPEED px/frame ──
|
|
194
|
+
; SCROLL is the phase 0..7; subtract SPEED, wrap mod 8.
|
|
195
|
+
LDA SCROLL
|
|
196
|
+
SEC
|
|
197
|
+
SBC SPEED
|
|
198
|
+
AND #$07
|
|
199
|
+
STA SCROLL
|
|
200
|
+
|
|
201
|
+
; ── Descend traffic at SPEED px/frame (smaller Y = lower on screen,
|
|
202
|
+
; because Y counts 192->1 with the beam). So descending = DEC Y. ──
|
|
203
|
+
LDA E1_Y
|
|
204
|
+
SEC
|
|
205
|
+
SBC SPEED
|
|
206
|
+
STA E1_Y
|
|
207
|
+
CMP #20 ; passed the bottom?
|
|
208
|
+
BCS .e1ok
|
|
209
|
+
; recycle to top, new deterministic lane from FRAME
|
|
210
|
+
LDA #186
|
|
211
|
+
STA E1_Y
|
|
212
|
+
LDA FRAME
|
|
213
|
+
AND #$3F
|
|
214
|
+
CLC
|
|
215
|
+
ADC #40
|
|
216
|
+
STA E1_X
|
|
217
|
+
INC SCORE ; survived a car
|
|
218
|
+
; ramp speed every time SCORE crosses a multiple of 4 (cap at 6)
|
|
219
|
+
LDA SCORE
|
|
220
|
+
AND #$03
|
|
221
|
+
BNE .e1ok
|
|
222
|
+
LDA SPEED
|
|
223
|
+
CMP #6
|
|
224
|
+
BCS .e1ok
|
|
225
|
+
INC SPEED
|
|
226
|
+
; speed-up "rev" sfx
|
|
227
|
+
LDA #$03
|
|
228
|
+
STA AUDC0
|
|
229
|
+
LDA #$08
|
|
230
|
+
STA AUDF0
|
|
231
|
+
LDA #$0C
|
|
232
|
+
STA AUDV0
|
|
233
|
+
LDA #10
|
|
234
|
+
STA SFX_LEFT
|
|
235
|
+
.e1ok:
|
|
236
|
+
|
|
237
|
+
; Hazard M0 descends a touch faster (SPEED+1).
|
|
238
|
+
LDA E2_Y
|
|
239
|
+
SEC
|
|
240
|
+
SBC SPEED
|
|
241
|
+
SBC #0 ; (placeholder; SPEED already applied)
|
|
242
|
+
STA E2_Y
|
|
243
|
+
CMP #18
|
|
244
|
+
BCS .e2ok
|
|
245
|
+
LDA #182
|
|
246
|
+
STA E2_Y
|
|
247
|
+
LDA FRAME
|
|
248
|
+
EOR #$5A
|
|
249
|
+
AND #$3F
|
|
250
|
+
CLC
|
|
251
|
+
ADC #36
|
|
252
|
+
STA E2_X
|
|
253
|
+
.e2ok:
|
|
254
|
+
|
|
255
|
+
; ── Collision check (read TIA collision latches from LAST frame) ──
|
|
256
|
+
; P0 vs P1 → CXPPMM bit7. M0 vs P0 → CXM0P bit6.
|
|
257
|
+
BIT CXPPMM
|
|
258
|
+
BMI .crash ; bit7 set = P0/P1 overlapped
|
|
259
|
+
BIT CXM0P
|
|
260
|
+
BVS .crash ; bit6 set = M0/P0 overlapped
|
|
261
|
+
JMP .nocrash
|
|
262
|
+
.crash:
|
|
263
|
+
; Reset speed, flash the screen, recycle the offending traffic up top,
|
|
264
|
+
; play a crash tone.
|
|
265
|
+
LDA #1
|
|
266
|
+
STA SPEED
|
|
267
|
+
LDA #12
|
|
268
|
+
STA FLASH
|
|
269
|
+
LDA #186
|
|
270
|
+
STA E1_Y
|
|
271
|
+
LDA #182
|
|
272
|
+
STA E2_Y
|
|
273
|
+
LDA #$08 ; noisy crash
|
|
274
|
+
STA AUDC0
|
|
275
|
+
LDA #$1F
|
|
276
|
+
STA AUDF0
|
|
277
|
+
LDA #$0F
|
|
278
|
+
STA AUDV0
|
|
279
|
+
LDA #14
|
|
280
|
+
STA SFX_LEFT
|
|
281
|
+
.nocrash:
|
|
282
|
+
STA CXCLR ; clear collision latches for the next frame
|
|
283
|
+
|
|
284
|
+
; ── sfx countdown ──
|
|
285
|
+
LDA SFX_LEFT
|
|
286
|
+
BEQ .sfxdone
|
|
287
|
+
DEC SFX_LEFT
|
|
288
|
+
BNE .sfxdone
|
|
289
|
+
LDA #0
|
|
290
|
+
STA AUDV0
|
|
291
|
+
.sfxdone:
|
|
292
|
+
|
|
293
|
+
; ── Position objects (race-the-beam) — 3 WSYNC-bounded lines ──
|
|
294
|
+
; Use the divide-by-15 coarse approach (good enough for a starter; the
|
|
295
|
+
; cars sit on lane centres). HMCLR first so stale HM values don't drift.
|
|
296
|
+
STA WSYNC
|
|
297
|
+
STA HMCLR
|
|
298
|
+
LDX P_X
|
|
299
|
+
LDA #0
|
|
300
|
+
.p0pos:
|
|
301
|
+
CPX #15
|
|
302
|
+
BCC .p0done
|
|
303
|
+
SEC
|
|
304
|
+
SBC #15
|
|
305
|
+
TAX
|
|
306
|
+
JMP .p0pos
|
|
307
|
+
.p0done:
|
|
308
|
+
STA RESP0
|
|
309
|
+
; P1 (enemy car)
|
|
310
|
+
STA WSYNC
|
|
311
|
+
LDX E1_X
|
|
312
|
+
LDA #0
|
|
313
|
+
.p1pos:
|
|
314
|
+
CPX #15
|
|
315
|
+
BCC .p1done
|
|
316
|
+
SEC
|
|
317
|
+
SBC #15
|
|
318
|
+
TAX
|
|
319
|
+
JMP .p1pos
|
|
320
|
+
.p1done:
|
|
321
|
+
STA RESP1
|
|
322
|
+
; M0 (hazard)
|
|
323
|
+
STA WSYNC
|
|
324
|
+
LDX E2_X
|
|
325
|
+
LDA #0
|
|
326
|
+
.m0pos:
|
|
327
|
+
CPX #15
|
|
328
|
+
BCC .m0done
|
|
329
|
+
SEC
|
|
330
|
+
SBC #15
|
|
331
|
+
TAX
|
|
332
|
+
JMP .m0pos
|
|
333
|
+
.m0done:
|
|
334
|
+
STA RESM0
|
|
335
|
+
STA HMOVE
|
|
336
|
+
|
|
337
|
+
; Crash flash: paint background dark-red while FLASH active.
|
|
338
|
+
LDA FLASH
|
|
339
|
+
BEQ .bgblack
|
|
340
|
+
LDA #$42 ; dark red
|
|
341
|
+
STA COLUBK
|
|
342
|
+
JMP .bgdone
|
|
343
|
+
.bgblack:
|
|
344
|
+
LDA #$00
|
|
345
|
+
STA COLUBK
|
|
346
|
+
.bgdone:
|
|
347
|
+
|
|
348
|
+
LDA #0
|
|
349
|
+
STA VBLANK
|
|
350
|
+
|
|
351
|
+
; ── Visible (192 lines) — TWO-LINE KERNEL ──
|
|
352
|
+
; Each pass renders TWO scanlines:
|
|
353
|
+
; line A: road edges (PF) + scrolling centre dash (PF) + player car.
|
|
354
|
+
; line B: enemy car (P1) + hazard (M0).
|
|
355
|
+
; Y counts 192 -> 2 in steps of 2. 96 passes = 192 lines.
|
|
356
|
+
LDY #192
|
|
357
|
+
.draw:
|
|
358
|
+
; ---- line A: road + player car ----
|
|
359
|
+
STA WSYNC
|
|
360
|
+
; Road rails via PF0 (reflected → left+right rails). The rails are the
|
|
361
|
+
; OUTER playfield pixels; PF0's high nibble shows on screen pixels
|
|
362
|
+
; 4..7 (the leftmost visible chunk after the 4-px PF0 gap), mirrored to
|
|
363
|
+
; the right edge by CTRLPF reflect. A constant rail every line.
|
|
364
|
+
LDA #%00010000 ; one rail bar on each side
|
|
365
|
+
STA PF0
|
|
366
|
+
; Centre dash via PF2 — a dash that appears on some scanline groups,
|
|
367
|
+
; phased by SCROLL so it crawls upward. (Y+SCROLL)&8 picks dash on/off.
|
|
368
|
+
TYA
|
|
369
|
+
CLC
|
|
370
|
+
ADC SCROLL
|
|
371
|
+
AND #%00001000
|
|
372
|
+
BEQ .nodash
|
|
373
|
+
LDA #%00011000 ; centre pixels of PF2 (maps near screen middle)
|
|
374
|
+
STA PF2
|
|
375
|
+
JMP .dashdone
|
|
376
|
+
.nodash:
|
|
377
|
+
LDA #0
|
|
378
|
+
STA PF2
|
|
379
|
+
.dashdone:
|
|
380
|
+
; Player car: 8 rows starting at P_Y region near the bottom (~Y 30..22).
|
|
381
|
+
; Window: (Y - 22) in [0..7] → index CAR bitmap.
|
|
382
|
+
TYA
|
|
383
|
+
SEC
|
|
384
|
+
SBC #22
|
|
385
|
+
CMP #8
|
|
386
|
+
BCS .pblank
|
|
387
|
+
TAX
|
|
388
|
+
LDA CAR,X
|
|
389
|
+
STA GRP0
|
|
390
|
+
JMP .pdone
|
|
391
|
+
.pblank:
|
|
392
|
+
LDA #0
|
|
393
|
+
STA GRP0
|
|
394
|
+
.pdone:
|
|
395
|
+
|
|
396
|
+
; ---- line B: enemy car + hazard ----
|
|
397
|
+
STA WSYNC
|
|
398
|
+
; Enemy car P1: 8 rows starting at E1_Y.
|
|
399
|
+
TYA
|
|
400
|
+
SEC
|
|
401
|
+
SBC E1_Y
|
|
402
|
+
CMP #8
|
|
403
|
+
BCS .eblank
|
|
404
|
+
TAX
|
|
405
|
+
LDA CAR,X
|
|
406
|
+
STA GRP1
|
|
407
|
+
JMP .edone
|
|
408
|
+
.eblank:
|
|
409
|
+
LDA #0
|
|
410
|
+
STA GRP1
|
|
411
|
+
.edone:
|
|
412
|
+
; Hazard M0: enable for 4 rows around E2_Y.
|
|
413
|
+
TYA
|
|
414
|
+
SEC
|
|
415
|
+
SBC E2_Y
|
|
416
|
+
CMP #4
|
|
417
|
+
BCS .hblank
|
|
418
|
+
LDA #2
|
|
419
|
+
STA ENAM0
|
|
420
|
+
JMP .hdone
|
|
421
|
+
.hblank:
|
|
422
|
+
LDA #0
|
|
423
|
+
STA ENAM0
|
|
424
|
+
.hdone:
|
|
425
|
+
|
|
426
|
+
DEY
|
|
427
|
+
DEY
|
|
428
|
+
BNE .draw
|
|
429
|
+
|
|
430
|
+
; ── Overscan (30 lines) — clear the playfield so it doesn't bleed ──
|
|
431
|
+
LDA #0
|
|
432
|
+
STA PF0
|
|
433
|
+
STA PF1
|
|
434
|
+
STA PF2
|
|
435
|
+
STA GRP0
|
|
436
|
+
STA GRP1
|
|
437
|
+
STA ENAM0
|
|
438
|
+
LDA #2
|
|
439
|
+
STA VBLANK
|
|
440
|
+
LDX #30
|
|
441
|
+
.os:
|
|
442
|
+
STA WSYNC
|
|
443
|
+
DEX
|
|
444
|
+
BNE .os
|
|
445
|
+
|
|
446
|
+
JMP MAIN
|
|
447
|
+
|
|
448
|
+
; ── 8-row top-down car silhouette (windshield + body) ──
|
|
449
|
+
CAR:
|
|
450
|
+
.byte %00111100
|
|
451
|
+
.byte %01111110
|
|
452
|
+
.byte %01011010
|
|
453
|
+
.byte %01111110
|
|
454
|
+
.byte %11111111
|
|
455
|
+
.byte %11111111
|
|
456
|
+
.byte %01011010
|
|
457
|
+
.byte %01111110
|
|
458
|
+
|
|
459
|
+
; ── Vector table ──
|
|
460
|
+
org $FFFA
|
|
461
|
+
.word START
|
|
462
|
+
.word START
|
|
463
|
+
.word START
|