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,83 @@
|
|
|
1
|
+
# NES CHR-ROM linker config preset for romdev (cc65-C with FIXED tile art).
|
|
2
|
+
#
|
|
3
|
+
# Use this when you write NES homebrew in C AND ship your pattern tables as a
|
|
4
|
+
# fixed 8KB CHR-ROM blob (vs chr-ram-runtime, which has the CPU upload tiles to
|
|
5
|
+
# CHR-RAM at runtime). The cartridge is NROM-256: 32KB PRG + 8KB CHR-ROM.
|
|
6
|
+
#
|
|
7
|
+
# DIFFERENCE FROM chr-ram-runtime.cfg:
|
|
8
|
+
# - Adds a ROM2 memory area (8KB CHR-ROM bank) + a CHARS segment. Put your
|
|
9
|
+
# tile data in `.segment "CHARS"` (e.g. `.incbin "tiles.chr"`); it lands in
|
|
10
|
+
# the CHR-ROM bank the PPU reads pattern tables from directly.
|
|
11
|
+
# - The companion crt0 (chr-rom.crt0.s) emits an iNES header with byte 5 = 1
|
|
12
|
+
# (one 8KB CHR-ROM bank) and does NOT clear CHR-RAM (there is none).
|
|
13
|
+
#
|
|
14
|
+
# To use this preset:
|
|
15
|
+
# 1. linkerConfig: "chr-rom" on build({output:'rom'}) (pulls in chr-rom.crt0.s)
|
|
16
|
+
# 2. Supply your tile art: a source with `.segment "CHARS"` + `.incbin
|
|
17
|
+
# "tiles.chr"`, plus the blob via binaryIncludePaths:{ "tiles.chr": path }.
|
|
18
|
+
# The CHARS segment must total exactly 8192 bytes (fill is on, so a short
|
|
19
|
+
# blob is padded; an over-long one errors).
|
|
20
|
+
# 3. For a DIFFERENT cart config (NROM-128, >8KB CHR, a mapper, vertical
|
|
21
|
+
# mirroring), don't use this preset — use the parametric `inesHeader`
|
|
22
|
+
# build option instead (it generates the header + .cfg for any NROM shape).
|
|
23
|
+
#
|
|
24
|
+
# This is the cc65-C-with-segments shape. To rebuild a COMMERCIAL game from its
|
|
25
|
+
# disassembly (one flat CODE blob with embedded vectors), use `inesHeader`.
|
|
26
|
+
|
|
27
|
+
SYMBOLS {
|
|
28
|
+
__STACKSIZE__: type = weak, value = $0300;
|
|
29
|
+
}
|
|
30
|
+
MEMORY {
|
|
31
|
+
ZP: file = "", start = $0002, size = $001A, type = rw, define = yes;
|
|
32
|
+
|
|
33
|
+
# iNES Cartridge Header — emitted by chr-rom.crt0.s (HEADER segment).
|
|
34
|
+
HEADER: file = %O, start = $0000, size = $0010, fill = yes;
|
|
35
|
+
|
|
36
|
+
# PRG-ROM: 2 × 16K = 32K (NROM-256).
|
|
37
|
+
ROM0: file = %O, start = $8000, size = $7FFA, fill = yes, define = yes;
|
|
38
|
+
|
|
39
|
+
# Hardware vectors at end of PRG.
|
|
40
|
+
ROMV: file = %O, start = $FFFA, size = $0006, fill = yes;
|
|
41
|
+
|
|
42
|
+
# CHR-ROM: one 8K bank. The PPU reads pattern tables straight from here —
|
|
43
|
+
# this is the whole point of the CHR-ROM preset.
|
|
44
|
+
ROM2: file = %O, start = $0000, size = $2000, fill = yes, define = yes;
|
|
45
|
+
|
|
46
|
+
SRAM: file = "", start = $0500, size = __STACKSIZE__, define = yes;
|
|
47
|
+
|
|
48
|
+
# BSS / DATA live in real RAM ($0300-$04FF, 512 bytes). NROM (mapper 0)
|
|
49
|
+
# with no battery has $6000-$7FFF UNMAPPED — don't put BSS there.
|
|
50
|
+
RAM: file = "", start = $0300, size = $0200, define = yes;
|
|
51
|
+
|
|
52
|
+
# Shadow OAM at $0200 (page 2). NMI handler DMAs it to PPU sprite RAM.
|
|
53
|
+
OAM: file = "", start = $0200, size = $0100;
|
|
54
|
+
}
|
|
55
|
+
SEGMENTS {
|
|
56
|
+
ZEROPAGE: load = ZP, type = zp;
|
|
57
|
+
HEADER: load = HEADER, type = ro;
|
|
58
|
+
STARTUP: load = ROM0, type = ro, define = yes;
|
|
59
|
+
LOWCODE: load = ROM0, type = ro, optional = yes;
|
|
60
|
+
ONCE: load = ROM0, type = ro, optional = yes;
|
|
61
|
+
CODE: load = ROM0, type = ro, define = yes;
|
|
62
|
+
RODATA: load = ROM0, type = ro, define = yes;
|
|
63
|
+
DATA: load = ROM0, run = RAM, type = rw, define = yes;
|
|
64
|
+
VECTORS: load = ROMV, type = rw;
|
|
65
|
+
CHARS: load = ROM2, type = ro;
|
|
66
|
+
BSS: load = RAM, type = bss, define = yes;
|
|
67
|
+
OAM: load = OAM, type = bss, optional = yes;
|
|
68
|
+
}
|
|
69
|
+
FEATURES {
|
|
70
|
+
CONDES: type = constructor,
|
|
71
|
+
label = __CONSTRUCTOR_TABLE__,
|
|
72
|
+
count = __CONSTRUCTOR_COUNT__,
|
|
73
|
+
segment = ONCE;
|
|
74
|
+
CONDES: type = destructor,
|
|
75
|
+
label = __DESTRUCTOR_TABLE__,
|
|
76
|
+
count = __DESTRUCTOR_COUNT__,
|
|
77
|
+
segment = RODATA;
|
|
78
|
+
CONDES: type = interruptor,
|
|
79
|
+
label = __INTERRUPTOR_TABLE__,
|
|
80
|
+
count = __INTERRUPTOR_COUNT__,
|
|
81
|
+
segment = RODATA,
|
|
82
|
+
import = __CALLIRQ__;
|
|
83
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
; crt0 for NES CHR-ROM mode + nes_runtime NMI handler.
|
|
2
|
+
;
|
|
3
|
+
; Companion to the linkerConfig:"chr-rom" preset. Identical to
|
|
4
|
+
; chr-ram-runtime.crt0.s EXCEPT:
|
|
5
|
+
; - the iNES header sets byte 5 = 1 (one 8KB CHR-ROM bank), and
|
|
6
|
+
; - there is NO CHR-RAM clear loop — pattern tables come from the CHR-ROM
|
|
7
|
+
; bank (the CHARS segment / ROM2 area) the PPU reads directly.
|
|
8
|
+
;
|
|
9
|
+
; The NMI handler is the canonical sprite-engine sequence (OAM DMA + VRAM-queue
|
|
10
|
+
; flush + scroll + PPUCTRL), same as the CHR-RAM runtime.
|
|
11
|
+
;
|
|
12
|
+
; Loaded silently by the linkerConfig:"chr-rom" preset.
|
|
13
|
+
|
|
14
|
+
.export _exit
|
|
15
|
+
.export __STARTUP__ : absolute = 1
|
|
16
|
+
.export start
|
|
17
|
+
.export nmi
|
|
18
|
+
.export irq
|
|
19
|
+
.export _shadow_oam
|
|
20
|
+
|
|
21
|
+
.import initlib, donelib, callmain
|
|
22
|
+
.import _main, zerobss, copydata
|
|
23
|
+
.import __RAM_START__, __RAM_SIZE__
|
|
24
|
+
.import __SRAM_START__, __SRAM_SIZE__
|
|
25
|
+
.import _vram_queue_flush
|
|
26
|
+
.import _scroll_x, _scroll_y, _ppuctrl_value, _nmi_counter
|
|
27
|
+
.importzp c_sp
|
|
28
|
+
|
|
29
|
+
; ------------------------------------------------------------------------
|
|
30
|
+
; 16-byte iNES header — CHR-ROM (byte 5 = 1 → one 8KB CHR-ROM bank).
|
|
31
|
+
|
|
32
|
+
.segment "HEADER"
|
|
33
|
+
.byte $4e, $45, $53, $1a ; "NES" + EOF
|
|
34
|
+
.byte 2 ; PRG-ROM banks (16K each) → 32K
|
|
35
|
+
.byte 1 ; CHR-ROM banks (8K each) → 8K CHR-ROM
|
|
36
|
+
.byte %00000001 ; flags6 — vertical mirroring
|
|
37
|
+
.byte %00000000 ; flags7 — mapper hi nybble
|
|
38
|
+
.byte 0, 0, 0, 0, 0, 0, 0, 0
|
|
39
|
+
|
|
40
|
+
; ------------------------------------------------------------------------
|
|
41
|
+
.segment "STARTUP"
|
|
42
|
+
|
|
43
|
+
start:
|
|
44
|
+
sei
|
|
45
|
+
cld
|
|
46
|
+
ldx #$ff
|
|
47
|
+
txs
|
|
48
|
+
|
|
49
|
+
; Disable everything that could fire during init.
|
|
50
|
+
lda #0
|
|
51
|
+
sta $2000 ; disable NMI
|
|
52
|
+
sta $2001 ; disable rendering
|
|
53
|
+
sta $4010 ; disable DMC IRQ
|
|
54
|
+
sta $4015 ; disable APU channels
|
|
55
|
+
bit $2002 ; clear vblank flag
|
|
56
|
+
|
|
57
|
+
; Wait two VBlanks before touching the PPU (standard NES init).
|
|
58
|
+
@vbl1: bit $2002
|
|
59
|
+
bpl @vbl1
|
|
60
|
+
@vbl2: bit $2002
|
|
61
|
+
bpl @vbl2
|
|
62
|
+
|
|
63
|
+
; Initialise shadow_oam to Y=$FF (off-screen) before anything else.
|
|
64
|
+
ldx #0
|
|
65
|
+
lda #$ff
|
|
66
|
+
@oam: sta _shadow_oam,x
|
|
67
|
+
inx
|
|
68
|
+
bne @oam
|
|
69
|
+
|
|
70
|
+
; NO CHR-RAM clear — pattern tables live in the CHR-ROM bank (CHARS).
|
|
71
|
+
; Just point PPUADDR at the palette ($3F00) ready for the caller.
|
|
72
|
+
bit $2002 ; reset PPUADDR latch
|
|
73
|
+
lda #$3F
|
|
74
|
+
sta $2006
|
|
75
|
+
lda #$00
|
|
76
|
+
sta $2006
|
|
77
|
+
|
|
78
|
+
; Clear BSS + copy DATA (cc65 conventions).
|
|
79
|
+
jsr zerobss
|
|
80
|
+
jsr copydata
|
|
81
|
+
|
|
82
|
+
; Set up cc65's C parameter stack pointer.
|
|
83
|
+
lda #<(__SRAM_START__ + __SRAM_SIZE__)
|
|
84
|
+
ldx #>(__SRAM_START__ + __SRAM_SIZE__)
|
|
85
|
+
sta c_sp
|
|
86
|
+
stx c_sp+1
|
|
87
|
+
|
|
88
|
+
jsr initlib
|
|
89
|
+
jsr callmain
|
|
90
|
+
|
|
91
|
+
_exit: jsr donelib
|
|
92
|
+
jmp start
|
|
93
|
+
|
|
94
|
+
; ------------------------------------------------------------------------
|
|
95
|
+
; NMI handler — runs every vblank when ppuctrl bit 7 is set.
|
|
96
|
+
|
|
97
|
+
.segment "STARTUP"
|
|
98
|
+
|
|
99
|
+
nmi:
|
|
100
|
+
pha
|
|
101
|
+
txa
|
|
102
|
+
pha
|
|
103
|
+
tya
|
|
104
|
+
pha
|
|
105
|
+
|
|
106
|
+
; OAM DMA: copy 256 bytes from $0200 to PPU OAM. Takes 513 cycles.
|
|
107
|
+
lda #$00
|
|
108
|
+
sta $2003 ; PPU OAMADDR = 0
|
|
109
|
+
lda #$02 ; high byte of $0200
|
|
110
|
+
sta $4014 ; PPU OAMDMA — kicks off the copy
|
|
111
|
+
|
|
112
|
+
; Flush the VRAM queue (nametable/palette writes game code stashed).
|
|
113
|
+
jsr _vram_queue_flush
|
|
114
|
+
|
|
115
|
+
; Reset PPUADDR to $2000 so the PPU doesn't sample random VRAM as BG.
|
|
116
|
+
bit $2002
|
|
117
|
+
lda #$20
|
|
118
|
+
sta $2006
|
|
119
|
+
lda #$00
|
|
120
|
+
sta $2006
|
|
121
|
+
|
|
122
|
+
; Set scroll (two PPUSCROLL writes: x then y).
|
|
123
|
+
lda _scroll_x
|
|
124
|
+
sta $2005
|
|
125
|
+
lda _scroll_y
|
|
126
|
+
sta $2005
|
|
127
|
+
|
|
128
|
+
; Re-enable NMI + base nametable + pattern-table bits via cached PPUCTRL.
|
|
129
|
+
lda _ppuctrl_value
|
|
130
|
+
sta $2000
|
|
131
|
+
|
|
132
|
+
; Tick the frame counter so ppu_wait_nmi can return.
|
|
133
|
+
inc _nmi_counter
|
|
134
|
+
|
|
135
|
+
pla
|
|
136
|
+
tay
|
|
137
|
+
pla
|
|
138
|
+
tax
|
|
139
|
+
pla
|
|
140
|
+
rti
|
|
141
|
+
|
|
142
|
+
irq: rti
|
|
143
|
+
|
|
144
|
+
; ------------------------------------------------------------------------
|
|
145
|
+
; Shadow OAM at $0200 — the NMI handler DMAs this to the PPU each frame.
|
|
146
|
+
.segment "OAM"
|
|
147
|
+
_shadow_oam: .res 256
|
|
148
|
+
|
|
149
|
+
; ------------------------------------------------------------------------
|
|
150
|
+
.segment "VECTORS"
|
|
151
|
+
.word nmi ; $FFFA
|
|
152
|
+
.word start ; $FFFC
|
|
153
|
+
.word irq ; $FFFE
|
|
@@ -20,7 +20,6 @@ const hex2 = (b) => "$" + (b & 0xFF).toString(16).padStart(2, "0").toUpperCase()
|
|
|
20
20
|
// Label form across disassemblers: `L` + 4..8 hex digits (m68k targets can be
|
|
21
21
|
// 24-bit, e.g. an $E0FF00xx RAM mirror → LE0FF0080).
|
|
22
22
|
const LABEL_RE = /^(L[0-9A-Fa-f]{4,8}):\s*$/;
|
|
23
|
-
const LABEL_REF_RE = /\bL[0-9A-Fa-f]{4,8}\b/g;
|
|
24
23
|
|
|
25
24
|
/** Parse one disasm line into {label?, code?, bytes?[]}. */
|
|
26
25
|
function parseLine(line) {
|
|
@@ -67,7 +66,7 @@ function firstDiff(a, b) {
|
|
|
67
66
|
// m68k (genesis) → objdump → m68k-elf-as/ld/objcopy
|
|
68
67
|
// arm (gba) → objdump → arm-none-eabi-as/ld/objcopy
|
|
69
68
|
|
|
70
|
-
const CPU_FAMILY = {
|
|
69
|
+
export const CPU_FAMILY = {
|
|
71
70
|
nes: "6502", c64: "6502", atari2600: "6502", atari7800: "6502", lynx: "6502",
|
|
72
71
|
snes: "65816",
|
|
73
72
|
sms: "z80", gg: "z80", msx: "z80",
|
|
@@ -254,7 +253,15 @@ async function reassembleGnuNative(disasm, startAddress, original, tools, family
|
|
|
254
253
|
if (!pinned) { const n = codeIdx.find((i) => !forced.has(i)); if (n == null) break; forced.add(n); }
|
|
255
254
|
}
|
|
256
255
|
// Floor: clean all-`.byte` (proven byte-exact, no labels to perturb layout).
|
|
257
|
-
|
|
256
|
+
// Mirror build()'s `.org` for the no-link (z80/gbz80) path: without it,
|
|
257
|
+
// objcopy emits the section from file offset 0, and assemble()'s
|
|
258
|
+
// `bin.slice(startAddress, …)` then returns bytes that are `startAddress`-short
|
|
259
|
+
// (empty for a $4000/$8000-based region) — so any non-zero-org region (MSX
|
|
260
|
+
// $4010, GB bank1 $4000, …) silently fails the floor. The linked path sets the
|
|
261
|
+
// origin via the link script, so it must NOT carry a redundant `.org`.
|
|
262
|
+
const rows = [".section .text", ".global _start"];
|
|
263
|
+
if (tools.noLink) rows.push(`.org 0x${startAddress.toString(16)}`);
|
|
264
|
+
rows.push("_start:");
|
|
258
265
|
for (let i = 0; i < original.length; i += 16) rows.push("\t.byte " + Array.from(original.slice(i, i + 16)).map((b) => "0x" + b.toString(16).padStart(2, "0")).join(","));
|
|
259
266
|
const r = await assemble(rows.join("\n") + "\n");
|
|
260
267
|
const ok = r.ok && r.bytes.length === original.length && firstDiff(original, r.bytes) < 0;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// This module is platform-agnostic: each toolchain passes in how to hash its
|
|
18
18
|
// source, where its seed lives, and how to compile from source.
|
|
19
19
|
|
|
20
|
-
import { readFile, writeFile, mkdir
|
|
20
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
21
21
|
import { createHash } from "node:crypto";
|
|
22
22
|
import path from "node:path";
|
|
23
23
|
import os from "node:os";
|
|
@@ -127,7 +127,7 @@ async function compileSgdkRuntime(baseHeaders, cc1Options) {
|
|
|
127
127
|
for (const [k, v] of Object.entries(localHeaders)) {
|
|
128
128
|
if (/\.i80$/i.test(k)) z80Includes[path.basename(k)] = v;
|
|
129
129
|
}
|
|
130
|
-
async function loadI80From(dir,
|
|
130
|
+
async function loadI80From(dir, _rel = "") {
|
|
131
131
|
let ents;
|
|
132
132
|
try { ents = await readdir(dir, { withFileTypes: true }); } catch { return; }
|
|
133
133
|
for (const e of ents) {
|
|
@@ -268,8 +268,10 @@ async function buildWithSgdk({ sources, headers, binaryIncludes, cc1Options, reb
|
|
|
268
268
|
// implicit decls, …) parsed into structured issues[]. The SGDK runtime is
|
|
269
269
|
// compiled WITHOUT these (sgdkCc1Options) — we can't fix SDK warnings and they'd
|
|
270
270
|
// bury the agent's own. -Wno-unused-parameter avoids the common `(void)hard`
|
|
271
|
-
// scaffold-param noise.
|
|
272
|
-
|
|
271
|
+
// scaffold-param noise. -Wno-main: SGDK MANDATES `int main(bool hardReset)`
|
|
272
|
+
// (sys.c declares it and calls main(TRUE)/main(FALSE)); GCC's -Wmain objects to
|
|
273
|
+
// the non-standard signature, but it's REQUIRED here, not a bug — so silence it.
|
|
274
|
+
const userCc1Options = [...sgdkCc1Options, "-Wall", "-Wextra", "-Wno-unused-parameter", "-Wno-main"];
|
|
273
275
|
|
|
274
276
|
// ── Stage A: gather SGDK headers (visible to tcc via tcc-style flat mount) ──
|
|
275
277
|
// cc1's -iquote /work picks up sibling files mounted alongside main.c.
|
package/src/toolchains/index.js
CHANGED
|
@@ -117,6 +117,27 @@ const PLATFORM_DEFAULT_LANGUAGE = {
|
|
|
117
117
|
msx: "c",
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Order build issues by DANGER so the agent reads the lethal ones first:
|
|
122
|
+
* critical (crash-class, e.g. the uint8 infinite-loop) → error → warning → info.
|
|
123
|
+
* Stable within a rank (preserves source order / file:line). Pure; no dedup.
|
|
124
|
+
* @param {Array<{severity?:string, critical?:boolean}>} issues
|
|
125
|
+
* @returns {Array} the same issues, ranked
|
|
126
|
+
*/
|
|
127
|
+
export function rankIssues(issues) {
|
|
128
|
+
const rank = (i) =>
|
|
129
|
+
i?.critical ? 0
|
|
130
|
+
: i?.severity === "error" ? 1
|
|
131
|
+
: i?.severity === "warning" ? 2
|
|
132
|
+
: 3;
|
|
133
|
+
// map→sort→unmap keeps it stable (Array.prototype.sort is stable in V8, but
|
|
134
|
+
// tie-break on original index to be explicit and portable).
|
|
135
|
+
return issues
|
|
136
|
+
.map((issue, idx) => ({ issue, idx }))
|
|
137
|
+
.sort((a, b) => rank(a.issue) - rank(b.issue) || a.idx - b.idx)
|
|
138
|
+
.map((x) => x.issue);
|
|
139
|
+
}
|
|
140
|
+
|
|
120
141
|
/**
|
|
121
142
|
* Public API for the platforms tool to discover the language matrix.
|
|
122
143
|
* Returns `{defaultLanguage, languages: [{language, toolchain, available, note?}]}`.
|
|
@@ -835,10 +856,13 @@ export async function buildForPlatform(args) {
|
|
|
835
856
|
r.log += "\n--- MSX ROM header present (\"AB\") ---";
|
|
836
857
|
}
|
|
837
858
|
}
|
|
838
|
-
// Combine lint warnings with parsed build log
|
|
839
|
-
//
|
|
859
|
+
// Combine lint warnings with parsed build log, then RANK so an agent
|
|
860
|
+
// triaging issues[] sees the dangerous ones first: crash-class (critical)
|
|
861
|
+
// → errors → plain warnings. Without this, a "WILL HANG" infinite-loop
|
|
862
|
+
// warning sits among unused-variable noise and gets skipped (the exact
|
|
863
|
+
// "agent missed the warning, hit the crash 100 functions later" failure).
|
|
840
864
|
const buildIssues = parseBuildLog(r.log);
|
|
841
|
-
const issues = [...lintIssues, ...buildIssues];
|
|
865
|
+
const issues = rankIssues([...lintIssues, ...buildIssues]);
|
|
842
866
|
return {
|
|
843
867
|
ok: r.exitCode === 0 && binary !== null,
|
|
844
868
|
binary,
|
|
@@ -32,6 +32,9 @@ export function parseBuildLog(log) {
|
|
|
32
32
|
const baseStage = stage.split(/\s|\(/)[0].toLowerCase();
|
|
33
33
|
if (/^cc65$|^ca65$|^ld65$/.test(baseStage)) {
|
|
34
34
|
issues.push(...parseCc65Like(text, baseStage));
|
|
35
|
+
// ld65's linker-level errors (segment-missing / memory-overflow) carry no
|
|
36
|
+
// file:line and slip past parseCc65Like — pick them up too.
|
|
37
|
+
if (baseStage === "ld65") issues.push(...parseLd65Linker(text, baseStage));
|
|
35
38
|
} else if (/^dasm$/.test(baseStage)) {
|
|
36
39
|
issues.push(...parseDasm(text));
|
|
37
40
|
} else if (/^asar$/.test(baseStage)) {
|
|
@@ -68,6 +71,7 @@ export function parseBuildLog(log) {
|
|
|
68
71
|
// failure). Include vasm + sdcc + wla, which the old fallback omitted.
|
|
69
72
|
const tag = baseStage || "unknown";
|
|
70
73
|
issues.push(...parseCc65Like(text, tag));
|
|
74
|
+
issues.push(...parseLd65Linker(text, tag));
|
|
71
75
|
issues.push(...parseSdcc(text, tag));
|
|
72
76
|
issues.push(...parseDasm(text));
|
|
73
77
|
issues.push(...parseAsar(text, tag));
|
|
@@ -109,6 +113,50 @@ function splitByStage(log) {
|
|
|
109
113
|
return stages;
|
|
110
114
|
}
|
|
111
115
|
|
|
116
|
+
// ld65 LINKER diagnostics have NO file:line — parseCc65Like (which requires a
|
|
117
|
+
// `file:line:` lead) misses them entirely, so a failed link returned issues[]
|
|
118
|
+
// EMPTY even though the real error sat in the log. The common ones on a
|
|
119
|
+
// mis-wired NES rebuild (wrong/absent linker config for the project's segments):
|
|
120
|
+
// ld65: Warning: Segment 'HEADER' does not exist
|
|
121
|
+
// ld65: Error: Memory area overflow in 'ROM0', segment 'CODE' (6 bytes)
|
|
122
|
+
// Error: Cannot generate most of the files due to memory area overflow
|
|
123
|
+
// They appear either bare or with an `ld65:`/`ld65.exe:` tool prefix. We add a
|
|
124
|
+
// short hint on the segment/overflow case (the linker config doesn't match the
|
|
125
|
+
// project's segments — the exact CHR-ROM-vs-CHR-RAM mismatch agents hit).
|
|
126
|
+
function parseLd65Linker(text, stage) {
|
|
127
|
+
const out = [];
|
|
128
|
+
const re = /^(?:ld65(?:\.exe)?:\s*)?(?<sev>Error|Warning):\s*(?<msg>.+)$/gm;
|
|
129
|
+
let m;
|
|
130
|
+
while ((m = re.exec(text))) {
|
|
131
|
+
const msg = m.groups.msg.trim().replace(/\x1b\[[0-9;]*m/g, "");
|
|
132
|
+
// Skip the file:line form — parseCc65Like already owns those (and a leading
|
|
133
|
+
// path would have been consumed as the message here, doubling the issue).
|
|
134
|
+
if (/^[^\s:]+:\d+:/.test(msg)) continue;
|
|
135
|
+
// Actionable hint in a SEPARATE field (matching GNU ld / sdld), not glued
|
|
136
|
+
// onto the message — two common ld65 link failures:
|
|
137
|
+
let hint;
|
|
138
|
+
if (/does not exist|overflow|Cannot generate/i.test(msg)) {
|
|
139
|
+
hint =
|
|
140
|
+
"The linker config's segments don't match the project. For an NROM " +
|
|
141
|
+
"rebuild use build({inesHeader:{...}}) or linkerConfig:'chr-rom'; otherwise " +
|
|
142
|
+
"check that your .cfg's SEGMENTS match the .segment names in your source.";
|
|
143
|
+
} else if (/unresolved external|undefined symbol/i.test(msg)) {
|
|
144
|
+
const sym = msg.match(/['"`]?(?<s>[A-Za-z_]\w*)['"`]?\s*$/)?.groups?.s;
|
|
145
|
+
hint =
|
|
146
|
+
`${sym ? `\`${sym}'` : "That symbol"} is referenced but never defined or linked. ` +
|
|
147
|
+
"Add the source/object that defines it (or the right cc65 lib for this platform) " +
|
|
148
|
+
"to your build, or check for a typo.";
|
|
149
|
+
}
|
|
150
|
+
out.push({
|
|
151
|
+
severity: m.groups.sev.toLowerCase() === "error" ? "error" : "warning",
|
|
152
|
+
message: msg,
|
|
153
|
+
stage: stage || "ld65",
|
|
154
|
+
...(hint ? { hint } : {}),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
|
|
112
160
|
// cc65, ca65, ld65 all use the gcc-style `file:line:col?: severity: message`.
|
|
113
161
|
// Example:
|
|
114
162
|
// /work/main.s:12: Error: Cannot open include file 'longbranch.mac'
|
|
@@ -173,10 +221,25 @@ function parseSdcc(text, stage) {
|
|
|
173
221
|
// An "Undefined Global" is effectively an error even though ASlink labels it
|
|
174
222
|
// a warning — the ROM won't run. Promote it so the agent treats it as fatal.
|
|
175
223
|
const isUndef = /undefined\s+global/i.test(msg);
|
|
224
|
+
// Give the same actionable hint GNU ld's undefined-reference path gets, so
|
|
225
|
+
// the SDCC platforms (GB/GBC/SMS/GG/MSX) reach parity on the single most
|
|
226
|
+
// common link failure (forgot to include the source/runtime that defines it).
|
|
227
|
+
// SDCC mangles C symbols with a leading '_' — show the C name too.
|
|
228
|
+
let hint;
|
|
229
|
+
if (isUndef) {
|
|
230
|
+
const sym = msg.match(/global\s+['"]?(?<s>\w+)['"]?/i)?.groups?.s;
|
|
231
|
+
const cName = sym && sym.startsWith("_") ? sym.slice(1) : sym;
|
|
232
|
+
hint =
|
|
233
|
+
`${sym ? `\`${sym}'` : "That symbol"} is called but never defined or linked` +
|
|
234
|
+
`${cName && cName !== sym ? ` (the C name is \`${cName}'; SDCC prefixes an underscore)` : ""}. ` +
|
|
235
|
+
"Add the source file (or runtime/library) that defines it to your build's sources/includes, " +
|
|
236
|
+
"or check for a typo in the name.";
|
|
237
|
+
}
|
|
176
238
|
out.push({
|
|
177
239
|
severity: lm.groups.sev === "Error" || isUndef ? "error" : "warning",
|
|
178
240
|
message: "linker: " + msg,
|
|
179
241
|
stage: "sdld",
|
|
242
|
+
...(hint ? { hint } : {}),
|
|
180
243
|
});
|
|
181
244
|
}
|
|
182
245
|
return out;
|
|
@@ -268,12 +331,26 @@ function parseWla(text, stage = "wla") {
|
|
|
268
331
|
const reLink = /^(?<obj>\S+\.obj):\s*(?<file>[^\s:]+):(?<line>\d+):\s*(?<phase>[A-Z_]+):\s*(?<msg>.+)$/gm;
|
|
269
332
|
let m;
|
|
270
333
|
while ((m = reLink.exec(text))) {
|
|
334
|
+
const rawMsg = m.groups.msg.trim();
|
|
335
|
+
// An "unknown label" reference is SNES's undefined-symbol failure — give it
|
|
336
|
+
// the same actionable hint ld65/sdld/GNU ld carry, so SNES reaches parity.
|
|
337
|
+
let hint;
|
|
338
|
+
const lbl = /unknown label\s+["'`]?(?<l>[A-Za-z_]\w*)/i.exec(rawMsg)?.groups?.l;
|
|
339
|
+
if (lbl || /reference to an unknown/i.test(rawMsg)) {
|
|
340
|
+
hint =
|
|
341
|
+
`${lbl ? `\`${lbl}'` : "That label"} is referenced but never defined or linked. ` +
|
|
342
|
+
"Add the source/object that defines it (or the right PVSnesLib runtime) to your build, " +
|
|
343
|
+
"or check for a typo.";
|
|
344
|
+
}
|
|
271
345
|
out.push({
|
|
272
346
|
severity: "error",
|
|
273
347
|
file: m.groups.file,
|
|
274
348
|
line: parseInt(m.groups.line, 10),
|
|
275
|
-
|
|
349
|
+
// Keep the wla internal phase name (FIX_REFERENCES, etc.) out of the
|
|
350
|
+
// agent-facing message — it's noise; the message + hint say what to do.
|
|
351
|
+
message: rawMsg,
|
|
276
352
|
stage: stage === "wla" ? "wlalink" : stage,
|
|
353
|
+
...(hint ? { hint } : {}),
|
|
277
354
|
});
|
|
278
355
|
}
|
|
279
356
|
// wla-65816 assembler: "<file>:<line>: ERROR|WARNING: <message>"
|
|
@@ -136,10 +136,14 @@ export function lintSdccSource(source, file = "main.c", opts = {}) {
|
|
|
136
136
|
if (bound !== null && bound > 255) {
|
|
137
137
|
issues.push({
|
|
138
138
|
severity: "warning",
|
|
139
|
+
// CRASH-CLASS: not cosmetic — this loop never exits and hangs the
|
|
140
|
+
// game. `critical` lifts it above ordinary warnings so an agent
|
|
141
|
+
// triaging issues[] can't miss it among unused-variable noise.
|
|
142
|
+
critical: true,
|
|
139
143
|
file,
|
|
140
144
|
line: i + 1,
|
|
141
145
|
stage: "lint",
|
|
142
|
-
message: `uint8 loop counter '${counter}' with bound ${bound} (> 255) — infinite loop`,
|
|
146
|
+
message: `WILL HANG: uint8 loop counter '${counter}' with bound ${bound} (> 255) — infinite loop`,
|
|
143
147
|
details: `A u8/uint8_t/char counter can never reach ${bound}, so this loop never exits and all code after it is dead. ${portLabel} gives no warning. Declare '${counter}' as uint16_t. See GB TROUBLESHOOTING § uint8 loop-bound trap.`,
|
|
144
148
|
ref: "uint8-loop-bound",
|
|
145
149
|
});
|
|
@@ -56,7 +56,7 @@ export const SDCC_PORTS = {
|
|
|
56
56
|
msx: { marg: "z80", libDir: "z80" },
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
import { runIsolated, textFile,
|
|
59
|
+
import { runIsolated, textFile, getOutputText } from "../_worker/run.js";
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Tag the abort/crash log with the SDCC-flavored hint pointing at the
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// #include that generated .h. Built by scripts/build-sjasm.sh.
|
|
6
6
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { existsSync
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -288,7 +288,7 @@ async function buildWithPvSnesLib({ sources, headers, tccOptions, wlaOptions, bi
|
|
|
288
288
|
* Minimum-viable path (R16 original behavior). No PVSnesLib runtime;
|
|
289
289
|
* bundled original crt0.asm + hdr.asm support a bare `int main()`.
|
|
290
290
|
*/
|
|
291
|
-
async function buildMinimal({ sources, headers, tccOptions, wlaOptions,
|
|
291
|
+
async function buildMinimal({ sources, headers, tccOptions, wlaOptions, _binaryIncludes = {} }) {
|
|
292
292
|
let log = "";
|
|
293
293
|
const hdrAsm = await readFile(path.join(MINIMAL_LIB_DIR, "hdr.asm"), "utf-8");
|
|
294
294
|
const crt0Asm = await readFile(path.join(MINIMAL_LIB_DIR, "crt0.asm"), "utf-8");
|
|
@@ -380,7 +380,7 @@ async function buildMinimal({ sources, headers, tccOptions, wlaOptions, binaryIn
|
|
|
380
380
|
let _headerCache = null;
|
|
381
381
|
async function loadPvSnesLibHeaders() {
|
|
382
382
|
if (_headerCache) return _headerCache;
|
|
383
|
-
const { readdir
|
|
383
|
+
const { readdir } = await import("node:fs/promises");
|
|
384
384
|
const out = {};
|
|
385
385
|
/**
|
|
386
386
|
* @param {string} dir
|
|
@@ -49,10 +49,8 @@ function vasm68kPreflight(source) {
|
|
|
49
49
|
const issues = [];
|
|
50
50
|
|
|
51
51
|
// Check 1: source includes an org $00000000 or starts at $00 implicitly.
|
|
52
|
-
// vasm68k defaults to $00000000 if no org is given, so missing org is fine
|
|
53
|
-
//
|
|
54
|
-
// vector table, the cart is unbootable.
|
|
55
|
-
const hasOrg0 = /\borg\s+\$0+\b/i.test(source) || /\borg\s+0\b/i.test(source);
|
|
52
|
+
// vasm68k defaults to $00000000 if no org is given, so a missing org is fine;
|
|
53
|
+
// what actually breaks boot is a missing reset vector, which we check below.
|
|
56
54
|
const hasReset = /\bdc\.l\s+(\$00FF[E-F][0-9A-F]{3}|_reset|reset)\b/i.test(source);
|
|
57
55
|
if (!hasReset) {
|
|
58
56
|
issues.push(
|
|
@@ -15,7 +15,7 @@ import { fileURLToPath } from "node:url";
|
|
|
15
15
|
import { existsSync } from "node:fs";
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
|
|
18
|
-
import { runIsolated, textFile, binaryFile, getOutputBytes
|
|
18
|
+
import { runIsolated, textFile, binaryFile, getOutputBytes } from "../_worker/run.js";
|
|
19
19
|
|
|
20
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
21
|
const __dirname = path.dirname(__filename);
|