romdevtools 0.28.0 → 0.29.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 +51 -41
- package/CHANGELOG.md +46 -0
- package/README.md +3 -3
- package/examples/README.md +7 -7
- package/examples/atari2600/templates/platformer.asm +1225 -332
- package/examples/atari2600/templates/puzzle.asm +1056 -0
- package/examples/atari2600/templates/racing.asm +906 -275
- package/examples/atari2600/templates/shmup.asm +1031 -239
- package/examples/atari2600/templates/sports.asm +1135 -253
- package/examples/atari7800/templates/platformer.c +991 -156
- package/examples/atari7800/templates/puzzle.c +1091 -148
- package/examples/atari7800/templates/racing.c +952 -124
- package/examples/atari7800/templates/shmup.c +812 -134
- package/examples/atari7800/templates/sports.c +820 -184
- package/examples/c64/templates/platformer.c +879 -164
- package/examples/c64/templates/puzzle.c +855 -178
- package/examples/c64/templates/racing.c +873 -97
- package/examples/c64/templates/shmup.c +757 -161
- package/examples/c64/templates/sports.c +755 -100
- package/examples/gb/templates/platformer.c +841 -179
- package/examples/gb/templates/puzzle.c +986 -246
- package/examples/gb/templates/racing.c +754 -174
- package/examples/gb/templates/shmup.c +673 -175
- package/examples/gb/templates/sports.c +790 -159
- package/examples/gba/templates/platformer.c +626 -165
- package/examples/gba/templates/puzzle.c +519 -269
- package/examples/gba/templates/racing.c +511 -206
- package/examples/gba/templates/shmup.c +564 -179
- package/examples/gba/templates/sports.c +454 -174
- package/examples/gbc/templates/platformer.c +944 -180
- package/examples/gbc/templates/puzzle.c +363 -109
- package/examples/gbc/templates/racing.c +884 -180
- package/examples/gbc/templates/shmup.c +821 -185
- package/examples/gbc/templates/sports.c +870 -162
- package/examples/genesis/templates/platformer.c +747 -129
- package/examples/genesis/templates/puzzle.c +694 -261
- package/examples/genesis/templates/racing.c +726 -203
- package/examples/genesis/templates/shmup.c +535 -142
- package/examples/genesis/templates/sports.c +495 -158
- package/examples/gg/templates/platformer.c +880 -215
- package/examples/gg/templates/puzzle.c +875 -216
- package/examples/gg/templates/racing.c +915 -172
- package/examples/gg/templates/shmup.c +714 -191
- package/examples/gg/templates/sports.c +732 -129
- package/examples/lynx/templates/platformer.c +604 -69
- package/examples/lynx/templates/puzzle.c +498 -158
- package/examples/lynx/templates/racing.c +538 -102
- package/examples/lynx/templates/shmup.c +458 -131
- package/examples/lynx/templates/sports.c +496 -72
- package/examples/msx/platformer/main.c +649 -162
- package/examples/msx/puzzle/main.c +742 -240
- package/examples/msx/racing/main.c +669 -178
- package/examples/msx/shmup/main.c +460 -178
- package/examples/msx/sports/main.c +592 -126
- package/examples/nes/templates/platformer.c +589 -171
- package/examples/nes/templates/puzzle.c +563 -242
- package/examples/nes/templates/racing.c +502 -208
- package/examples/nes/templates/shmup.c +339 -145
- package/examples/nes/templates/sports.c +341 -183
- package/examples/pce/platformer/main.c +874 -205
- package/examples/pce/puzzle/main.c +802 -287
- package/examples/pce/racing/main.c +783 -208
- package/examples/pce/shmup/main.c +638 -212
- package/examples/pce/sports/main.c +586 -169
- package/examples/porting-across-platforms/README.md +1 -1
- package/examples/sms/templates/platformer.c +762 -177
- package/examples/sms/templates/puzzle.c +752 -212
- package/examples/sms/templates/racing.c +808 -145
- package/examples/sms/templates/shmup.c +599 -162
- package/examples/sms/templates/sports.c +630 -122
- package/examples/snes/templates/music_demo.c +7 -0
- package/examples/snes/templates/platformer-data.asm +123 -24
- package/examples/snes/templates/platformer-hdr.asm +57 -0
- package/examples/snes/templates/platformer.c +586 -165
- package/examples/snes/templates/puzzle-data.asm +116 -21
- package/examples/snes/templates/puzzle-hdr.asm +57 -0
- package/examples/snes/templates/puzzle.c +614 -235
- package/examples/snes/templates/racing-data.asm +390 -32
- package/examples/snes/templates/racing-hdr.asm +57 -0
- package/examples/snes/templates/racing.c +807 -196
- package/examples/snes/templates/shmup-data.asm +87 -29
- package/examples/snes/templates/shmup-hdr.asm +57 -0
- package/examples/snes/templates/shmup.c +459 -198
- package/examples/snes/templates/sports-data.asm +48 -2
- package/examples/snes/templates/sports-hdr.asm +57 -0
- package/examples/snes/templates/sports.c +414 -163
- package/package.json +1 -1
- package/src/host/LibretroHost.js +59 -1
- package/src/http/tool-registry.js +11 -11
- package/src/mcp/tools/cheats.js +2 -1
- package/src/mcp/tools/frame.js +3 -2
- package/src/mcp/tools/index.js +3 -3
- package/src/mcp/tools/input.js +5 -4
- package/src/mcp/tools/lifecycle.js +6 -4
- package/src/mcp/tools/platform-docs.js +1 -1
- package/src/mcp/tools/preview-tile.js +6 -2
- package/src/mcp/tools/project.js +1098 -130
- package/src/mcp/tools/rom-id.js +5 -1
- package/src/mcp/tools/run-until.js +4 -2
- package/src/mcp/tools/snippets.js +6 -6
- package/src/mcp/tools/sprite-pipeline.js +14 -2
- package/src/mcp/tools/state.js +2 -1
- package/src/mcp/tools/tile-inspect.js +8 -1
- package/src/mcp/tools/toolchain.js +12 -1
- package/src/mcp/tools/watch-memory.js +4 -3
- package/src/observer/bus.js +73 -0
- package/src/observer/livestream.html +4 -2
- package/src/observer/tool-wrap.js +17 -14
- package/src/platforms/atari7800/MENTAL_MODEL.md +5 -5
- package/src/platforms/atari7800/TROUBLESHOOTING.md +5 -5
- package/src/platforms/c64/MENTAL_MODEL.md +11 -4
- package/src/platforms/c64/TROUBLESHOOTING.md +13 -0
- package/src/platforms/gb/MENTAL_MODEL.md +3 -3
- package/src/platforms/gb/TROUBLESHOOTING.md +61 -8
- package/src/platforms/gb/lib/c/README.md +10 -11
- package/src/platforms/gb/lib/c/gb_crt0.s +27 -3
- package/src/platforms/gb/lib/c/patch-header.js +13 -3
- package/src/platforms/gba/MENTAL_MODEL.md +4 -4
- package/src/platforms/gba/TROUBLESHOOTING.md +3 -3
- package/src/platforms/gba/lib/c/gba_sfx.c +40 -0
- package/src/platforms/gba/lib/c/gba_sfx.h +10 -0
- package/src/platforms/gbc/MENTAL_MODEL.md +4 -4
- package/src/platforms/gbc/TROUBLESHOOTING.md +4 -4
- package/src/platforms/gbc/UPSTREAM_SOURCES.md +1 -1
- package/src/platforms/gbc/lib/c/README.md +10 -11
- package/src/platforms/gbc/lib/c/gb_crt0.s +26 -3
- package/src/platforms/gbc/lib/c/patch-header.js +13 -3
- package/src/platforms/genesis/MENTAL_MODEL.md +3 -3
- package/src/platforms/genesis/TROUBLESHOOTING.md +2 -2
- package/src/platforms/gg/MENTAL_MODEL.md +4 -4
- package/src/platforms/gg/TROUBLESHOOTING.md +3 -3
- package/src/platforms/gg/UPSTREAM_SOURCES.md +1 -1
- package/src/platforms/gg/lib/c/joypad_read.c +29 -0
- package/src/platforms/lynx/MENTAL_MODEL.md +1 -1
- package/src/platforms/lynx/TROUBLESHOOTING.md +3 -3
- package/src/platforms/msx/MENTAL_MODEL.md +5 -5
- package/src/platforms/msx/TROUBLESHOOTING.md +2 -2
- package/src/platforms/msx/lib/c/msx_hw.h +1 -0
- package/src/platforms/msx/lib/c/msx_vdp.c +25 -0
- package/src/platforms/nes/MENTAL_MODEL.md +2 -2
- package/src/platforms/nes/lib/c/nes_runtime.c +149 -34
- package/src/platforms/nes/lib/c/nes_runtime.h +34 -1
- package/src/platforms/pce/MENTAL_MODEL.md +5 -5
- package/src/platforms/pce/TROUBLESHOOTING.md +1 -1
- package/src/platforms/pce/lib/c/pce_hw.h +11 -0
- package/src/platforms/pce/lib/c/pce_video.c +32 -0
- package/src/platforms/sms/MENTAL_MODEL.md +6 -6
- package/src/platforms/snes/MENTAL_MODEL.md +2 -2
- package/src/platforms/snes/TROUBLESHOOTING.md +40 -1
- package/src/toolchains/cc65/presets/nes/chr-ram-runtime.cfg +13 -8
- package/src/toolchains/cc65/presets/nes/chr-ram-runtime.crt0.s +58 -5
- package/src/toolchains/cc65/presets/nes/chr-rom.crt0.s +52 -3
- package/src/toolchains/cc65/presets/pce/rom32k.cfg +52 -0
- package/src/toolchains/index.js +27 -11
|
@@ -19,13 +19,15 @@
|
|
|
19
19
|
# 3. Write CHR data from C at runtime: PPUADDR = 0x00; PPUDATA = byte; etc.
|
|
20
20
|
|
|
21
21
|
SYMBOLS {
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
22
|
+
# C parameter stack is ONE page ($0600-$06FF, grows down from $0700).
|
|
23
|
+
# NROM-sized C games use far less than 256 B of it (shallow call
|
|
24
|
+
# depth, mostly static data), and shrinking it frees $0500-$05FF as
|
|
25
|
+
# the USER SCRATCH PAGE: game code may place absolute-addressed
|
|
26
|
+
# arrays there (e.g. `#define BOARD ((unsigned char*)0x0500)`) when
|
|
27
|
+
# BSS ($0300-$04FF) is full — the puzzle example game does exactly
|
|
28
|
+
# this. The top page ($0700-$07FF) stays reserved for a music
|
|
29
|
+
# driver's scratch RAM (FamiTone2 et al.).
|
|
30
|
+
__STACKSIZE__: type = weak, value = $0100;
|
|
29
31
|
}
|
|
30
32
|
MEMORY {
|
|
31
33
|
ZP: file = "", start = $0002, size = $001A, type = rw, define = yes;
|
|
@@ -41,7 +43,10 @@ MEMORY {
|
|
|
41
43
|
|
|
42
44
|
# NO ROM2 / CHARS — this is the whole point of the CHR-RAM preset.
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
# $0500-$05FF: user scratch page (see SYMBOLS note) — NOT a segment;
|
|
47
|
+
# game code addresses it absolutely so the linker never places
|
|
48
|
+
# anything here.
|
|
49
|
+
SRAM: file = "", start = $0600, size = __STACKSIZE__, define = yes;
|
|
45
50
|
|
|
46
51
|
# Reserved page for a sound-driver's RAM scratch ($0700-$07FF). The
|
|
47
52
|
# bundled FamiTone2 engine (music_demo scaffold) pins FT_BASE_ADR here
|
|
@@ -28,7 +28,12 @@
|
|
|
28
28
|
.import _main, zerobss, copydata
|
|
29
29
|
.import __RAM_START__, __RAM_SIZE__
|
|
30
30
|
.import __SRAM_START__, __SRAM_SIZE__
|
|
31
|
-
.import
|
|
31
|
+
.import _vram_q_hi, _vram_q_lo, _vram_q_val
|
|
32
|
+
.import _vram_queue_head, _vram_queue_len, _vram_queue_lock
|
|
33
|
+
|
|
34
|
+
; Must match nes_runtime.c (QUEUE_MAX 32 ring buffer).
|
|
35
|
+
QUEUE_MASK = 31
|
|
36
|
+
FLUSH_BUDGET = 16
|
|
32
37
|
.import _scroll_x, _scroll_y, _ppuctrl_value, _nmi_counter
|
|
33
38
|
.importzp c_sp
|
|
34
39
|
|
|
@@ -39,7 +44,12 @@
|
|
|
39
44
|
.byte $4e, $45, $53, $1a ; "NES" + EOF
|
|
40
45
|
.byte 2 ; PRG-ROM banks (16K each) → 32K
|
|
41
46
|
.byte 0 ; CHR-ROM banks (8K each) → 0 = CHR-RAM
|
|
42
|
-
.byte %
|
|
47
|
+
.byte %00000011 ; flags6 — vertical mirroring + BATTERY.
|
|
48
|
+
; The battery bit maps persistent 8KB
|
|
49
|
+
; PRG-RAM at $6000 (the save_ram region)
|
|
50
|
+
; — hiscore_load/save in nes_runtime use
|
|
51
|
+
; it. Benign when unused; without it,
|
|
52
|
+
; $6000-$7FFF is OPEN BUS on NROM.
|
|
43
53
|
.byte %00000000 ; flags7 — mapper hi nybble
|
|
44
54
|
.byte 0, 0, 0, 0, 0, 0, 0, 0
|
|
45
55
|
|
|
@@ -129,9 +139,46 @@ nmi:
|
|
|
129
139
|
lda #$02 ; high byte of $0200
|
|
130
140
|
sta $4014 ; PPU OAMDMA — kicks off the copy
|
|
131
141
|
|
|
132
|
-
;
|
|
133
|
-
;
|
|
134
|
-
|
|
142
|
+
; ── Drain the VRAM queue — IN ASSEMBLY, on purpose ──────────────
|
|
143
|
+
; Vblank is ~2273 CPU cycles and the OAM DMA above just spent 513.
|
|
144
|
+
; Compiled C costs 200+ cycles per queue entry, so a C flush blows
|
|
145
|
+
; past the end of vblank — and PPUDATA writes during ACTIVE
|
|
146
|
+
; RENDERING land at corrupted addresses (the PPU's internal v
|
|
147
|
+
; register is busy fetching tiles; its coarse-X/fine-Y counters
|
|
148
|
+
; shear every late write). This loop costs ~40 cycles per entry,
|
|
149
|
+
; so FLUSH_BUDGET entries always finish safely inside vblank.
|
|
150
|
+
; QUEUE_MASK/FLUSH_BUDGET must match nes_runtime.c's ring buffer.
|
|
151
|
+
lda _vram_queue_lock
|
|
152
|
+
bne @flush_done ; a push is mid-flight — skip this vblank
|
|
153
|
+
lda _vram_queue_len
|
|
154
|
+
beq @flush_done
|
|
155
|
+
cmp #FLUSH_BUDGET
|
|
156
|
+
bcc @flush_n_ok
|
|
157
|
+
lda #FLUSH_BUDGET
|
|
158
|
+
@flush_n_ok:
|
|
159
|
+
sta nmi_drain ; loop counter
|
|
160
|
+
sta nmi_drained ; remembered for the length update
|
|
161
|
+
bit $2002 ; reset the PPUADDR write latch
|
|
162
|
+
ldx _vram_queue_head
|
|
163
|
+
@flush_loop:
|
|
164
|
+
lda _vram_q_hi,x
|
|
165
|
+
sta $2006
|
|
166
|
+
lda _vram_q_lo,x
|
|
167
|
+
sta $2006
|
|
168
|
+
lda _vram_q_val,x
|
|
169
|
+
sta $2007
|
|
170
|
+
inx
|
|
171
|
+
txa
|
|
172
|
+
and #QUEUE_MASK ; ring wrap
|
|
173
|
+
tax
|
|
174
|
+
dec nmi_drain
|
|
175
|
+
bne @flush_loop
|
|
176
|
+
stx _vram_queue_head
|
|
177
|
+
lda _vram_queue_len
|
|
178
|
+
sec
|
|
179
|
+
sbc nmi_drained
|
|
180
|
+
sta _vram_queue_len
|
|
181
|
+
@flush_done:
|
|
135
182
|
|
|
136
183
|
; Reset PPUADDR to $2000 (otherwise the queue's last $2006 write
|
|
137
184
|
; leaves it dangling and the PPU samples random VRAM as the BG).
|
|
@@ -172,6 +219,12 @@ irq: rti
|
|
|
172
219
|
_shadow_oam: .res 256
|
|
173
220
|
|
|
174
221
|
; ------------------------------------------------------------------------
|
|
222
|
+
; NMI-private temporaries — deliberately NOT cc65's zp tmp1-4 (the NMI
|
|
223
|
+
; would corrupt them under interrupted C code).
|
|
224
|
+
.segment "BSS"
|
|
225
|
+
nmi_drain: .res 1
|
|
226
|
+
nmi_drained: .res 1
|
|
227
|
+
|
|
175
228
|
.segment "VECTORS"
|
|
176
229
|
.word nmi ; $FFFA
|
|
177
230
|
.word start ; $FFFC
|
|
@@ -22,7 +22,12 @@
|
|
|
22
22
|
.import _main, zerobss, copydata
|
|
23
23
|
.import __RAM_START__, __RAM_SIZE__
|
|
24
24
|
.import __SRAM_START__, __SRAM_SIZE__
|
|
25
|
-
.import
|
|
25
|
+
.import _vram_q_hi, _vram_q_lo, _vram_q_val
|
|
26
|
+
.import _vram_queue_head, _vram_queue_len, _vram_queue_lock
|
|
27
|
+
|
|
28
|
+
; Must match nes_runtime.c (QUEUE_MAX 32 ring buffer).
|
|
29
|
+
QUEUE_MASK = 31
|
|
30
|
+
FLUSH_BUDGET = 16
|
|
26
31
|
.import _scroll_x, _scroll_y, _ppuctrl_value, _nmi_counter
|
|
27
32
|
.importzp c_sp
|
|
28
33
|
|
|
@@ -109,8 +114,46 @@ nmi:
|
|
|
109
114
|
lda #$02 ; high byte of $0200
|
|
110
115
|
sta $4014 ; PPU OAMDMA — kicks off the copy
|
|
111
116
|
|
|
112
|
-
;
|
|
113
|
-
|
|
117
|
+
; ── Drain the VRAM queue — IN ASSEMBLY, on purpose ──────────────
|
|
118
|
+
; Vblank is ~2273 CPU cycles and the OAM DMA above just spent 513.
|
|
119
|
+
; Compiled C costs 200+ cycles per queue entry, so a C flush blows
|
|
120
|
+
; past the end of vblank — and PPUDATA writes during ACTIVE
|
|
121
|
+
; RENDERING land at corrupted addresses (the PPU's internal v
|
|
122
|
+
; register is busy fetching tiles; its coarse-X/fine-Y counters
|
|
123
|
+
; shear every late write). This loop costs ~40 cycles per entry,
|
|
124
|
+
; so FLUSH_BUDGET entries always finish safely inside vblank.
|
|
125
|
+
; QUEUE_MASK/FLUSH_BUDGET must match nes_runtime.c's ring buffer.
|
|
126
|
+
lda _vram_queue_lock
|
|
127
|
+
bne @flush_done ; a push is mid-flight — skip this vblank
|
|
128
|
+
lda _vram_queue_len
|
|
129
|
+
beq @flush_done
|
|
130
|
+
cmp #FLUSH_BUDGET
|
|
131
|
+
bcc @flush_n_ok
|
|
132
|
+
lda #FLUSH_BUDGET
|
|
133
|
+
@flush_n_ok:
|
|
134
|
+
sta nmi_drain ; loop counter
|
|
135
|
+
sta nmi_drained ; remembered for the length update
|
|
136
|
+
bit $2002 ; reset the PPUADDR write latch
|
|
137
|
+
ldx _vram_queue_head
|
|
138
|
+
@flush_loop:
|
|
139
|
+
lda _vram_q_hi,x
|
|
140
|
+
sta $2006
|
|
141
|
+
lda _vram_q_lo,x
|
|
142
|
+
sta $2006
|
|
143
|
+
lda _vram_q_val,x
|
|
144
|
+
sta $2007
|
|
145
|
+
inx
|
|
146
|
+
txa
|
|
147
|
+
and #QUEUE_MASK ; ring wrap
|
|
148
|
+
tax
|
|
149
|
+
dec nmi_drain
|
|
150
|
+
bne @flush_loop
|
|
151
|
+
stx _vram_queue_head
|
|
152
|
+
lda _vram_queue_len
|
|
153
|
+
sec
|
|
154
|
+
sbc nmi_drained
|
|
155
|
+
sta _vram_queue_len
|
|
156
|
+
@flush_done:
|
|
114
157
|
|
|
115
158
|
; Reset PPUADDR to $2000 so the PPU doesn't sample random VRAM as BG.
|
|
116
159
|
bit $2002
|
|
@@ -147,6 +190,12 @@ irq: rti
|
|
|
147
190
|
_shadow_oam: .res 256
|
|
148
191
|
|
|
149
192
|
; ------------------------------------------------------------------------
|
|
193
|
+
; NMI-private temporaries — deliberately NOT cc65's zp tmp1-4 (the NMI
|
|
194
|
+
; would corrupt them under interrupted C code).
|
|
195
|
+
.segment "BSS"
|
|
196
|
+
nmi_drain: .res 1
|
|
197
|
+
nmi_drained: .res 1
|
|
198
|
+
|
|
150
199
|
.segment "VECTORS"
|
|
151
200
|
.word nmi ; $FFFA
|
|
152
201
|
.word start ; $FFFC
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# PC Engine 32KB HuCard ld65 config (romdev 'rom32k' preset).
|
|
2
|
+
#
|
|
3
|
+
# WHY THIS EXISTS: cc65's stock pce.cfg defaults to an 8KB image, and its
|
|
4
|
+
# documented 16K/32K option ($CARTSIZE) places STARTUP/VECTORS at the END of
|
|
5
|
+
# the file — but a HuCard maps file offset 0 as bank 0, and the HuC6280 reset
|
|
6
|
+
# maps MPR7 (=$E000-$FFFF, where the vectors live) to BANK 0. So a stock-cfg
|
|
7
|
+
# 32K image boots to a black screen (verified on geargrafx). This config puts
|
|
8
|
+
# bank 0 (STARTUP/VECTORS + hot code) FIRST in the file, at $E000, and the
|
|
9
|
+
# remaining 24KB of banks 1-3 at $8000-$DFFF — exactly where cc65's pce crt0
|
|
10
|
+
# TAMs them (MPR4=bank1, MPR5=bank2, MPR6=bank3) before calling main().
|
|
11
|
+
SYMBOLS {
|
|
12
|
+
__CARTSIZE__: type = weak, value = $8000; # crt0 compares >$8000 vs this
|
|
13
|
+
__STACKSIZE__: type = weak, value = $0300; # 3 pages stack
|
|
14
|
+
}
|
|
15
|
+
MEMORY {
|
|
16
|
+
ZP: file = "", start = $0000, define = yes, size = $0100;
|
|
17
|
+
# RAM bank ($F8 at MPR1)
|
|
18
|
+
MAIN: file = "", start = $2200, define = yes, size = $1E00 - __STACKSIZE__;
|
|
19
|
+
# HuCard bank 0 — hardware maps it at $E000 (MPR7) at reset. File offset 0.
|
|
20
|
+
ROM0: file = %O, start = $E000, size = $2000, fill = yes, fillval = $FF;
|
|
21
|
+
# HuCard banks 1-3 — crt0 maps them at $8000/$A000/$C000 (MPR4/5/6).
|
|
22
|
+
ROM: file = %O, start = $8000, size = $6000, fill = yes, fillval = $FF;
|
|
23
|
+
}
|
|
24
|
+
SEGMENTS {
|
|
25
|
+
ZEROPAGE: load = ZP, type = zp;
|
|
26
|
+
EXTZP: load = ZP, type = zp, optional = yes;
|
|
27
|
+
APPZP: load = ZP, type = zp, optional = yes;
|
|
28
|
+
DATA: load = ROM0, run = MAIN, type = rw, define = yes;
|
|
29
|
+
INIT: load = MAIN, type = bss, optional = yes;
|
|
30
|
+
BSS: load = MAIN, type = bss, define = yes;
|
|
31
|
+
LOWCODE: load = ROM0, type = ro, optional = yes;
|
|
32
|
+
ONCE: load = ROM0, type = ro, optional = yes;
|
|
33
|
+
CODE: load = ROM, type = ro;
|
|
34
|
+
RODATA: load = ROM, type = ro;
|
|
35
|
+
STARTUP: load = ROM0, type = ro, start = $FFF6 - $0066;
|
|
36
|
+
VECTORS: load = ROM0, type = ro, start = $FFF6;
|
|
37
|
+
}
|
|
38
|
+
FEATURES {
|
|
39
|
+
CONDES: type = constructor,
|
|
40
|
+
label = __CONSTRUCTOR_TABLE__,
|
|
41
|
+
count = __CONSTRUCTOR_COUNT__,
|
|
42
|
+
segment = ONCE;
|
|
43
|
+
CONDES: type = destructor,
|
|
44
|
+
label = __DESTRUCTOR_TABLE__,
|
|
45
|
+
count = __DESTRUCTOR_COUNT__,
|
|
46
|
+
segment = RODATA;
|
|
47
|
+
CONDES: type = interruptor,
|
|
48
|
+
label = __INTERRUPTOR_TABLE__,
|
|
49
|
+
count = __INTERRUPTOR_COUNT__,
|
|
50
|
+
segment = RODATA,
|
|
51
|
+
import = __CALLIRQ__;
|
|
52
|
+
}
|
package/src/toolchains/index.js
CHANGED
|
@@ -37,7 +37,7 @@ const CC65_TARGET = {
|
|
|
37
37
|
const LANGUAGE_TOOLCHAIN = {
|
|
38
38
|
atari2600: {
|
|
39
39
|
asm: { toolchain: "dasm", available: true },
|
|
40
|
-
basic: { toolchain: "batariBasic", available: false, note: "BASIC for 2600 via batariBasic — not bundled. bB's transpiler is written in Perl, which we don't ship as WASM. A port to C or JS would be a multi-day project. For now, write 2600 games in 6507 asm via dasm — the bundled
|
|
40
|
+
basic: { toolchain: "batariBasic", available: false, note: "BASIC for 2600 via batariBasic — not bundled. bB's transpiler is written in Perl, which we don't ship as WASM. A port to C or JS would be a multi-day project. For now, write 2600 games in 6507 asm via dasm — the bundled example games (default, paddle, single_screen) show the canonical race-the-beam pattern, and an LLM agent writes 2600 asm fluently." },
|
|
41
41
|
},
|
|
42
42
|
nes: {
|
|
43
43
|
asm: { toolchain: "cc65", available: true },
|
|
@@ -65,11 +65,11 @@ const LANGUAGE_TOOLCHAIN = {
|
|
|
65
65
|
},
|
|
66
66
|
snes: {
|
|
67
67
|
asm: { toolchain: "asar", available: true },
|
|
68
|
-
c: { toolchain: "tcc816+wladx", available: true, note: "C for SNES via tcc-65816 + wla-65816 + wlalink. The PVSnesLib runtime IS bundled (built from source) and auto-linked — #include <snes.h> gives you consoleDrawText, setMode, oamSet, WaitForVBlank, etc. out of the box. `
|
|
68
|
+
c: { toolchain: "tcc816+wladx", available: true, note: "C for SNES via tcc-65816 + wla-65816 + wlalink. The PVSnesLib runtime IS bundled (built from source) and auto-linked — #include <snes.h> gives you consoleDrawText, setMode, oamSet, WaitForVBlank, etc. out of the box. `examples({op:'fork'})` gives you a complete working PVSnesLib C project. Pass options.pvsneslib:false for the bare-main minimum-viable path." },
|
|
69
69
|
},
|
|
70
70
|
genesis: {
|
|
71
71
|
asm: { toolchain: "vasm68k", available: true },
|
|
72
|
-
c: { toolchain: "m68k-elf-gcc", available: true, note: "C for Genesis via gcc 14.2.0 + binutils + newlib, all compiled to WASM. The SGDK runtime IS bundled (built from source) and auto-linked — sprite engine, VDP, controller, PSG/Z80 sound, resource helpers all work; #include <genesis.h>. `
|
|
72
|
+
c: { toolchain: "m68k-elf-gcc", available: true, note: "C for Genesis via gcc 14.2.0 + binutils + newlib, all compiled to WASM. The SGDK runtime IS bundled (built from source) and auto-linked — sprite engine, VDP, controller, PSG/Z80 sound, resource helpers all work; #include <genesis.h>. `examples({op:'fork'})` gives you a complete working SGDK C project (the recommended path). Pass options.sgdk:false for the bare-gcc minimum-viable path." },
|
|
73
73
|
},
|
|
74
74
|
gba: {
|
|
75
75
|
c: { toolchain: "arm-none-eabi-gcc", available: true, note: "C for GBA via gcc 14.2.0 + binutils + newlib + libtonc 1.4.5 (default) OR libgba 0.5.4 (opt-in via runtime:\"libgba\"), all compiled to WASM (R24 + R28). #include <tonc.h> + tte_write/tte_printf works out of the box — that's the canonical Tonc-tutorial API every published GBA C resource uses. Caveat: tte_iohook (libtonc) and console.c (libgba) — the libsysbase-backed iprintf bridges — are NOT bundled. Use tte_printf directly, which is what the Tonc tutorial actually does." },
|
|
@@ -91,7 +91,7 @@ const LANGUAGE_TOOLCHAIN = {
|
|
|
91
91
|
* Default language per platform. The choice reflects what's fastest /
|
|
92
92
|
* smallest / best-matched to LLM fluency. Every platform that has a bundled
|
|
93
93
|
* C compiler + runtime defaults to C — that's the canonical, productive path
|
|
94
|
-
* and what `
|
|
94
|
+
* and what `examples({op:'fork'})` projects use (cc65 for NES/C64/Atari7800/
|
|
95
95
|
* Lynx, SDCC for GB/GBC/SMS/GG, gcc+SGDK for Genesis, tcc+PVSnesLib for SNES,
|
|
96
96
|
* gcc+libtonc for GBA). Platforms whose only bundled toolchain is an assembler
|
|
97
97
|
* default to asm (Atari 2600 → dasm; SNES/Genesis keep an asm option too, but
|
|
@@ -743,10 +743,9 @@ export async function buildForPlatform(args) {
|
|
|
743
743
|
// crt0 + headers + sources come straight from the caller. The build
|
|
744
744
|
// pipeline does NOT auto-inject platform runtimes, custom crt0s,
|
|
745
745
|
// or post-link header patches. Every byte that compiles is visible
|
|
746
|
-
// to the caller's repo. Use `
|
|
747
|
-
//
|
|
748
|
-
//
|
|
749
|
-
// to fetch individual pieces.
|
|
746
|
+
// to the caller's repo. Use `examples({op:'fork'})` to get a
|
|
747
|
+
// self-contained project with the runtime files copied in, or
|
|
748
|
+
// `examples({op:'snippets'/'copySnippets'})` to fetch individual pieces.
|
|
750
749
|
const crt0 = args.crt0;
|
|
751
750
|
|
|
752
751
|
// Pre-flight lint: scan the C sources for known SDCC C89 violations
|
|
@@ -787,10 +786,27 @@ export async function buildForPlatform(args) {
|
|
|
787
786
|
// and RAM-size ($0149) bytes — without -m/-r, -v leaves them at the
|
|
788
787
|
// linker's garbage pad (e.g. type $3C), and emulators/hardware reject
|
|
789
788
|
// an unknown MBC type with "retro_load_game failed". -m 0x00 = ROM ONLY
|
|
790
|
-
// (no mapper), -r 0x00 = no cart RAM — correct for
|
|
789
|
+
// (no mapper), -r 0x00 = no cart RAM — correct for plain 32KB builds.
|
|
790
|
+
//
|
|
791
|
+
// Battery-cart passthrough (0.29.0 examples): a crt0 may DECLARE the
|
|
792
|
+
// cart in the header window (the GB equivalent of the NES crt0's iNES
|
|
793
|
+
// BATTERY bit — see the gbc lib gb_crt0.s, which emits $0147=$03 /
|
|
794
|
+
// $0149=$02 for MBC1+RAM+BATTERY so hi-scores persist in SAVE_RAM).
|
|
795
|
+
// If the linked image carries a KNOWN battery-MBC type byte with a
|
|
796
|
+
// sane RAM size, pass those through to rgbfix instead of stomping
|
|
797
|
+
// them to ROM-only; anything unrecognized (linker pad garbage) still
|
|
798
|
+
// falls back to the safe ROM-only default, so crt0s that don't
|
|
799
|
+
// declare a cart behave exactly as before.
|
|
800
|
+
const BATTERY_CART_TYPES = new Set([0x03, 0x06, 0x0F, 0x10, 0x13, 0x1B, 0x1E]); // MBC1/2/3/5 +BATTERY variants
|
|
801
|
+
const declType = binary.length > 0x149 ? binary[0x147] : 0x00;
|
|
802
|
+
const declRam = binary.length > 0x149 ? binary[0x149] : 0x00;
|
|
803
|
+
const cartByte = BATTERY_CART_TYPES.has(declType) ? declType : 0x00;
|
|
804
|
+
const ramByte = cartByte !== 0x00 && declRam >= 0x01 && declRam <= 0x05 ? declRam : 0x00;
|
|
805
|
+
const mArg = "0x" + cartByte.toString(16).padStart(2, "0").toUpperCase();
|
|
806
|
+
const rArg = "0x" + ramByte.toString(16).padStart(2, "0").toUpperCase();
|
|
791
807
|
const fixOpts = args.platform === "gbc"
|
|
792
|
-
? ["-v", "-p", "0xFF", "-C", "-m",
|
|
793
|
-
: ["-v", "-p", "0xFF", "-m",
|
|
808
|
+
? ["-v", "-p", "0xFF", "-C", "-m", mArg, "-r", rArg]
|
|
809
|
+
: ["-v", "-p", "0xFF", "-m", mArg, "-r", rArg];
|
|
794
810
|
const fix = await runRgbfix({ rom: binary, options: fixOpts });
|
|
795
811
|
if (fix.exitCode === 0 && fix.binary) {
|
|
796
812
|
binary = fix.binary;
|