romdevtools 0.71.1 → 0.84.1
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 +184 -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 +24 -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/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
|
@@ -15,6 +15,9 @@ import { fileURLToPath } from "node:url";
|
|
|
15
15
|
import { existsSync } from "node:fs";
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
|
|
18
|
+
import { resolveToolBaseDir } from "../common/wasm-tool.js";
|
|
19
|
+
import { CBuild, BuildError } from "../common/c-build.js";
|
|
20
|
+
|
|
18
21
|
const __filename = fileURLToPath(import.meta.url);
|
|
19
22
|
const __dirname = path.dirname(__filename);
|
|
20
23
|
|
|
@@ -23,21 +26,18 @@ const __dirname = path.dirname(__filename);
|
|
|
23
26
|
// needs to link a target, so they must come from the same place as the WASM.
|
|
24
27
|
// Resolve the package's base dir once; fall back to a local copy under src/
|
|
25
28
|
// if present (transition / dev). The package is a hard dep of romdev.
|
|
26
|
-
function resolveCc65BaseDir() {
|
|
27
|
-
try {
|
|
28
|
-
const u = import.meta.resolve("romdev-toolchain-cc65");
|
|
29
|
-
const dir = path.dirname(fileURLToPath(u));
|
|
30
|
-
if (existsSync(path.join(dir, "wasm", "cc65.js"))) return dir;
|
|
31
|
-
} catch { /* package not resolvable — fall through to local */ }
|
|
32
|
-
if (existsSync(path.join(__dirname, "wasm", "cc65.js"))) return __dirname;
|
|
33
|
-
throw new Error("cc65 WASM not found — install romdev-toolchain-cc65");
|
|
34
|
-
}
|
|
35
29
|
// Lazy + memoized: resolve (and possibly throw "not installed") only on the
|
|
36
30
|
// first cc65 build (NES/C64/Atari7800/Lynx), not at module load — so booting
|
|
37
31
|
// the server never touches this package unless cc65 is actually used. Resolve
|
|
38
32
|
// the base dir once; derive the wasm + share dirs from it on demand.
|
|
39
33
|
let _cc65Base;
|
|
40
|
-
const cc65Base = () =>
|
|
34
|
+
const cc65Base = () =>
|
|
35
|
+
(_cc65Base ??= resolveToolBaseDir({
|
|
36
|
+
pkg: "romdev-toolchain-cc65",
|
|
37
|
+
sentinel: "wasm/cc65.js",
|
|
38
|
+
localDir: __dirname,
|
|
39
|
+
label: "cc65",
|
|
40
|
+
}));
|
|
41
41
|
|
|
42
42
|
/** True if the cc65 build toolchain WASM (cc65 + ld65, in romdev-toolchain-cc65)
|
|
43
43
|
* is installed/resolvable, without throwing — for the catalog(status) capability
|
|
@@ -45,7 +45,7 @@ const cc65Base = () => (_cc65Base ??= resolveCc65BaseDir());
|
|
|
45
45
|
* reflects ld65 (the linker) availability. */
|
|
46
46
|
export function cc65Available() {
|
|
47
47
|
try {
|
|
48
|
-
const dir =
|
|
48
|
+
const dir = cc65Base();
|
|
49
49
|
return existsSync(path.join(dir, "wasm", "cc65.js")) && existsSync(path.join(dir, "wasm", "ld65.js"));
|
|
50
50
|
} catch { return false; }
|
|
51
51
|
}
|
|
@@ -275,63 +275,57 @@ export async function buildC(args) {
|
|
|
275
275
|
// and these are pure upside. (NOTE: cc65 errors on an unknown -W name, so this
|
|
276
276
|
// list is verified valid against the bundled cc65.)
|
|
277
277
|
const ccWarn = ["-W", "unused-var,unused-func,unused-label,const-comparison,struct-param,pointer-sign"];
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
// ccOptions/caOptions = caller-supplied extra flags (e.g. GameTank's `--cpu 65c02`
|
|
279
|
+
// for the `-t none` path where the CPU isn't implied by a built-in cc65 target).
|
|
280
|
+
const ccOpts = [...(args.debug ? ["-g"] : []), ...ccWarn, ...(args.ccOptions ?? [])];
|
|
281
|
+
const caOpts = [...(args.debug ? ["-g"] : []), ...(args.caOptions ?? [])];
|
|
280
282
|
const sources = normalizeSources(args, "main.c");
|
|
281
283
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (cc.exitCode !== 0 || !cc.asmSource) {
|
|
301
|
-
return { binary: null, log, exitCode: cc.exitCode || 1, stage: "cc65" };
|
|
284
|
+
const cb = new CBuild();
|
|
285
|
+
try {
|
|
286
|
+
/** @type {Record<string, Uint8Array>} */
|
|
287
|
+
const objects = {};
|
|
288
|
+
for (const [name, src] of Object.entries(sources)) {
|
|
289
|
+
const ext = path.extname(name).toLowerCase();
|
|
290
|
+
let asmSource;
|
|
291
|
+
let asmName;
|
|
292
|
+
if (ext === ".s" || ext === ".asm") {
|
|
293
|
+
asmSource = src;
|
|
294
|
+
asmName = name;
|
|
295
|
+
} else {
|
|
296
|
+
// failure stage is bare "cc65" (no name) but the log header carries the name.
|
|
297
|
+
const cc = await cb.stage("cc65",
|
|
298
|
+
() => runCc65({ source: src, headers: args.headers, target: args.target, options: ccOpts }),
|
|
299
|
+
(r) => r.asmSource, { logName: `cc65 (${name})` });
|
|
300
|
+
asmSource = cc.asmSource;
|
|
301
|
+
asmName = name.replace(/\.(c|h)$/i, ".s");
|
|
302
302
|
}
|
|
303
|
-
|
|
304
|
-
|
|
303
|
+
const ca = await cb.stage("ca65",
|
|
304
|
+
() => runCa65({ source: asmSource, includes: args.asmIncludes, binaryIncludes: args.binaryIncludes, target: args.target, options: caOpts }),
|
|
305
|
+
(r) => r.object, { logName: `ca65 (${asmName})` });
|
|
306
|
+
objects[asmName.replace(/\.s$/, ".o")] = ca.object;
|
|
305
307
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
// ld65 is NOT throw-on-fail here: it returns its own success/failure inline with
|
|
309
|
+
// dbg + ramUsage, so keep it as a plain call (the cc65 contract has no `ok` field).
|
|
310
|
+
const ld = await runLd65({
|
|
311
|
+
objects,
|
|
310
312
|
target: args.target,
|
|
311
|
-
|
|
313
|
+
debug: args.debug,
|
|
314
|
+
linkerConfig: args.linkerConfig,
|
|
312
315
|
});
|
|
313
|
-
log +=
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
316
|
+
cb.log += "--- ld65 ---\n" + ld.log; // exact: no trailing newline (matches pre-refactor)
|
|
317
|
+
return {
|
|
318
|
+
binary: ld.binary,
|
|
319
|
+
dbg: ld.dbg,
|
|
320
|
+
log: cb.log,
|
|
321
|
+
exitCode: ld.exitCode,
|
|
322
|
+
ramUsage: parseRamUsage(ld.map),
|
|
323
|
+
stage: ld.exitCode === 0 ? "done" : "ld65",
|
|
324
|
+
};
|
|
325
|
+
} catch (e) {
|
|
326
|
+
if (e instanceof BuildError) return e.fields(); // cc65 shape: no `ok` field
|
|
327
|
+
throw e;
|
|
319
328
|
}
|
|
320
|
-
const ld = await runLd65({
|
|
321
|
-
objects,
|
|
322
|
-
target: args.target,
|
|
323
|
-
debug: args.debug,
|
|
324
|
-
linkerConfig: args.linkerConfig,
|
|
325
|
-
});
|
|
326
|
-
log += "--- ld65 ---\n" + ld.log;
|
|
327
|
-
return {
|
|
328
|
-
binary: ld.binary,
|
|
329
|
-
dbg: ld.dbg,
|
|
330
|
-
log,
|
|
331
|
-
exitCode: ld.exitCode,
|
|
332
|
-
ramUsage: parseRamUsage(ld.map),
|
|
333
|
-
stage: ld.exitCode === 0 ? "done" : "ld65",
|
|
334
|
-
};
|
|
335
329
|
}
|
|
336
330
|
|
|
337
331
|
/**
|
|
@@ -351,42 +345,38 @@ export async function buildC(args) {
|
|
|
351
345
|
* @param {string} [args.linkerConfig] custom ld65 .cfg
|
|
352
346
|
*/
|
|
353
347
|
export async function buildAsm(args) {
|
|
354
|
-
const caOpts = args.debug ? ["-g"] : [];
|
|
348
|
+
const caOpts = [...(args.debug ? ["-g"] : []), ...(args.caOptions ?? [])];
|
|
355
349
|
const sources = normalizeSources(args, "main.s");
|
|
356
350
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
351
|
+
const cb = new CBuild();
|
|
352
|
+
try {
|
|
353
|
+
/** @type {Record<string, Uint8Array>} */
|
|
354
|
+
const objects = {};
|
|
355
|
+
for (const [name, src] of Object.entries(sources)) {
|
|
356
|
+
const ca = await cb.stage("ca65",
|
|
357
|
+
() => runCa65({ source: src, includes: args.includes, binaryIncludes: args.binaryIncludes, target: args.target, options: caOpts }),
|
|
358
|
+
(r) => r.object, { logName: `ca65 (${name})` });
|
|
359
|
+
objects[name.replace(/\.(s|asm)$/i, ".o")] = ca.object;
|
|
360
|
+
}
|
|
361
|
+
const ld = await runLd65({
|
|
362
|
+
objects,
|
|
365
363
|
target: args.target,
|
|
366
|
-
|
|
364
|
+
debug: args.debug,
|
|
365
|
+
linkerConfig: args.linkerConfig,
|
|
367
366
|
});
|
|
368
|
-
log +=
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
367
|
+
cb.log += "--- ld65 ---\n" + ld.log;
|
|
368
|
+
return {
|
|
369
|
+
binary: ld.binary,
|
|
370
|
+
dbg: ld.dbg,
|
|
371
|
+
log: cb.log,
|
|
372
|
+
exitCode: ld.exitCode,
|
|
373
|
+
ramUsage: parseRamUsage(ld.map),
|
|
374
|
+
stage: ld.exitCode === 0 ? "done" : "ld65",
|
|
375
|
+
};
|
|
376
|
+
} catch (e) {
|
|
377
|
+
if (e instanceof BuildError) return e.fields(); // cc65 shape: no `ok` field
|
|
378
|
+
throw e;
|
|
374
379
|
}
|
|
375
|
-
const ld = await runLd65({
|
|
376
|
-
objects,
|
|
377
|
-
target: args.target,
|
|
378
|
-
debug: args.debug,
|
|
379
|
-
linkerConfig: args.linkerConfig,
|
|
380
|
-
});
|
|
381
|
-
log += "--- ld65 ---\n" + ld.log;
|
|
382
|
-
return {
|
|
383
|
-
binary: ld.binary,
|
|
384
|
-
dbg: ld.dbg,
|
|
385
|
-
log,
|
|
386
|
-
exitCode: ld.exitCode,
|
|
387
|
-
ramUsage: parseRamUsage(ld.map),
|
|
388
|
-
stage: ld.exitCode === 0 ? "done" : "ld65",
|
|
389
|
-
};
|
|
390
380
|
}
|
|
391
381
|
|
|
392
382
|
/**
|
|
@@ -3,36 +3,31 @@
|
|
|
3
3
|
// Run with --cpu 6502 (default). For SNES/65816 use --cpu 65816.
|
|
4
4
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { existsSync } from "node:fs";
|
|
7
6
|
import path from "node:path";
|
|
8
7
|
|
|
9
8
|
import { runIsolated, textFile, binaryFile } from "../_worker/run.js";
|
|
9
|
+
import { resolveGlueFile } from "../common/wasm-tool.js";
|
|
10
10
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = path.dirname(__filename);
|
|
13
13
|
|
|
14
|
-
// da65's WASM ships in romdev-toolchain-cc65 (alongside cc65 / ca65 / ld65)
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
function resolveDa65Glue() {
|
|
18
|
-
try {
|
|
19
|
-
const u = import.meta.resolve("romdev-toolchain-cc65");
|
|
20
|
-
const p = path.join(path.dirname(fileURLToPath(u)), "wasm", "da65.js");
|
|
21
|
-
if (existsSync(p)) return p;
|
|
22
|
-
} catch { /* package not resolvable — fall through to local */ }
|
|
23
|
-
const local = path.join(__dirname, "wasm", "da65.js");
|
|
24
|
-
if (existsSync(local)) return local;
|
|
25
|
-
throw new Error("da65 WASM not found — install romdev-toolchain-cc65");
|
|
26
|
-
}
|
|
27
|
-
// Lazy + memoized: resolve only on the first da65 disassembly, not at boot.
|
|
14
|
+
// da65's WASM ships in romdev-toolchain-cc65 (alongside cc65 / ca65 / ld65); a
|
|
15
|
+
// local src/ copy is the dev fallback. Lazy + memoized: resolve only on the
|
|
16
|
+
// first da65 disassembly, not at boot.
|
|
28
17
|
let _glue;
|
|
29
|
-
const glue = () =>
|
|
18
|
+
const glue = () =>
|
|
19
|
+
(_glue ??= resolveGlueFile({
|
|
20
|
+
pkg: "romdev-toolchain-cc65",
|
|
21
|
+
file: "da65.js",
|
|
22
|
+
localDir: __dirname,
|
|
23
|
+
label: "da65",
|
|
24
|
+
}));
|
|
30
25
|
|
|
31
26
|
/** True if the da65 WASM (romdev-toolchain-cc65) is installed/resolvable, without
|
|
32
27
|
* throwing — for capability probes (catalog status) that must not crash when the
|
|
33
28
|
* toolchain is absent. */
|
|
34
29
|
export function da65Available() {
|
|
35
|
-
try { return !!
|
|
30
|
+
try { return !!glue(); } catch { return false; }
|
|
36
31
|
}
|
|
37
32
|
|
|
38
33
|
/**
|
|
@@ -40,12 +40,23 @@ export async function resolveLinkerConfig(platform, arg) {
|
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
const supportSources = {};
|
|
43
|
+
// Bundled headers a preset makes available to the C compile (e.g. GameTank's
|
|
44
|
+
// `gametank.h` register defs) — keyed by their REAL name so `#include "gametank.h"`
|
|
45
|
+
// resolves, NOT renamed like the `.s` support sources. A bundled header is named
|
|
46
|
+
// `<name>.h` (no `<preset>.` prefix needed); the `.s`/`.asm` files follow the
|
|
47
|
+
// support-source convention.
|
|
48
|
+
/** @type {Record<string,string>} */
|
|
49
|
+
const headers = {};
|
|
43
50
|
try {
|
|
44
51
|
const entries = await readdir(presetDir);
|
|
45
52
|
const prefix = `${preset}.`;
|
|
46
53
|
for (const f of entries) {
|
|
47
|
-
if (!f.startsWith(prefix) || f === `${preset}.cfg`) continue;
|
|
48
54
|
const ext = path.extname(f).toLowerCase();
|
|
55
|
+
if (ext === ".h") {
|
|
56
|
+
headers[f] = await readFile(path.join(presetDir, f), "utf-8");
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!f.startsWith(prefix) || f === `${preset}.cfg`) continue;
|
|
49
60
|
if (ext === ".s" || ext === ".asm") {
|
|
50
61
|
const contents = await readFile(path.join(presetDir, f), "utf-8");
|
|
51
62
|
const suffix = f.slice(prefix.length);
|
|
@@ -55,5 +66,5 @@ export async function resolveLinkerConfig(platform, arg) {
|
|
|
55
66
|
} catch {
|
|
56
67
|
// No support files.
|
|
57
68
|
}
|
|
58
|
-
return { cfg, supportSources };
|
|
69
|
+
return { cfg, supportSources, headers };
|
|
59
70
|
}
|
|
@@ -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
|