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,460 @@
|
|
|
1
|
+
; ── platformer.asm — Atari 2600 PLATFORMER genre scaffold ─────────────
|
|
2
|
+
;
|
|
3
|
+
; SINGLE-SCREEN platformer. Pitfall!, Montezuma's Revenge, and Kangaroo
|
|
4
|
+
; are 2600 platformers, and they are single-screen-at-a-time: the 2600
|
|
5
|
+
; has NO hardware scroll, no tilemap, and 128 bytes of RAM, so a smooth
|
|
6
|
+
; side-scroller is not the honest 2600 idiom (you'd flip whole screens,
|
|
7
|
+
; Pitfall-style, instead). What IS idiomatic — and what this scaffold
|
|
8
|
+
; ships — is gravity + a jump arc + land-on-top collision against a set
|
|
9
|
+
; of fixed platforms, all on one screen.
|
|
10
|
+
;
|
|
11
|
+
; TIA object roles:
|
|
12
|
+
; P0 = the player (8-px sprite) that walks + jumps.
|
|
13
|
+
; PF = the platforms (and the floor): three horizontal playfield bars
|
|
14
|
+
; at fixed Y bands. The playfield is the only 2600 object wide
|
|
15
|
+
; enough to be a platform; players/missiles are too narrow.
|
|
16
|
+
;
|
|
17
|
+
; Physics (fixed-point Y, 1 sub-pixel bit):
|
|
18
|
+
; * Gravity pulls the player down every frame (velocity += g).
|
|
19
|
+
; * Pressing FIRE while standing on a surface launches a jump
|
|
20
|
+
; (velocity = -jump).
|
|
21
|
+
; * After moving, we test the player's FEET against each platform's
|
|
22
|
+
; (Y, x-span) in CODE — not TIA collision — because we need to know
|
|
23
|
+
; WHICH surface to stand on and TIA only gives a yes/no overlap.
|
|
24
|
+
; * Walk left/right with the joystick; you can't walk off the screen.
|
|
25
|
+
;
|
|
26
|
+
; This is the jump/gravity/collision CORE. Extend it with: a second
|
|
27
|
+
; sprite (P1) as a pickup or enemy, M0 as a thrown rock, ladders (let
|
|
28
|
+
; UP/DOWN move Y when overlapping a ladder x-span), or Pitfall-style
|
|
29
|
+
; screen flipping when the player exits the left/right edge.
|
|
30
|
+
;
|
|
31
|
+
; TIMING: 262 lines = 3 VSYNC + 37 VBLANK + 192 visible + 30 overscan.
|
|
32
|
+
; One positioning WSYNC is counted against VBLANK (loop = #36). The
|
|
33
|
+
; visible region is a TWO-LINE KERNEL so the per-pass work (platform PF
|
|
34
|
+
; lookup + player-sprite test) fits the cycle budget.
|
|
35
|
+
|
|
36
|
+
processor 6502
|
|
37
|
+
org $F000
|
|
38
|
+
|
|
39
|
+
VSYNC = $00
|
|
40
|
+
VBLANK = $01
|
|
41
|
+
WSYNC = $02
|
|
42
|
+
NUSIZ0 = $04
|
|
43
|
+
COLUP0 = $06
|
|
44
|
+
COLUPF = $08
|
|
45
|
+
COLUBK = $09
|
|
46
|
+
CTRLPF = $0A
|
|
47
|
+
PF0 = $0D
|
|
48
|
+
PF1 = $0E
|
|
49
|
+
PF2 = $0F
|
|
50
|
+
RESP0 = $10
|
|
51
|
+
GRP0 = $1B
|
|
52
|
+
HMP0 = $20
|
|
53
|
+
HMOVE = $2A
|
|
54
|
+
HMCLR = $2B
|
|
55
|
+
SWCHA = $280
|
|
56
|
+
INPT4 = $0C ; fire button (active-low, bit7) = JUMP
|
|
57
|
+
; TIA audio
|
|
58
|
+
AUDC0 = $15
|
|
59
|
+
AUDF0 = $17
|
|
60
|
+
AUDV0 = $19
|
|
61
|
+
|
|
62
|
+
; ── Zero-page state ───────────────────────────────────────────────────
|
|
63
|
+
P_X = $80 ; player X (visible column)
|
|
64
|
+
P_Y = $81 ; player top scanline (integer part). Y counts with
|
|
65
|
+
; the beam 192->0, so SMALLER = LOWER on screen.
|
|
66
|
+
P_VY = $82 ; vertical velocity, signed, in half-pixels (8.1)
|
|
67
|
+
P_YSUB = $83 ; (spare — physics is integer-pixel; kept for layout)
|
|
68
|
+
ON_GND = $84 ; 1 = standing on a surface (can jump)
|
|
69
|
+
FRAME = $85
|
|
70
|
+
SFX_LEFT = $86
|
|
71
|
+
TMP = $87
|
|
72
|
+
LANDY = $88 ; the Y we snap to when we land
|
|
73
|
+
; PFROW: 96-byte playfield row buffer (one entry per 2-line kernel row),
|
|
74
|
+
; built ONCE at boot from the platform table. $89..$E8. Stack ($FF down)
|
|
75
|
+
; has $E9..$FF free (23 bytes) — ample, since the only JSR is the one-shot
|
|
76
|
+
; build_pfrow and the kernel itself calls nothing.
|
|
77
|
+
PFROW = $89
|
|
78
|
+
|
|
79
|
+
; Player height (sprite rows).
|
|
80
|
+
PH = 8
|
|
81
|
+
|
|
82
|
+
; ── Platform table ────────────────────────────────────────────────────
|
|
83
|
+
; Each platform = (top-band scanline Y, x-left, x-right). The visuals are
|
|
84
|
+
; FULL-WIDTH horizontal bars (cheap + reads cleanly), so the x-spans below
|
|
85
|
+
; are the whole screen and the land-on-top test lets you stand anywhere on
|
|
86
|
+
; a platform. Beam Y counts 192(top)->1(bottom): a LARGER Y value sits
|
|
87
|
+
; HIGHER on the screen, so PLAT_Y=18 is the bottom FLOOR and PLAT_Y=150 is
|
|
88
|
+
; the top ledge.
|
|
89
|
+
; floor : Y=18 (bottom, full width)
|
|
90
|
+
; ledge : Y=70
|
|
91
|
+
; ledge : Y=110
|
|
92
|
+
; ledge : Y=150 (highest)
|
|
93
|
+
NUM_PLAT = 4
|
|
94
|
+
|
|
95
|
+
START:
|
|
96
|
+
SEI
|
|
97
|
+
CLD
|
|
98
|
+
LDX #$FF
|
|
99
|
+
TXS
|
|
100
|
+
LDA #0
|
|
101
|
+
.clr:
|
|
102
|
+
STA $00,X
|
|
103
|
+
DEX
|
|
104
|
+
BNE .clr
|
|
105
|
+
|
|
106
|
+
; Start the player standing on the floor.
|
|
107
|
+
LDA #76
|
|
108
|
+
STA P_X
|
|
109
|
+
LDA #26 ; just above the floor band (floor top = 18, +PH)
|
|
110
|
+
STA P_Y
|
|
111
|
+
LDA #1
|
|
112
|
+
STA ON_GND
|
|
113
|
+
|
|
114
|
+
; Colours
|
|
115
|
+
LDA #$84 ; blue sky background
|
|
116
|
+
STA COLUBK
|
|
117
|
+
LDA #$1E ; yellow player
|
|
118
|
+
STA COLUP0
|
|
119
|
+
LDA #$28 ; brown/orange platforms
|
|
120
|
+
STA COLUPF
|
|
121
|
+
|
|
122
|
+
; Reflected playfield (harmless for full-width bars; left set so that if
|
|
123
|
+
; you switch ledges to partial patterns later they mirror symmetrically).
|
|
124
|
+
LDA #%00000001
|
|
125
|
+
STA CTRLPF
|
|
126
|
+
|
|
127
|
+
; Build the static playfield row buffer ONCE (platforms never move).
|
|
128
|
+
JSR build_pfrow
|
|
129
|
+
|
|
130
|
+
; Boot chime.
|
|
131
|
+
LDA #$04
|
|
132
|
+
STA AUDC0
|
|
133
|
+
LDA #$0C
|
|
134
|
+
STA AUDF0
|
|
135
|
+
LDA #$0F
|
|
136
|
+
STA AUDV0
|
|
137
|
+
LDA #15
|
|
138
|
+
STA SFX_LEFT
|
|
139
|
+
|
|
140
|
+
MAIN:
|
|
141
|
+
INC FRAME
|
|
142
|
+
|
|
143
|
+
; ── VSYNC (3 lines) ──
|
|
144
|
+
LDA #2
|
|
145
|
+
STA VSYNC
|
|
146
|
+
STA WSYNC
|
|
147
|
+
STA WSYNC
|
|
148
|
+
STA WSYNC
|
|
149
|
+
LDA #0
|
|
150
|
+
STA VSYNC
|
|
151
|
+
|
|
152
|
+
; ── VBLANK (37 lines: 36 here + 1 positioning WSYNC below) ──
|
|
153
|
+
LDA #2
|
|
154
|
+
STA VBLANK
|
|
155
|
+
LDX #36
|
|
156
|
+
.vb:
|
|
157
|
+
STA WSYNC
|
|
158
|
+
DEX
|
|
159
|
+
BNE .vb
|
|
160
|
+
|
|
161
|
+
; ── Horizontal move (every 2nd frame to throttle) ──
|
|
162
|
+
LDA FRAME
|
|
163
|
+
AND #$01
|
|
164
|
+
BNE .skipmove
|
|
165
|
+
LDA SWCHA
|
|
166
|
+
ASL ; bit7 = Right
|
|
167
|
+
BCS .nr
|
|
168
|
+
LDA P_X
|
|
169
|
+
CMP #140
|
|
170
|
+
BCS .nr
|
|
171
|
+
INC P_X
|
|
172
|
+
INC P_X
|
|
173
|
+
.nr:
|
|
174
|
+
ASL ; bit6 = Left
|
|
175
|
+
BCS .nl
|
|
176
|
+
LDA P_X
|
|
177
|
+
CMP #16
|
|
178
|
+
BCC .nl
|
|
179
|
+
DEC P_X
|
|
180
|
+
DEC P_X
|
|
181
|
+
.nl:
|
|
182
|
+
.skipmove:
|
|
183
|
+
|
|
184
|
+
; ── Jump: FIRE while on the ground launches an upward velocity ──
|
|
185
|
+
; Coordinate reminder: Y is the BEAM scanline; the top of the screen is
|
|
186
|
+
; the LARGER Y. So "up" = INCREASING P_Y, and a positive P_VY rises.
|
|
187
|
+
; P_VY is signed WHOLE PIXELS/frame (no sub-pixel — integer motion looks
|
|
188
|
+
; perfectly fine for a 2600 jump and avoids a fractional-carry bug where
|
|
189
|
+
; small half-pixel velocities never accumulate a whole pixel).
|
|
190
|
+
LDA ON_GND
|
|
191
|
+
BEQ .nojump
|
|
192
|
+
BIT INPT4
|
|
193
|
+
BMI .nojump ; bit7 set = button released
|
|
194
|
+
LDA #6 ; initial jump speed (pixels/frame, upward)
|
|
195
|
+
STA P_VY
|
|
196
|
+
LDA #0
|
|
197
|
+
STA ON_GND
|
|
198
|
+
; jump sfx
|
|
199
|
+
LDA #$0C
|
|
200
|
+
STA AUDC0
|
|
201
|
+
LDA #$14
|
|
202
|
+
STA AUDF0
|
|
203
|
+
LDA #$0F
|
|
204
|
+
STA AUDV0
|
|
205
|
+
LDA #6
|
|
206
|
+
STA SFX_LEFT
|
|
207
|
+
.nojump:
|
|
208
|
+
|
|
209
|
+
; ── Gravity + integrate vertical velocity (only while airborne) ──
|
|
210
|
+
; Standing still on a platform we DON'T apply gravity (otherwise the
|
|
211
|
+
; player drops 1px every frame and the landing snap fights it → jitter).
|
|
212
|
+
LDA ON_GND
|
|
213
|
+
BNE .skipgrav
|
|
214
|
+
DEC P_VY ; gravity: velocity drifts toward falling each frame
|
|
215
|
+
; clamp terminal fall speed to -8 px/frame (P_VY is signed; -8 = $F8).
|
|
216
|
+
LDA P_VY
|
|
217
|
+
CMP #$F8 ; if P_VY (unsigned) < $F8 AND it's negative → too fast
|
|
218
|
+
BCS .vyok ; >= $F8 (covers -8..-1 and 0..127) → keep
|
|
219
|
+
; here P_VY is in $80..$F7 = -128..-9 → clamp to -8
|
|
220
|
+
LDA #$F8
|
|
221
|
+
STA P_VY
|
|
222
|
+
.vyok:
|
|
223
|
+
; P_Y += P_VY (signed add: sign-extend P_VY into the add)
|
|
224
|
+
LDA P_VY
|
|
225
|
+
CLC
|
|
226
|
+
ADC P_Y
|
|
227
|
+
STA P_Y
|
|
228
|
+
.skipgrav:
|
|
229
|
+
|
|
230
|
+
; ── Land-on-top collision against the platform table ──
|
|
231
|
+
; Only while DESCENDING (P_VY negative = moving down the screen). For
|
|
232
|
+
; each platform, the stand-line = PLAT_Y + PH (the player's feet rest
|
|
233
|
+
; just above the band's top edge). If the player's feet (P_Y) have
|
|
234
|
+
; reached or just dropped through that line from above, and X is within
|
|
235
|
+
; the platform's span, snap onto it.
|
|
236
|
+
LDA P_VY
|
|
237
|
+
BPL .noland ; rising or stationary → can't land this frame
|
|
238
|
+
LDX #0
|
|
239
|
+
.landloop:
|
|
240
|
+
LDA PLAT_Y,X
|
|
241
|
+
CLC
|
|
242
|
+
ADC #PH ; stand-line for this platform
|
|
243
|
+
STA LANDY
|
|
244
|
+
; player at/below the stand-line? (P_Y <= LANDY, i.e. NOT P_Y > LANDY)
|
|
245
|
+
LDA P_Y
|
|
246
|
+
CMP LANDY
|
|
247
|
+
BEQ .ydepth ; exactly on it
|
|
248
|
+
BCS .nextplat ; P_Y > LANDY → still above the surface → no land
|
|
249
|
+
.ydepth:
|
|
250
|
+
; not fallen WAY past it (avoid grabbing a platform from underneath):
|
|
251
|
+
; require LANDY - P_Y <= 12.
|
|
252
|
+
LDA LANDY
|
|
253
|
+
SEC
|
|
254
|
+
SBC P_Y
|
|
255
|
+
CMP #13
|
|
256
|
+
BCS .nextplat ; dropped >12px below → ignore
|
|
257
|
+
; x-span test: PLAT_XL <= P_X <= PLAT_XR
|
|
258
|
+
LDA P_X
|
|
259
|
+
CMP PLAT_XL,X
|
|
260
|
+
BCC .nextplat
|
|
261
|
+
CMP PLAT_XR,X
|
|
262
|
+
BCS .nextplat ; (XR=159 + the +1 makes the whole row standable)
|
|
263
|
+
; LAND!
|
|
264
|
+
LDA LANDY
|
|
265
|
+
STA P_Y
|
|
266
|
+
LDA #0
|
|
267
|
+
STA P_VY
|
|
268
|
+
LDA #1
|
|
269
|
+
STA ON_GND
|
|
270
|
+
JMP .landdone
|
|
271
|
+
.nextplat:
|
|
272
|
+
INX
|
|
273
|
+
CPX #NUM_PLAT
|
|
274
|
+
BNE .landloop
|
|
275
|
+
; matched nothing → still airborne
|
|
276
|
+
LDA #0
|
|
277
|
+
STA ON_GND
|
|
278
|
+
.landdone:
|
|
279
|
+
.noland:
|
|
280
|
+
|
|
281
|
+
; Safety floor: never let the player fall off the bottom of the world.
|
|
282
|
+
LDA P_Y
|
|
283
|
+
CMP #18
|
|
284
|
+
BCS .floorok
|
|
285
|
+
LDA #26
|
|
286
|
+
STA P_Y
|
|
287
|
+
LDA #0
|
|
288
|
+
STA P_VY
|
|
289
|
+
LDA #1
|
|
290
|
+
STA ON_GND
|
|
291
|
+
.floorok:
|
|
292
|
+
|
|
293
|
+
; ── sfx countdown ──
|
|
294
|
+
LDA SFX_LEFT
|
|
295
|
+
BEQ .sfxdone
|
|
296
|
+
DEC SFX_LEFT
|
|
297
|
+
BNE .sfxdone
|
|
298
|
+
LDA #0
|
|
299
|
+
STA AUDV0
|
|
300
|
+
.sfxdone:
|
|
301
|
+
|
|
302
|
+
; ── Position P0 at column P_X (1 WSYNC, counted in VBLANK) ──
|
|
303
|
+
STA WSYNC
|
|
304
|
+
STA HMCLR
|
|
305
|
+
LDX P_X
|
|
306
|
+
LDA #0
|
|
307
|
+
.p0pos:
|
|
308
|
+
CPX #15
|
|
309
|
+
BCC .p0done
|
|
310
|
+
SEC
|
|
311
|
+
SBC #15
|
|
312
|
+
TAX
|
|
313
|
+
JMP .p0pos
|
|
314
|
+
.p0done:
|
|
315
|
+
STA RESP0
|
|
316
|
+
STA HMOVE
|
|
317
|
+
|
|
318
|
+
LDA #0
|
|
319
|
+
STA VBLANK
|
|
320
|
+
|
|
321
|
+
; ── Visible (192 lines) — SINGLE-LINE KERNEL reading a PF row buffer ──
|
|
322
|
+
; CRITICAL CYCLE NOTE: the platforms are STATIC, so we DON'T recompute
|
|
323
|
+
; them per scanline (a per-line JSR over the platform table overflowed
|
|
324
|
+
; the 76-cycle budget → frames grew to ~250 lines → no vsync lock →
|
|
325
|
+
; black rolling screen — the bug this kernel fixes). Instead PFROW[] is
|
|
326
|
+
; a 96-byte buffer (one entry per 2-line row) filled ONCE at boot from
|
|
327
|
+
; the platform table: $FF = platform here, $00 = open air. The kernel
|
|
328
|
+
; just LDA PFROW,X / STA PF1 / STA PF2 (cheap) + a single player-sprite
|
|
329
|
+
; test. That comfortably fits one scanline.
|
|
330
|
+
;
|
|
331
|
+
; X = row index 0..95 (top→bottom in buffer order). Y = beam scanline
|
|
332
|
+
; 192→1. We draw two scanlines per buffer row.
|
|
333
|
+
LDX #0 ; PFROW index
|
|
334
|
+
LDY #192
|
|
335
|
+
.draw:
|
|
336
|
+
STA WSYNC
|
|
337
|
+
; --- playfield for this row (full-width bars) ---
|
|
338
|
+
LDA PFROW,X
|
|
339
|
+
STA PF0
|
|
340
|
+
STA PF1
|
|
341
|
+
STA PF2
|
|
342
|
+
; --- player sprite test ---
|
|
343
|
+
TYA
|
|
344
|
+
SEC
|
|
345
|
+
SBC P_Y
|
|
346
|
+
CMP #PH
|
|
347
|
+
BCS .pblank
|
|
348
|
+
STY TMP ; save beam line
|
|
349
|
+
TAY
|
|
350
|
+
LDA PLAYER,Y
|
|
351
|
+
STA GRP0
|
|
352
|
+
LDY TMP
|
|
353
|
+
JMP .pdone
|
|
354
|
+
.pblank:
|
|
355
|
+
LDA #0
|
|
356
|
+
STA GRP0
|
|
357
|
+
.pdone:
|
|
358
|
+
DEY
|
|
359
|
+
; second scanline of this row — reuse same PF, re-test the sprite.
|
|
360
|
+
STA WSYNC
|
|
361
|
+
STA GRP0 ; (A still holds the sprite/blank byte from above —
|
|
362
|
+
; good enough; sprite is effectively 2px tall rows)
|
|
363
|
+
DEY
|
|
364
|
+
INX
|
|
365
|
+
CPX #96
|
|
366
|
+
BNE .draw
|
|
367
|
+
|
|
368
|
+
; ── Overscan (30 lines) ──
|
|
369
|
+
LDA #0
|
|
370
|
+
STA PF0
|
|
371
|
+
STA PF1
|
|
372
|
+
STA PF2
|
|
373
|
+
STA GRP0
|
|
374
|
+
LDA #2
|
|
375
|
+
STA VBLANK
|
|
376
|
+
LDX #30
|
|
377
|
+
.os:
|
|
378
|
+
STA WSYNC
|
|
379
|
+
DEX
|
|
380
|
+
BNE .os
|
|
381
|
+
|
|
382
|
+
JMP MAIN
|
|
383
|
+
|
|
384
|
+
; ── build_pfrow: fill the 96-byte PFROW buffer from the platform table.
|
|
385
|
+
; Called ONCE at boot. Row r covers beam scanlines (192 - 2*r) down to
|
|
386
|
+
; (191 - 2*r). A row is a platform if its top scanline falls within any
|
|
387
|
+
; platform's PLAT_Y..PLAT_Y+(bandHeight) window. ──
|
|
388
|
+
PF_BAND = 8 ; platform visual thickness in scanlines
|
|
389
|
+
build_pfrow:
|
|
390
|
+
LDX #0 ; row index 0..95
|
|
391
|
+
.brow:
|
|
392
|
+
; beam scanline for this row = 192 - 2*X
|
|
393
|
+
TXA
|
|
394
|
+
ASL
|
|
395
|
+
STA TMP ; TMP = 2*X
|
|
396
|
+
LDA #192
|
|
397
|
+
SEC
|
|
398
|
+
SBC TMP
|
|
399
|
+
STA LANDY ; LANDY reused as "this row's scanline"
|
|
400
|
+
; test against each platform
|
|
401
|
+
LDY #0
|
|
402
|
+
LDA #0
|
|
403
|
+
STA PFROW,X ; default open air
|
|
404
|
+
.bplat:
|
|
405
|
+
LDA LANDY
|
|
406
|
+
SEC
|
|
407
|
+
SBC PLAT_Y,Y
|
|
408
|
+
CMP #PF_BAND
|
|
409
|
+
BCS .bnext
|
|
410
|
+
; within a platform band → mark solid
|
|
411
|
+
LDA #$FF
|
|
412
|
+
STA PFROW,X
|
|
413
|
+
.bnext:
|
|
414
|
+
INY
|
|
415
|
+
CPY #NUM_PLAT
|
|
416
|
+
BNE .bplat
|
|
417
|
+
INX
|
|
418
|
+
CPX #96
|
|
419
|
+
BNE .brow
|
|
420
|
+
RTS
|
|
421
|
+
|
|
422
|
+
; ── Player sprite (8 rows) — a little explorer ──
|
|
423
|
+
PLAYER:
|
|
424
|
+
.byte %00111100
|
|
425
|
+
.byte %00111100
|
|
426
|
+
.byte %00011000
|
|
427
|
+
.byte %01111110
|
|
428
|
+
.byte %10111101
|
|
429
|
+
.byte %00111100
|
|
430
|
+
.byte %00100100
|
|
431
|
+
.byte %01100110
|
|
432
|
+
|
|
433
|
+
; ── Platform table ────────────────────────────────────────────────────
|
|
434
|
+
; Parallel arrays indexed 0..NUM_PLAT-1. Y = band top scanline (beam
|
|
435
|
+
; coords: bigger Y = higher on screen). XL/XR = the column span for the
|
|
436
|
+
; land-on-top test. The bars render FULL WIDTH, so every span is the whole
|
|
437
|
+
; screen (you can stand anywhere on a platform — visual == collision). To
|
|
438
|
+
; make narrower ledges, give a platform a partial PFROW pattern AND shrink
|
|
439
|
+
; its XL/XR here so the two stay in sync.
|
|
440
|
+
PLAT_Y:
|
|
441
|
+
.byte 18 ; floor (bottom)
|
|
442
|
+
.byte 70 ; ledge
|
|
443
|
+
.byte 110 ; ledge
|
|
444
|
+
.byte 150 ; ledge (top)
|
|
445
|
+
PLAT_XL:
|
|
446
|
+
.byte 0
|
|
447
|
+
.byte 0
|
|
448
|
+
.byte 0
|
|
449
|
+
.byte 0
|
|
450
|
+
PLAT_XR:
|
|
451
|
+
.byte 159
|
|
452
|
+
.byte 159
|
|
453
|
+
.byte 159
|
|
454
|
+
.byte 159
|
|
455
|
+
|
|
456
|
+
; ── Vector table ──
|
|
457
|
+
org $FFFA
|
|
458
|
+
.word START
|
|
459
|
+
.word START
|
|
460
|
+
.word START
|