romdevtools 0.71.1 → 0.84.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 +19 -16
- package/CHANGELOG.md +175 -0
- package/README.md +5 -5
- package/examples/README.md +1 -0
- package/examples/gametank/templates/gt_draw.h +91 -0
- package/examples/gametank/templates/gt_hud.h +89 -0
- package/examples/gametank/templates/gt_palette.h +59 -0
- package/examples/gametank/templates/gt_sound.h +90 -0
- package/examples/gametank/templates/gt_sprites.h +53 -0
- package/examples/gametank/templates/platformer.c +318 -0
- package/examples/gametank/templates/puzzle.c +295 -0
- package/examples/gametank/templates/racing.c +139 -0
- package/examples/gametank/templates/shmup.c +277 -0
- package/examples/gametank/templates/sports.c +119 -0
- package/package.json +23 -22
- package/src/cores/capabilities.js +24 -0
- package/src/cores/registry.js +7 -1
- package/src/host/LibretroHost.js +90 -17
- package/src/host/callbacks.js +20 -2
- package/src/host/cpu-state.js +17 -0
- package/src/host/gametank-acp-state.js +53 -0
- package/src/http/skill-doc.js +10 -6
- package/src/mcp/tools/audio.js +1 -1
- package/src/mcp/tools/cart-parts.js +76 -0
- package/src/mcp/tools/lifecycle.js +1 -1
- package/src/mcp/tools/platform-tools.js +9 -1
- package/src/platforms/gametank/lib/gt/audio/acp_image.bin +0 -0
- package/src/platforms/gametank/lib/gt/audio/audio_coprocessor.c +55 -0
- package/src/platforms/gametank/lib/gt/audio/audio_coprocessor.h +34 -0
- package/src/platforms/gametank/lib/gt/audio/audio_fw.asm +223 -0
- package/src/platforms/gametank/lib/gt/audio/audio_fw_blob.s +8 -0
- package/src/platforms/gametank/lib/gt/audio/instruments.c +79 -0
- package/src/platforms/gametank/lib/gt/audio/instruments.h +25 -0
- package/src/platforms/gametank/lib/gt/audio/music.c +370 -0
- package/src/platforms/gametank/lib/gt/audio/music.h +35 -0
- package/src/platforms/gametank/lib/gt/audio/note_numbers.h +132 -0
- package/src/platforms/gametank/lib/gt/audio/scaled_sines.raw +0 -0
- package/src/platforms/gametank/lib/gt/audio/sine_256_-63_63.bin +0 -0
- package/src/platforms/gametank/lib/gt/banking.c +29 -0
- package/src/platforms/gametank/lib/gt/banking.h +8 -0
- package/src/platforms/gametank/lib/gt/banking2.s +41 -0
- package/src/platforms/gametank/lib/gt/crt0.s +102 -0
- package/src/platforms/gametank/lib/gt/draw_logo.s +55 -0
- package/src/platforms/gametank/lib/gt/feature/persist/persist.c +103 -0
- package/src/platforms/gametank/lib/gt/feature/persist/persist.h +13 -0
- package/src/platforms/gametank/lib/gt/feature/random/random.c +40 -0
- package/src/platforms/gametank/lib/gt/feature/random/random.h +18 -0
- package/src/platforms/gametank/lib/gt/feature/text/text.c +111 -0
- package/src/platforms/gametank/lib/gt/feature/text/text.h +25 -0
- package/src/platforms/gametank/lib/gt/gametank.c +12 -0
- package/src/platforms/gametank/lib/gt/gametank.h +80 -0
- package/src/platforms/gametank/lib/gt/gametank_logo.inc +393 -0
- package/src/platforms/gametank/lib/gt/gen/assets/assets_index.h +9 -0
- package/src/platforms/gametank/lib/gt/gen/assets/sdk_default.h +5 -0
- package/src/platforms/gametank/lib/gt/gen/bank_nums.h +11 -0
- package/src/platforms/gametank/lib/gt/gen/modules_enabled.h +4 -0
- package/src/platforms/gametank/lib/gt/gen/modules_enabled.inc +4 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_direct.c +132 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_direct.h +75 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_queue.c +177 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_queue.h +35 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_util.s +157 -0
- package/src/platforms/gametank/lib/gt/gfx/gfx_sys.c +43 -0
- package/src/platforms/gametank/lib/gt/gfx/gfx_sys.h +46 -0
- package/src/platforms/gametank/lib/gt/gfx/sprites.c +216 -0
- package/src/platforms/gametank/lib/gt/gfx/sprites.h +41 -0
- package/src/platforms/gametank/lib/gt/init.c +9 -0
- package/src/platforms/gametank/lib/gt/input.c +26 -0
- package/src/platforms/gametank/lib/gt/input.h +20 -0
- package/src/platforms/gametank/lib/gt/interrupt.s +67 -0
- package/src/platforms/gametank/lib/gt/vectors.s +14 -0
- package/src/platforms/gametank/lib/gt/wait.s +53 -0
- package/src/playtest/playtest.js +174 -26
- package/src/playtest/resampler/build.sh +19 -0
- package/src/playtest/resampler/index.mjs +75 -0
- package/src/playtest/resampler/resampler.c +129 -0
- package/src/playtest/resampler/resampler.mjs +2 -0
- package/src/playtest/resampler/resampler.wasm +0 -0
- package/src/toolchains/arm-none-eabi-gcc/gcc.js +39 -188
- package/src/toolchains/asar/asar.js +10 -15
- package/src/toolchains/cc65/cc65.js +82 -92
- package/src/toolchains/cc65/da65.js +12 -17
- package/src/toolchains/cc65/preset-resolver.js +13 -2
- package/src/toolchains/cc65/presets/gametank/gametank.h +80 -0
- package/src/toolchains/cc65/presets/gametank/sdk.cfg +32 -0
- package/src/toolchains/cc65/presets/gametank/sdk.crt0.s +61 -0
- package/src/toolchains/cc65/presets/gametank/sdk.vectors.s +11 -0
- package/src/toolchains/cc65/presets/gametank/single-bank.cfg +28 -0
- package/src/toolchains/cc65/presets/gametank/single-bank.crt0.s +71 -0
- package/src/toolchains/cc65/presets/gametank/single-bank.vectors.s +12 -0
- package/src/toolchains/common/c-build.js +109 -0
- package/src/toolchains/common/gcc-toolchain.js +164 -0
- package/src/toolchains/common/wasm-tool.js +101 -0
- package/src/toolchains/dasm/dasm.js +12 -18
- package/src/toolchains/gba-c/gba-c.js +253 -305
- package/src/toolchains/genesis-c/genesis-c.js +196 -225
- package/src/toolchains/index.js +58 -4
- package/src/toolchains/m68k-elf-gcc/gcc.js +39 -202
- package/src/toolchains/mips-c/mips-c.js +68 -78
- package/src/toolchains/mips-elf-gcc/gcc.js +55 -118
- package/src/toolchains/rgbds/rgbds.js +7 -19
- package/src/toolchains/sdcc/sdcc.js +35 -26
- package/src/toolchains/sh-c/sh-c.js +44 -52
- package/src/toolchains/sh-elf-gcc/gcc.js +55 -110
- package/src/toolchains/sjasm/sjasm.js +10 -14
- package/src/toolchains/snes-c/snes-c.js +125 -150
- package/src/toolchains/tcc816/tcc816.js +10 -15
- package/src/toolchains/vasm68k/vasm68k.js +11 -16
- package/src/toolchains/wladx/wladx.js +5 -17
- package/src/toolchains/z80/binutils.js +5 -11
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* GameTank-specific defines
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#ifndef GAMETANK_H
|
|
6
|
+
#define GAMETANK_H
|
|
7
|
+
|
|
8
|
+
typedef char bool;
|
|
9
|
+
#define true 1
|
|
10
|
+
#define false 0
|
|
11
|
+
|
|
12
|
+
#define audio_reset ((char *) 0x2000)
|
|
13
|
+
#define audio_nmi ((char *) 0x2001)
|
|
14
|
+
#define audio_rate ((char *) 0x2006)
|
|
15
|
+
#define dma_flags ((char *) 0x2007)
|
|
16
|
+
#define bank_reg ((char *) 0x2005)
|
|
17
|
+
#define via ((char*) 0x2800)
|
|
18
|
+
#define aram ((unsigned char *) 0x3000)
|
|
19
|
+
#define vram ((unsigned char *) 0x4000)
|
|
20
|
+
#define vram_VX ((char *) 0x4000)
|
|
21
|
+
#define vram_VY ((char *) 0x4001)
|
|
22
|
+
#define vram_GX ((char *) 0x4002)
|
|
23
|
+
#define vram_GY ((char *) 0x4003)
|
|
24
|
+
#define vram_WIDTH ((char *) 0x4004)
|
|
25
|
+
#define vram_HEIGHT ((char *) 0x4005)
|
|
26
|
+
#define vram_COLOR ((char *) 0x4007)
|
|
27
|
+
#define vram_START ((char *) 0x4006)
|
|
28
|
+
|
|
29
|
+
#define SCREEN_WIDTH 128
|
|
30
|
+
#define SCREEN_HEIGHT 128
|
|
31
|
+
|
|
32
|
+
#define DMA_ENABLE 1
|
|
33
|
+
#define DMA_PAGE_OUT 2
|
|
34
|
+
#define DMA_NMI 4
|
|
35
|
+
#define DMA_COLORFILL_ENABLE 8
|
|
36
|
+
#define DMA_GCARRY 16
|
|
37
|
+
#define DMA_CPU_TO_VRAM 32
|
|
38
|
+
#define DMA_IRQ 64
|
|
39
|
+
#define DMA_OPAQUE 128
|
|
40
|
+
|
|
41
|
+
#define BANK_GRAM_MASK 7
|
|
42
|
+
#define BANK_SECOND_FRAMEBUFFER 8
|
|
43
|
+
#define BANK_CLIP_X 16
|
|
44
|
+
#define BANK_CLIP_Y 32
|
|
45
|
+
#define BANK_RAM_MASK 192
|
|
46
|
+
|
|
47
|
+
#define GRAM_PAGE(x) x
|
|
48
|
+
|
|
49
|
+
#define VX 0
|
|
50
|
+
#define VY 1
|
|
51
|
+
#define GX 2
|
|
52
|
+
#define GY 3
|
|
53
|
+
#define WIDTH 4
|
|
54
|
+
#define HEIGHT 5
|
|
55
|
+
#define START 6
|
|
56
|
+
#define COLOR 7
|
|
57
|
+
|
|
58
|
+
#define gamepad_1 ((volatile char *) 0x2008)
|
|
59
|
+
#define gamepad_2 ((volatile char *) 0x2009)
|
|
60
|
+
|
|
61
|
+
#define ORB 0
|
|
62
|
+
#define ORA 1
|
|
63
|
+
#define DDRB 2
|
|
64
|
+
#define DDRA 3
|
|
65
|
+
#define T1C 5
|
|
66
|
+
#define ACR 11
|
|
67
|
+
#define PCR 12
|
|
68
|
+
#define IFR 13
|
|
69
|
+
#define IER 14
|
|
70
|
+
|
|
71
|
+
extern char frameflag, frameflip, flagsMirror, banksMirror, bankflip;
|
|
72
|
+
|
|
73
|
+
void wait();
|
|
74
|
+
void nop5();
|
|
75
|
+
void nop10();
|
|
76
|
+
|
|
77
|
+
#define PROFILER_START(x) via[ORB] = 0x80; via[ORB] = x;
|
|
78
|
+
#define PROFILER_END(x) via[ORB] = 0x80; via[ORB] = x | 0x40;
|
|
79
|
+
|
|
80
|
+
#endif
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# GameTank SDK-runtime single-bank (EEPROM32K) ld65 config. Like single-bank.cfg
|
|
2
|
+
# but adds the segments the bundled SDK runtime uses (LOADERS / COMMON / SAVE /
|
|
3
|
+
# WAVES) — in the multi-bank SDK build these live in dedicated flash banks; for the
|
|
4
|
+
# single 32 KB cart they all fold into the one ROM region (SAVE → RAM). Flat 32 KB
|
|
5
|
+
# image = a valid .gtr (EEPROM32K). VECTORS pinned to $FFFA.
|
|
6
|
+
MEMORY {
|
|
7
|
+
ZP: start = $0000, size = $0100, type = rw, define = yes;
|
|
8
|
+
RAM: start = $0200, size = $1D00, define = yes;
|
|
9
|
+
ROM: start = $8000, size = $8000, file = %O, fill = yes, define = yes;
|
|
10
|
+
}
|
|
11
|
+
SEGMENTS {
|
|
12
|
+
ZEROPAGE: load = ZP, type = zp, define = yes;
|
|
13
|
+
DATA: load = ROM, type = rw, define = yes, run = RAM;
|
|
14
|
+
BSS: load = RAM, type = bss, define = yes;
|
|
15
|
+
HEAP: load = RAM, type = bss, optional = yes;
|
|
16
|
+
STARTUP: load = ROM, type = ro;
|
|
17
|
+
ONCE: load = ROM, type = ro, optional = yes;
|
|
18
|
+
CODE: load = ROM, type = ro;
|
|
19
|
+
RODATA: load = ROM, type = ro;
|
|
20
|
+
LOADERS: load = ROM, type = ro, optional = yes; # SDK sprite/blit loaders
|
|
21
|
+
COMMON: load = ROM, type = ro, optional = yes; # SDK shared code/data
|
|
22
|
+
WAVES: load = ROM, type = ro, optional = yes; # SDK audio wave tables
|
|
23
|
+
SAVE: load = RAM, type = bss, optional = yes; # persist (battery SRAM) — RAM-backed here
|
|
24
|
+
VECTORS: load = ROM, type = ro, start = $FFFA;
|
|
25
|
+
}
|
|
26
|
+
FEATURES {
|
|
27
|
+
CONDES: type = constructor, label = __CONSTRUCTOR_TABLE__, count = __CONSTRUCTOR_COUNT__, segment = ONCE;
|
|
28
|
+
CONDES: type = destructor, label = __DESTRUCTOR_TABLE__, count = __DESTRUCTOR_COUNT__, segment = RODATA;
|
|
29
|
+
}
|
|
30
|
+
SYMBOLS {
|
|
31
|
+
__STACKSIZE__: type = weak, value = $0200;
|
|
32
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
; ---------------------------------------------------------------------------
|
|
2
|
+
; GameTank SDK-runtime crt0 (single-bank EEPROM32K, with the bundled SDK gfx/
|
|
3
|
+
; input/random/ACP runtime). Adapted from gametank_sdk src/gt/crt0.s, MIT — but
|
|
4
|
+
; STRIPPED of flash banking + the audio-FW asset: a single 32 KB cart maps at
|
|
5
|
+
; boot, so there's no flash bank to shift in, and the bare runtime has no
|
|
6
|
+
; asset-pipeline audio firmware. Calls _sdk_init (init_graphics + ACP) then
|
|
7
|
+
; _main. The SDK's interrupt.s provides _nmi_int / _irq_int, so this crt0 does
|
|
8
|
+
; NOT define them (that was the duplicate-symbol clash with the bare crt0).
|
|
9
|
+
; ---------------------------------------------------------------------------
|
|
10
|
+
.export _init, _exit
|
|
11
|
+
.import _main, _sdk_init
|
|
12
|
+
.export __STARTUP__ : absolute = 1
|
|
13
|
+
.import __RAM_START__, __RAM_SIZE__
|
|
14
|
+
.import copydata, zerobss, initlib, donelib
|
|
15
|
+
|
|
16
|
+
.PC02
|
|
17
|
+
|
|
18
|
+
BankReg = $2005
|
|
19
|
+
VIA = $2800
|
|
20
|
+
DDRA = 3
|
|
21
|
+
ORAr = 1
|
|
22
|
+
|
|
23
|
+
.include "zeropage.inc"
|
|
24
|
+
|
|
25
|
+
.segment "STARTUP"
|
|
26
|
+
|
|
27
|
+
_init: LDX #$FF
|
|
28
|
+
TXS
|
|
29
|
+
CLD
|
|
30
|
+
|
|
31
|
+
LDX #0
|
|
32
|
+
viaWakeup:
|
|
33
|
+
INX
|
|
34
|
+
BNE viaWakeup
|
|
35
|
+
|
|
36
|
+
STZ BankReg ; single 32K cart: fixed bank, nothing to shift
|
|
37
|
+
STZ $1FFF
|
|
38
|
+
|
|
39
|
+
LDA #%00000111 ; VIA DDRA: banking pins as output
|
|
40
|
+
STA VIA+DDRA
|
|
41
|
+
LDA #$FF
|
|
42
|
+
STA VIA+ORAr
|
|
43
|
+
|
|
44
|
+
; cc65 C argument-stack pointer = top of work RAM
|
|
45
|
+
LDA #<(__RAM_START__ + __RAM_SIZE__)
|
|
46
|
+
STA c_sp
|
|
47
|
+
LDA #>(__RAM_START__ + __RAM_SIZE__)
|
|
48
|
+
STA c_sp+1
|
|
49
|
+
|
|
50
|
+
JSR zerobss
|
|
51
|
+
JSR copydata
|
|
52
|
+
JSR initlib
|
|
53
|
+
|
|
54
|
+
JSR _sdk_init ; init_graphics + ACP (the bundled runtime)
|
|
55
|
+
CLI ; enable IRQs — the draw queue + ACP are
|
|
56
|
+
; INTERRUPT-DRIVEN (blit-done IRQ processes
|
|
57
|
+
; queue_draw_box; without CLI nothing draws)
|
|
58
|
+
JSR _main
|
|
59
|
+
|
|
60
|
+
_exit: JSR donelib
|
|
61
|
+
BRK
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
; GameTank SDK-runtime vector table at $FFFA. The interrupt handlers come from the
|
|
2
|
+
; bundled SDK runtime (interrupt.s exports _nmi_int / _irq_int), so this just
|
|
3
|
+
; imports + points the table at them. (The bare single-bank.vectors.s defines its
|
|
4
|
+
; own rti handlers; the SDK preset uses the SDK's real ones.)
|
|
5
|
+
.import _init, _nmi_int, _irq_int
|
|
6
|
+
|
|
7
|
+
.segment "VECTORS"
|
|
8
|
+
|
|
9
|
+
.addr _nmi_int ; $FFFA NMI
|
|
10
|
+
.addr _init ; $FFFC RESET
|
|
11
|
+
.addr _irq_int ; $FFFE IRQ / BRK
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# GameTank single-bank (EEPROM32K) ld65 config — the Tier-A "bare" path.
|
|
2
|
+
# Collapses the SDK's multi-bank 2 MB linkerConfig.js to ONE 32 KB ROM bank at
|
|
3
|
+
# $8000-$FFFF (no flash SPI, no Node bank-cat, no zopfli). ZP/RAM match the SDK
|
|
4
|
+
# (ZP $0, stack $0100, work RAM $0200-$1EFF). VECTORS at $FFFA. Output is a flat
|
|
5
|
+
# 32 KB image that IS a valid .gtr (the core size-detects the EEPROM32K mapper).
|
|
6
|
+
MEMORY {
|
|
7
|
+
ZP: start = $0000, size = $0100, type = rw, define = yes;
|
|
8
|
+
RAM: start = $0200, size = $1D00, define = yes;
|
|
9
|
+
ROM: start = $8000, size = $8000, file = %O, fill = yes, define = yes;
|
|
10
|
+
}
|
|
11
|
+
SEGMENTS {
|
|
12
|
+
ZEROPAGE: load = ZP, type = zp, define = yes;
|
|
13
|
+
DATA: load = ROM, type = rw, define = yes, run = RAM;
|
|
14
|
+
BSS: load = RAM, type = bss, define = yes;
|
|
15
|
+
HEAP: load = RAM, type = bss, optional = yes;
|
|
16
|
+
STARTUP: load = ROM, type = ro;
|
|
17
|
+
ONCE: load = ROM, type = ro, optional = yes;
|
|
18
|
+
CODE: load = ROM, type = ro;
|
|
19
|
+
RODATA: load = ROM, type = ro;
|
|
20
|
+
VECTORS: load = ROM, type = ro, start = $FFFA;
|
|
21
|
+
}
|
|
22
|
+
FEATURES {
|
|
23
|
+
CONDES: type = constructor, label = __CONSTRUCTOR_TABLE__, count = __CONSTRUCTOR_COUNT__, segment = ONCE;
|
|
24
|
+
CONDES: type = destructor, label = __DESTRUCTOR_TABLE__, count = __DESTRUCTOR_COUNT__, segment = RODATA;
|
|
25
|
+
}
|
|
26
|
+
SYMBOLS {
|
|
27
|
+
__STACKSIZE__: type = weak, value = $0200;
|
|
28
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
; ---------------------------------------------------------------------------
|
|
2
|
+
; GameTank Tier-A crt0 (single-bank EEPROM32K, no SDK runtime).
|
|
3
|
+
; Adapted from gametank_sdk src/gt/crt0.s, MIT (Clyde Shaffer). STRIPPED for the
|
|
4
|
+
; bare 32 KB path: NO flash bank shift-out (the whole game is in one bank that
|
|
5
|
+
; maps at boot), NO _sdk_init, NO _AudioFWPkg .incbin. Just: stack init, VIA
|
|
6
|
+
; wakeup, zerobss/copydata/constructors, set the cc65 C arg-stack pointer, call
|
|
7
|
+
; main(). A default NMI/IRQ handler (rti) lives here so a bare game links without
|
|
8
|
+
; defining its own.
|
|
9
|
+
; ---------------------------------------------------------------------------
|
|
10
|
+
.export _init, _exit
|
|
11
|
+
.export _nmi_int, _irq_int
|
|
12
|
+
.import _main
|
|
13
|
+
.export __STARTUP__ : absolute = 1
|
|
14
|
+
.import __RAM_START__, __RAM_SIZE__
|
|
15
|
+
.import copydata, zerobss, initlib, donelib
|
|
16
|
+
|
|
17
|
+
.PC02 ; W65C02 opcode set (stz/bra/phx/…)
|
|
18
|
+
|
|
19
|
+
BankReg = $2005
|
|
20
|
+
VIA = $2800
|
|
21
|
+
DDRA = 3
|
|
22
|
+
ORAr = 1
|
|
23
|
+
|
|
24
|
+
.include "zeropage.inc"
|
|
25
|
+
|
|
26
|
+
.segment "STARTUP"
|
|
27
|
+
|
|
28
|
+
_init: LDX #$FF ; init stack pointer to $01FF
|
|
29
|
+
TXS
|
|
30
|
+
CLD
|
|
31
|
+
|
|
32
|
+
LDX #0 ; brief VIA wakeup delay
|
|
33
|
+
viaWakeup:
|
|
34
|
+
INX
|
|
35
|
+
BNE viaWakeup
|
|
36
|
+
|
|
37
|
+
; Park the banking register at a known state. With a single 32 KB cart
|
|
38
|
+
; the active bank is fixed at boot, so we don't shift a flash bank in.
|
|
39
|
+
STZ BankReg
|
|
40
|
+
STZ $1FFF
|
|
41
|
+
|
|
42
|
+
LDA #%00000111 ; VIA DDRA: low 3 bits output (banking pins)
|
|
43
|
+
STA VIA+DDRA
|
|
44
|
+
LDA #$FF
|
|
45
|
+
STA VIA+ORAr
|
|
46
|
+
|
|
47
|
+
; ---------------------------------------------------------------------------
|
|
48
|
+
; cc65 C argument-stack pointer = top of work RAM
|
|
49
|
+
LDA #<(__RAM_START__ + __RAM_SIZE__)
|
|
50
|
+
STA c_sp
|
|
51
|
+
LDA #>(__RAM_START__ + __RAM_SIZE__)
|
|
52
|
+
STA c_sp+1
|
|
53
|
+
|
|
54
|
+
; ---------------------------------------------------------------------------
|
|
55
|
+
JSR zerobss ; clear BSS
|
|
56
|
+
JSR copydata ; copy initialized DATA to RAM
|
|
57
|
+
JSR initlib ; run constructors
|
|
58
|
+
|
|
59
|
+
JSR _main
|
|
60
|
+
|
|
61
|
+
_exit: JSR donelib ; run destructors
|
|
62
|
+
BRK
|
|
63
|
+
|
|
64
|
+
; ---------------------------------------------------------------------------
|
|
65
|
+
; Default interrupt handlers — a bare game that doesn't define its own still
|
|
66
|
+
; links. (A game CAN provide _nmi_int/_irq_int; cc65's linker lets a strong
|
|
67
|
+
; symbol from the game override these .export'd ones... so keep them weak by
|
|
68
|
+
; only defining if absent — here they're plain rti fallbacks.)
|
|
69
|
+
_nmi_int:
|
|
70
|
+
_irq_int:
|
|
71
|
+
RTI
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
; ---------------------------------------------------------------------------
|
|
2
|
+
; GameTank 6502 vector table at $FFFA (NMI / RESET / IRQ-BRK).
|
|
3
|
+
; Adapted from gametank_sdk src/gt/vectors.s, MIT. The handlers are exported by
|
|
4
|
+
; the Tier-A crt0 (rti defaults) — a game can override _nmi_int/_irq_int.
|
|
5
|
+
; ---------------------------------------------------------------------------
|
|
6
|
+
.import _init, _nmi_int, _irq_int
|
|
7
|
+
|
|
8
|
+
.segment "VECTORS"
|
|
9
|
+
|
|
10
|
+
.addr _nmi_int ; $FFFA NMI
|
|
11
|
+
.addr _init ; $FFFC RESET
|
|
12
|
+
.addr _irq_int ; $FFFE IRQ / BRK
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Shared C-SDK build-pipeline helper (0.81.0). The gba-c / genesis-c / mips-c / sh-c
|
|
2
|
+
// builders all walk the SAME spine — compile each .c (cc1 → as), assemble raw .s,
|
|
3
|
+
// assemble a crt0, link, optionally objcopy, finalize — and at EVERY stage repeat the
|
|
4
|
+
// identical 5-part dance:
|
|
5
|
+
//
|
|
6
|
+
// const r = await runX(...);
|
|
7
|
+
// log += `--- stage ---\n${r.log || "(ok)"}\n`;
|
|
8
|
+
// if (r.exitCode !== 0 || !r.<output>)
|
|
9
|
+
// return { ok: false, binary: null, log, exitCode: r.exitCode || 1, stage, ...(r.crash ? { crash } : {}) };
|
|
10
|
+
//
|
|
11
|
+
// That `if (...) return { ok:false, ... }` line appeared ~73 times across the 5 builders.
|
|
12
|
+
// CBuild owns it: each builder makes one `new CBuild()`, calls `.stage(name, () => runX(...), pick)`,
|
|
13
|
+
// and the helper accumulates the log + THROWS a BuildError on any failure. The builder
|
|
14
|
+
// wraps its body in try/catch and turns a BuildError into the same `{ ok:false, ... }` shape
|
|
15
|
+
// it returned before — so the public contract is byte-for-byte unchanged, the boilerplate
|
|
16
|
+
// is gone, and each builder keeps only its genuinely-per-platform wiring (which crt0, which
|
|
17
|
+
// libs, finalize). No behavior change.
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Thrown by CBuild.stage() when a stage fails. Carries everything the builder's
|
|
21
|
+
* error return needs. The builder catches it and maps to its result object.
|
|
22
|
+
*/
|
|
23
|
+
export class BuildError extends Error {
|
|
24
|
+
/** @param {{stage:string, exitCode:number, log:string, crash?:any}} info */
|
|
25
|
+
constructor(info) {
|
|
26
|
+
super(`build failed at stage: ${info.stage}`);
|
|
27
|
+
this.name = "BuildError";
|
|
28
|
+
this.stage = info.stage;
|
|
29
|
+
this.exitCode = info.exitCode;
|
|
30
|
+
this.log = info.log;
|
|
31
|
+
if (info.crash) this.crash = info.crash;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The canonical failure result the GCC/tcc C-SDK builders return (includes
|
|
35
|
+
* `ok: false`). Pass extra fields (e.g. a platform-specific `{ runtime }`) to merge.
|
|
36
|
+
* @param {Record<string, any>} [extra]
|
|
37
|
+
*/
|
|
38
|
+
toResult(extra = {}) {
|
|
39
|
+
return { ok: false, ...this.fields(extra) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The failure fields WITHOUT a forced `ok` — for builders (cc65/sdcc) whose
|
|
44
|
+
* lower layer returns `{ binary, log, exitCode, stage }` and lets the caller
|
|
45
|
+
* (index.js) add `ok`. Same { binary:null, log, exitCode, stage, crash? } core
|
|
46
|
+
* + any `extra` (e.g. sdcc's `failedTU` / `compiledOK`). Note: crash is only
|
|
47
|
+
* spread when present (cc65/sdcc historically never carried it — pass nothing).
|
|
48
|
+
* @param {Record<string, any>} [extra]
|
|
49
|
+
*/
|
|
50
|
+
fields(extra = {}) {
|
|
51
|
+
return {
|
|
52
|
+
binary: null,
|
|
53
|
+
log: this.log,
|
|
54
|
+
exitCode: this.exitCode,
|
|
55
|
+
stage: this.stage,
|
|
56
|
+
...(this.crash ? { crash: this.crash } : {}),
|
|
57
|
+
...extra,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A build-log accumulator + stage runner. One per build.
|
|
64
|
+
*
|
|
65
|
+
* Usage:
|
|
66
|
+
* const cb = new CBuild();
|
|
67
|
+
* const cc = await cb.stage(`cc1 (${name})`, () => runCc1x({...}), r => r.asmSource);
|
|
68
|
+
* // cc is the full run result; cb.log has the accumulated log.
|
|
69
|
+
* // a failure THROWS BuildError — wrap the build body in try/catch.
|
|
70
|
+
*/
|
|
71
|
+
export class CBuild {
|
|
72
|
+
constructor() {
|
|
73
|
+
/** @type {string} accumulated build log */
|
|
74
|
+
this.log = "";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Run one toolchain stage, append its log, and throw BuildError on failure.
|
|
79
|
+
*
|
|
80
|
+
* @template T
|
|
81
|
+
* @param {string} name stage label — the failure `stage` field AND, by default,
|
|
82
|
+
* the log header. Most builders use one string for both.
|
|
83
|
+
* @param {() => Promise<T>} run the toolchain call (returns {log, exitCode, crash?, ...output})
|
|
84
|
+
* @param {(r: T) => any} pick selects the stage's required output (e.g. r => r.object);
|
|
85
|
+
* a falsy result is treated as failure even if exitCode is 0
|
|
86
|
+
* @param {{logName?:string}} [opts] `logName` overrides ONLY the log header when a
|
|
87
|
+
* builder historically logged a label that differs from the
|
|
88
|
+
* failure `stage` string (e.g. log "as (x → .o)" / stage "as (x)").
|
|
89
|
+
* @returns {Promise<T>} the full run result (on success)
|
|
90
|
+
*/
|
|
91
|
+
async stage(name, run, pick, opts = {}) {
|
|
92
|
+
const r = await run();
|
|
93
|
+
this.log += `--- ${opts.logName ?? name} ---\n${r.log || "(ok)"}\n`;
|
|
94
|
+
if (r.exitCode !== 0 || !pick(r)) {
|
|
95
|
+
throw new BuildError({
|
|
96
|
+
stage: name,
|
|
97
|
+
exitCode: r.exitCode || 1,
|
|
98
|
+
log: this.log,
|
|
99
|
+
crash: r.crash,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return r;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Append a freeform note to the log (e.g. a sub-step that isn't a toolchain stage). */
|
|
106
|
+
note(text) {
|
|
107
|
+
this.log += text.endsWith("\n") ? text : text + "\n";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Shared GCC-family toolchain factory (0.81.0). The arm/m68k/mips/sh "gcc" wrappers
|
|
2
|
+
// were ~73% identical: each exported runCc1X / runXAs / runXLd / runXObjcopy that did
|
|
3
|
+
// the same input-marshalling + runIsolated() + output-decoding, differing only in:
|
|
4
|
+
// - the npm package the glue ships in + the glue filenames
|
|
5
|
+
// - the per-stage arch flags (cc1 / as / ld), which for MIPS depend on endian
|
|
6
|
+
// - the linker-script filename + the objcopy output filename
|
|
7
|
+
// makeGccToolchain(config) returns the 4 run functions, so each arch shrinks to a
|
|
8
|
+
// small config object. Glue resolution is lazy+memoized (booting never loads a
|
|
9
|
+
// toolchain package until something is actually built with it).
|
|
10
|
+
//
|
|
11
|
+
// All four stages keep the exact pre-refactor argv, /work paths, output encodings,
|
|
12
|
+
// and return shapes (including `...(r.crash ? { crash, stage:"crash" } : {})`), so
|
|
13
|
+
// this is a pure de-duplication — no behavior change.
|
|
14
|
+
import { runIsolated, getOutputBytes, getOutputText } from "../_worker/run.js";
|
|
15
|
+
import { makeGlueResolver, marshalInputs } from "./wasm-tool.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} GccArchConfig
|
|
19
|
+
* @property {string} pkg npm package the glue ships in
|
|
20
|
+
* @property {string} localDir the wrapper's __dirname (dev fallback base)
|
|
21
|
+
* @property {string} label tool label for "not found" errors
|
|
22
|
+
* @property {Object} glue glue filenames per stage
|
|
23
|
+
* @property {string} glue.cc1 e.g. "cc1-m68k.mjs"
|
|
24
|
+
* @property {string} glue.as e.g. "m68k-elf-as.mjs"
|
|
25
|
+
* @property {string} glue.ld e.g. "m68k-elf-ld.mjs"
|
|
26
|
+
* @property {string} glue.objcopy e.g. "m68k-elf-objcopy.mjs"
|
|
27
|
+
* @property {string[]|((endian:string)=>string[])} cc1Flags arch flags for cc1 (before -iquote/-I)
|
|
28
|
+
* @property {string[]|((endian:string)=>string[])} asFlags arch flags for as (before -I)
|
|
29
|
+
* @property {string[]|((endian:string)=>string[])} [ldFlags] arch flags for ld (before -T); default []
|
|
30
|
+
* @property {string} ldScriptName name the link script is mounted as (e.g. "genesis.ld")
|
|
31
|
+
* @property {string} outputName objcopy output filename (e.g. "main.bin", "main.gba")
|
|
32
|
+
* @property {string} [defaultEndian] "big"|"little" — only meaningful when flags are fns
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/** Resolve a flags entry that may be a constant array or an endian-dependent fn. */
|
|
36
|
+
function flags(entry, endian) {
|
|
37
|
+
return typeof entry === "function" ? entry(endian) : (entry ?? []);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build the 4 GCC-stage run functions for one architecture.
|
|
42
|
+
* @param {GccArchConfig} config
|
|
43
|
+
* @returns {{ runCc1:Function, runAs:Function, runLd:Function, runObjcopy:Function }}
|
|
44
|
+
*/
|
|
45
|
+
export function makeGccToolchain(config) {
|
|
46
|
+
const glue = makeGlueResolver({ pkg: config.pkg, localDir: config.localDir, label: config.label });
|
|
47
|
+
const endianOf = (args) => args.endian ?? config.defaultEndian ?? "big";
|
|
48
|
+
|
|
49
|
+
/** cc1: C source → assembly (.s). */
|
|
50
|
+
async function runCc1(args) {
|
|
51
|
+
const { source, options = [] } = args;
|
|
52
|
+
const inputFiles = marshalInputs({ primary: { name: "main.c", text: source }, text: args.headers ?? {} });
|
|
53
|
+
const argv = [
|
|
54
|
+
...flags(config.cc1Flags, endianOf(args)),
|
|
55
|
+
"-iquote", "/work",
|
|
56
|
+
"-I", "/work",
|
|
57
|
+
...options,
|
|
58
|
+
"/work/main.c",
|
|
59
|
+
"-o", "/work/main.s",
|
|
60
|
+
];
|
|
61
|
+
const r = await runIsolated({
|
|
62
|
+
gluePath: glue(config.glue.cc1),
|
|
63
|
+
argv,
|
|
64
|
+
inputFiles,
|
|
65
|
+
outputFiles: [{ vfsPath: "/work/main.s", encoding: "utf8" }],
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
log: r.log,
|
|
69
|
+
exitCode: r.exitCode,
|
|
70
|
+
asmSource: getOutputText(r, "/work/main.s") || null,
|
|
71
|
+
...(r.crash ? { crash: r.crash, stage: "crash" } : {}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** as: assembly (.s) → object (.o). */
|
|
76
|
+
async function runAs(args) {
|
|
77
|
+
const { source, options = [] } = args;
|
|
78
|
+
const inputFiles = marshalInputs({
|
|
79
|
+
primary: { name: "main.s", text: source },
|
|
80
|
+
text: args.includes ?? {},
|
|
81
|
+
binary: args.binaryIncludes ?? {},
|
|
82
|
+
});
|
|
83
|
+
const argv = [
|
|
84
|
+
...flags(config.asFlags, endianOf(args)),
|
|
85
|
+
"-I", "/work",
|
|
86
|
+
...options,
|
|
87
|
+
"/work/main.s",
|
|
88
|
+
"-o", "/work/main.o",
|
|
89
|
+
];
|
|
90
|
+
const r = await runIsolated({
|
|
91
|
+
gluePath: glue(config.glue.as),
|
|
92
|
+
argv,
|
|
93
|
+
inputFiles,
|
|
94
|
+
outputFiles: [{ vfsPath: "/work/main.o", encoding: "base64" }],
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
log: r.log,
|
|
98
|
+
exitCode: r.exitCode,
|
|
99
|
+
object: getOutputBytes(r, "/work/main.o"),
|
|
100
|
+
...(r.crash ? { crash: r.crash, stage: "crash" } : {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** ld: objects + link script → ELF (+ map). */
|
|
105
|
+
async function runLd(args) {
|
|
106
|
+
const { objects, linkScript, libraries = [], libraryPaths = [], options = [] } = args;
|
|
107
|
+
const inputFiles = marshalInputs({
|
|
108
|
+
text: { [config.ldScriptName]: linkScript },
|
|
109
|
+
binary: { ...objects, ...(args.archives ?? {}) },
|
|
110
|
+
});
|
|
111
|
+
const argv = [
|
|
112
|
+
...flags(config.ldFlags, endianOf(args)),
|
|
113
|
+
"-T", "/work/" + config.ldScriptName,
|
|
114
|
+
"-o", "/work/main.elf",
|
|
115
|
+
"-Map=/work/main.map",
|
|
116
|
+
...libraryPaths.flatMap((p) => ["-L", p]),
|
|
117
|
+
...Object.keys(objects).map((n) => "/work/" + n),
|
|
118
|
+
...libraries.map((l) => `-l${l}`),
|
|
119
|
+
...options,
|
|
120
|
+
];
|
|
121
|
+
const r = await runIsolated({
|
|
122
|
+
gluePath: glue(config.glue.ld),
|
|
123
|
+
argv,
|
|
124
|
+
inputFiles,
|
|
125
|
+
outputFiles: [
|
|
126
|
+
{ vfsPath: "/work/main.elf", encoding: "base64" },
|
|
127
|
+
{ vfsPath: "/work/main.map", encoding: "utf8" },
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
return {
|
|
131
|
+
log: r.log,
|
|
132
|
+
exitCode: r.exitCode,
|
|
133
|
+
elf: getOutputBytes(r, "/work/main.elf"),
|
|
134
|
+
map: getOutputText(r, "/work/main.map") || null,
|
|
135
|
+
...(r.crash ? { crash: r.crash, stage: "crash" } : {}),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** objcopy: ELF → raw binary. */
|
|
140
|
+
async function runObjcopy(args) {
|
|
141
|
+
const { elf, options = [] } = args;
|
|
142
|
+
const inputFiles = marshalInputs({ binary: { "main.elf": elf } });
|
|
143
|
+
const argv = [
|
|
144
|
+
"-O", "binary",
|
|
145
|
+
...options,
|
|
146
|
+
"/work/main.elf",
|
|
147
|
+
"/work/" + config.outputName,
|
|
148
|
+
];
|
|
149
|
+
const r = await runIsolated({
|
|
150
|
+
gluePath: glue(config.glue.objcopy),
|
|
151
|
+
argv,
|
|
152
|
+
inputFiles,
|
|
153
|
+
outputFiles: [{ vfsPath: "/work/" + config.outputName, encoding: "base64" }],
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
log: r.log,
|
|
157
|
+
exitCode: r.exitCode,
|
|
158
|
+
binary: getOutputBytes(r, "/work/" + config.outputName),
|
|
159
|
+
...(r.crash ? { crash: r.crash, stage: "crash" } : {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { runCc1, runAs, runLd, runObjcopy };
|
|
164
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Shared WASM-toolchain plumbing (0.81.0). Every toolchain wrapper used to
|
|
2
|
+
// re-implement the SAME two things:
|
|
3
|
+
// 1. Resolve a tool's emscripten glue from its npm package, with a local
|
|
4
|
+
// src/ fallback for dev, throwing an install hint if neither exists.
|
|
5
|
+
// 2. Marshal { name: text|bytes } maps into the runIsolated() inputFiles[]
|
|
6
|
+
// shape (textFile / binaryFile per entry).
|
|
7
|
+
// This module owns both so the per-tool wrappers carry only what's genuinely
|
|
8
|
+
// tool-specific (package name, glue filename, argv, output decoding).
|
|
9
|
+
//
|
|
10
|
+
// Resolution stays LAZY at the call site (the wrappers memoize) so booting the
|
|
11
|
+
// server never touches a 100MB+ toolchain package unless that toolchain is
|
|
12
|
+
// actually used to build something.
|
|
13
|
+
import { existsSync } from "node:fs";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { textFile, binaryFile } from "../_worker/run.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a single tool glue FILE from its package, falling back to a local
|
|
20
|
+
* in-tree copy. This is the arm/m68k/mips/sh-gcc + asar + wladx + rgbds + dasm +
|
|
21
|
+
* vasm68k + sjasm + tcc816 shape (one .js/.mjs glue per tool).
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} a
|
|
24
|
+
* @param {string} a.pkg npm package the glue ships in (e.g. "romdev-platform-gba")
|
|
25
|
+
* @param {string} a.file glue filename inside the package's wasm/ dir (e.g. "cc1-arm.mjs")
|
|
26
|
+
* @param {string} a.localDir the wrapper's __dirname (its wasm/ holds the dev fallback)
|
|
27
|
+
* @param {string} [a.label] tool name for the "not found" error (defaults to file)
|
|
28
|
+
* @returns {string} absolute path to the glue file
|
|
29
|
+
*/
|
|
30
|
+
export function resolveGlueFile({ pkg, file, localDir, label }) {
|
|
31
|
+
try {
|
|
32
|
+
const u = import.meta.resolve(pkg);
|
|
33
|
+
const p = path.join(path.dirname(fileURLToPath(u)), "wasm", file);
|
|
34
|
+
if (existsSync(p)) return p;
|
|
35
|
+
} catch { /* package not resolvable — fall through to local */ }
|
|
36
|
+
const local = path.join(localDir, "wasm", file);
|
|
37
|
+
if (existsSync(local)) return local;
|
|
38
|
+
throw new Error(`${label ?? file} WASM not found — install ${pkg}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a tool's BASE directory (the caller derives wasm/ + share/ from it).
|
|
43
|
+
* This is the cc65 + sdcc shape, where one package holds several tools + shared
|
|
44
|
+
* data and the wrapper wants the root, validated by a sentinel file's presence.
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} a
|
|
47
|
+
* @param {string} a.pkg npm package (e.g. "romdev-toolchain-cc65")
|
|
48
|
+
* @param {string} a.sentinel a path under the base that must exist (e.g. "wasm/cc65.js")
|
|
49
|
+
* @param {string} a.localDir the wrapper's __dirname (the dev fallback base)
|
|
50
|
+
* @param {string} [a.label] tool name for the "not found" error
|
|
51
|
+
* @returns {string} absolute path to the base dir
|
|
52
|
+
*/
|
|
53
|
+
export function resolveToolBaseDir({ pkg, sentinel, localDir, label }) {
|
|
54
|
+
try {
|
|
55
|
+
const u = import.meta.resolve(pkg);
|
|
56
|
+
const dir = path.dirname(fileURLToPath(u));
|
|
57
|
+
if (existsSync(path.join(dir, sentinel))) return dir;
|
|
58
|
+
} catch { /* package not resolvable — fall through to local */ }
|
|
59
|
+
if (existsSync(path.join(localDir, sentinel))) return localDir;
|
|
60
|
+
throw new Error(`${label ?? pkg} WASM not found — install ${pkg}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A lazy, memoized glue resolver bound to a package + local dir. Returns a
|
|
65
|
+
* function `glue(file)` that resolves+caches each glue path on first use.
|
|
66
|
+
* Replaces the `const _glue = {}; const xGlue = (f) => (_glue[f] ??= ...)`
|
|
67
|
+
* boilerplate every multi-tool wrapper repeats.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} a
|
|
70
|
+
* @param {string} a.pkg
|
|
71
|
+
* @param {string} a.localDir
|
|
72
|
+
* @param {string} [a.label]
|
|
73
|
+
* @returns {(file: string) => string}
|
|
74
|
+
*/
|
|
75
|
+
export function makeGlueResolver({ pkg, localDir, label }) {
|
|
76
|
+
const cache = {};
|
|
77
|
+
return (file) =>
|
|
78
|
+
(cache[file] ??= resolveGlueFile({ pkg, file, localDir, label }));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Marshal text + binary virtual-file maps into the runIsolated() inputFiles[]
|
|
83
|
+
* array, all rooted at /work. Order: the primary source first (if given), then
|
|
84
|
+
* text entries, then binary entries — matching what the wrappers built by hand.
|
|
85
|
+
*
|
|
86
|
+
* @param {Object} a
|
|
87
|
+
* @param {{name: string, text: string}} [a.primary] the main source (e.g. main.c → /work/main.c)
|
|
88
|
+
* @param {Record<string,string>} [a.text] virtual text files (headers, includes, link scripts)
|
|
89
|
+
* @param {Record<string,Uint8Array>} [a.binary] virtual binary files (objects, archives, binary includes)
|
|
90
|
+
* @returns {import("../_worker/run.js").InputFile[]}
|
|
91
|
+
*/
|
|
92
|
+
export function marshalInputs({ primary, text = {}, binary = {} } = {}) {
|
|
93
|
+
/** @type {import("../_worker/run.js").InputFile[]} */
|
|
94
|
+
const files = [];
|
|
95
|
+
if (primary) files.push(textFile("/work/" + primary.name, primary.text));
|
|
96
|
+
for (const [name, content] of Object.entries(text))
|
|
97
|
+
files.push(textFile("/work/" + name, content));
|
|
98
|
+
for (const [name, bytes] of Object.entries(binary))
|
|
99
|
+
files.push(binaryFile("/work/" + name, bytes));
|
|
100
|
+
return files;
|
|
101
|
+
}
|