@henols/vice-mcp 0.2.1 → 0.2.3

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.
Files changed (108) hide show
  1. package/README.md +4 -3
  2. package/THIRD-PARTY-NOTICES.md +423 -25
  3. package/{r2000-acme-ident.ts → anno-acme-ident.ts} +13 -13
  4. package/anno-bank.ts +171 -0
  5. package/anno-cli.ts +3040 -0
  6. package/{r2000-confidence.ts → anno-confidence.ts} +22 -22
  7. package/anno-coverage.ts +2465 -0
  8. package/anno-derive.ts +590 -0
  9. package/anno-details.ts +169 -0
  10. package/anno-enum-gen.ts +919 -0
  11. package/anno-export-asm.ts +2396 -0
  12. package/anno-graphics.ts +338 -0
  13. package/anno-hazard-report.ts +1367 -0
  14. package/anno-import.ts +495 -0
  15. package/anno-index.ts +150 -0
  16. package/anno-join.ts +480 -0
  17. package/{r2000-memmap-render.ts → anno-memmap-render.ts} +236 -95
  18. package/anno-provenance-ledger.ts +472 -0
  19. package/{r2000-regbits-gen.ts → anno-regbits-gen.ts} +20 -15
  20. package/{r2000-regbits.json → anno-regbits.json} +2 -2
  21. package/anno-register.ts +399 -0
  22. package/anno-store-export.ts +661 -0
  23. package/anno-store.ts +4002 -0
  24. package/anno-symbols.ts +266 -0
  25. package/anno-tools.ts +3264 -0
  26. package/anno-types.ts +1909 -0
  27. package/backend-detect.mts +124 -312
  28. package/block-class.ts +201 -0
  29. package/build.ts +4 -2
  30. package/capture-predicate.ts +597 -0
  31. package/channel-lock.ts +349 -0
  32. package/disasm-decoder.ts +14 -14
  33. package/disasm-opcodes.ts +4 -4
  34. package/disasm-renderer.ts +2 -2
  35. package/evid-ingest.ts +217 -0
  36. package/evid-reconcile.ts +316 -0
  37. package/host-tool-client.ts +430 -0
  38. package/hostpath.ts +1 -1
  39. package/incident-record.ts +23 -12
  40. package/install-resources.ts +30 -14
  41. package/memmap-lookup.ts +285 -0
  42. package/package.json +48 -23
  43. package/prg-image.ts +118 -0
  44. package/repo-root.ts +107 -8
  45. package/resources/backend-detect.mjs +98 -236
  46. package/resources/broker-control.mjs +189 -16
  47. package/resources/broker-epoch.mjs +1 -1
  48. package/resources/broker-kill.mjs +8 -2
  49. package/resources/broker-launch.mjs +373 -214
  50. package/resources/broker-state.mjs +64 -18
  51. package/resources/container-guard.mjs +1 -1
  52. package/resources/ghidra-project.mjs +790 -0
  53. package/resources/host-tool.mjs +2561 -0
  54. package/resources/vice-broker.mjs +330 -184
  55. package/resources/vice-launcher.sh +130 -12
  56. package/stock-address.ts +6 -6
  57. package/stock-cia.ts +2 -2
  58. package/stock-condition.ts +8 -8
  59. package/stock-connect.ts +10 -6
  60. package/stock-derived.ts +29 -37
  61. package/stock-diagnose.ts +200 -36
  62. package/stock-dispatch.ts +200 -68
  63. package/stock-execution.ts +5 -3
  64. package/stock-handler.ts +1 -1
  65. package/stock-input.ts +9 -9
  66. package/stock-machine.ts +17 -6
  67. package/stock-paths.ts +18 -14
  68. package/stock-petscii.ts +1 -1
  69. package/stock-protocol.ts +17 -12
  70. package/stock-recycle.ts +83 -2
  71. package/stock-registers.ts +54 -29
  72. package/stock-reproducible-run.ts +811 -0
  73. package/stock-run-until.ts +100 -1
  74. package/stock-sprites.ts +3 -3
  75. package/stock-symbols.ts +13 -13
  76. package/stock-timing.ts +2 -2
  77. package/stock-vicii.ts +1 -1
  78. package/stop-oracle.ts +167 -0
  79. package/text-capability-probe.ts +660 -0
  80. package/text-connect.ts +157 -0
  81. package/text-protocol.ts +810 -0
  82. package/text-tools.ts +778 -0
  83. package/textmon-backtrace.ts +385 -0
  84. package/textmon-cpuhistory.ts +335 -0
  85. package/textmon-memmap.ts +494 -0
  86. package/textmon-profile.ts +458 -0
  87. package/textmon-registers.ts +748 -0
  88. package/tools-manifest.stock.json +864 -3
  89. package/version.ts +1 -1
  90. package/vice-broker-client.ts +189 -42
  91. package/vice-errors.ts +268 -0
  92. package/vice-proxy.ts +392 -2175
  93. package/vsf-slice.ts +640 -0
  94. package/capability-registry.ts +0 -388
  95. package/r2000-cli.ts +0 -1103
  96. package/r2000-d64.ts +0 -310
  97. package/r2000-enum-gen.ts +0 -574
  98. package/r2000-launch.ts +0 -357
  99. package/r2000-mcp-client.ts +0 -596
  100. package/r2000-project.ts +0 -190
  101. package/r2000-symbols.ts +0 -388
  102. package/r2000-tools.ts +0 -914
  103. package/r2000-verify.ts +0 -184
  104. package/refresh-manifest.ts +0 -124
  105. package/tools-manifest.json +0 -1223
  106. package/vice-probe.ts +0 -278
  107. package/vice-sync.ts +0 -336
  108. package/vice.ts +0 -772
package/anno-bank.ts ADDED
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+ // anno-bank.ts
3
+ //
4
+ // Phase 37, plan 37-06 (AUTO-04/AUTO-05): the processor-port bit decode, the
5
+ // per-range banked-region resolution, and the region-to-map-entry
6
+ // consistency table `anno-join.ts`'s candidate-constraint step calls BEFORE
7
+ // `selectMemmapEntry()` ever runs. This is the phase's highest-risk
8
+ // requirement pair -- both are recorded UNVALIDATED, not narrowed, and a
9
+ // measured survey found no prior art to copy (SVD-Loader, radare2's
10
+ // device-description import, IDA's device definitions all annotate
11
+ // unconditionally, because their domain has no path-dependent address
12
+ // meaning). Resolve bank state BEFORE the address; decline with a reason
13
+ // where the program itself does not determine one.
14
+ //
15
+ // THIS MODULE NEVER NAMES THE PERSISTENCE DEPENDENCY, NEVER OPENS THE STORE,
16
+ // and NEVER IMPORTS `hostpath.ts`/`containerpath.ts` -- the same posture
17
+ // `memmap-lookup.ts` takes. It takes plain data (a raw `$01` value, a
18
+ // `MemmapEntry`) and returns plain data.
19
+ //
20
+ // WHAT THIS IS THE ONE AUTHORITATIVE PLACE FOR: D-37-21's bit arithmetic
21
+ // (`decodeBankState`), the not-applicable-outside-the-three-ranges rule
22
+ // (`resolveBankedRegion`), the address-is-bank-conditional-at-all membership
23
+ // test (`isBankConditionalAddress`), and the region-to-map-entry consistency
24
+ // table (`regionAdmitsEntry`) `anno-join.ts`'s candidate constraint uses.
25
+ //
26
+ // The REACHING-VALUES SET computation -- which recovered processor-port
27
+ // value(s) reach a given program point, over the recovered const-write facts
28
+ // and the stored cross-reference graph (D-37-24) -- lives in `anno-join.ts`
29
+ // itself, not here. This module only ever decodes a SINGLE already-resolved
30
+ // value; it has no notion of "which value(s) reach this address" and never
31
+ // will (that is the join's own responsibility, since it alone holds the
32
+ // store handle and the cross-reference graph).
33
+ //
34
+ // WHAT NOT TO DO:
35
+ // - Never derive "is this region io/basic/kernal/character-rom" from
36
+ // memmap.json's `desc` field by regex (37-RESEARCH.md Pitfall 2 /
37
+ // `BANK_CONDITIONAL_RANGES`'s own header comment in `memmap-lookup.ts`).
38
+ // `regionAdmitsEntry()` below matches against the STRUCTURED `section`
39
+ // field (plus one whole-word "RAM" label fallback, since no committed
40
+ // section names RAM specifically within any of the three ranges) --
41
+ // never `desc`.
42
+ // - Never default an unresolved region to RAM (the power-on state). The
43
+ // caller (`anno-join.ts`) declines instead; this module only ever
44
+ // returns a decoded/resolved answer or the explicit "not applicable"
45
+ // member -- it never invents a fallback of its own.
46
+ // - Never write the store's reserved `bank` column (D-37-25). This module
47
+ // does not touch the store at all, so that prohibition is satisfied by
48
+ // construction here -- restated because this is the plan that would be
49
+ // tempted.
50
+
51
+ import { BANK_CONDITIONAL_RANGES } from "./memmap-lookup.ts";
52
+ import type { MemmapEntry } from "./memmap-lookup.ts";
53
+
54
+ /**
55
+ * D-37-21: what a bank-conditional range currently contains. `not_applicable`
56
+ * exists so a caller can tell "this address is outside all three
57
+ * bank-conditional ranges" from "the constraint resolved to RAM" -- collapsing
58
+ * the two would make an out-of-range address silently RAM-constrained, which
59
+ * is exactly the confident-wrong-comment failure `AUTO-04` exists to prevent.
60
+ */
61
+ export type BankedRegion = "io_area" | "character_rom" | "ram" | "basic_rom" | "kernal_rom" | "not_applicable";
62
+
63
+ /** The decoded triple, plus the raw value it was decoded from. Each field
64
+ * answers "what does THIS ONE range currently hold", independent of whether
65
+ * the address a caller cares about actually falls inside it -- that mapping
66
+ * is `resolveBankedRegion()`'s job. */
67
+ export interface BankState {
68
+ raw: number;
69
+ ioRange: BankedRegion;
70
+ basicRange: BankedRegion;
71
+ kernalRange: BankedRegion;
72
+ }
73
+
74
+ /**
75
+ * D-37-21's bit arithmetic, stated as bit tests rather than left for a reader
76
+ * to re-derive from `bank.a`'s own inline comment -- which mislabels the
77
+ * all-RAM value's own bit 2 as clear (it is SET; the RAM outcome it states is
78
+ * right anyway, because the RAM case never depends on bit 2). Masks to bits
79
+ * 2-0 FIRST, so two values differing only above bit 2 decode identically --
80
+ * asserted as its own test case rather than merely trusted.
81
+ *
82
+ * - `$D000-$DFFF` (the I/O range): when bits #1-#0 are both clear, the
83
+ * region is RAM regardless of bit #2. Otherwise, bit #2 clear is
84
+ * Character ROM; bit #2 set is the I/O area.
85
+ * - `$A000-$BFFF` (the BASIC range): BASIC ROM only when BOTH bits #1 and
86
+ * #0 are set; otherwise RAM.
87
+ * - `$E000-$FFFF` (the KERNAL range): KERNAL ROM when bit #1 is set;
88
+ * otherwise RAM.
89
+ */
90
+ export function decodeBankState(value: number): BankState {
91
+ const raw = value;
92
+ const b = value & 0x07; // bits #2-#0: CHAREN(2) HIRAM(1) LORAM(0)
93
+ const bits10 = b & 0x03;
94
+ const bit2Set = (b & 0x04) !== 0;
95
+
96
+ const ioRange: BankedRegion = bits10 === 0 ? "ram" : bit2Set ? "io_area" : "character_rom";
97
+ const basicRange: BankedRegion = bits10 === 0x03 ? "basic_rom" : "ram";
98
+ const kernalRange: BankedRegion = (b & 0x02) !== 0 ? "kernal_rom" : "ram";
99
+
100
+ return { raw, ioRange, basicRange, kernalRange };
101
+ }
102
+
103
+ /** Does `address` fall inside ANY of the three bank-conditional ranges,
104
+ * regardless of the current `$01` value? The join uses this to decide
105
+ * whether the whole reaching-values/decline machinery applies to an address
106
+ * at all -- outside these three ranges, bank state is irrelevant and the
107
+ * candidate set stays unconstrained (D-37-22). */
108
+ export function isBankConditionalAddress(address: number): boolean {
109
+ for (const range of BANK_CONDITIONAL_RANGES) {
110
+ if (address >= range.start && address <= range.end) return true;
111
+ }
112
+ return false;
113
+ }
114
+
115
+ /**
116
+ * D-37-22: resolves a single address against an already-decoded `state`,
117
+ * returning the NOT-APPLICABLE member for any address outside all three
118
+ * `BANK_CONDITIONAL_RANGES` -- never RAM. The three ranges are disjoint by
119
+ * construction (each a fixed, hand-maintained, non-overlapping span), so the
120
+ * first (and only) match determines the answer.
121
+ */
122
+ export function resolveBankedRegion(address: number, state: BankState): BankedRegion {
123
+ for (const range of BANK_CONDITIONAL_RANGES) {
124
+ if (address < range.start || address > range.end) continue;
125
+ if (range.start === 0xd000) return state.ioRange;
126
+ if (range.start === 0xa000) return state.basicRange;
127
+ if (range.start === 0xe000) return state.kernalRange;
128
+ }
129
+ return "not_applicable";
130
+ }
131
+
132
+ /**
133
+ * The region-to-map-entry consistency table (D-37-22's candidate constraint,
134
+ * T-37-29's mitigation). Matches the STRUCTURED `section` field, never
135
+ * `desc` (Pitfall 2) -- a small, explicit, hand-maintained keyword set per
136
+ * region, cross-checked against the real committed `memmap.json`'s own
137
+ * section vocabulary (37-RESEARCH.md §C; MEASURED this plan against the
138
+ * real file's `$D000-$DFFF` overlap).
139
+ *
140
+ * RAM has NO dedicated `section` anywhere in the three bank-conditional
141
+ * ranges -- there is nothing chip-specific to document about plain program
142
+ * RAM sitting underneath a ROM/IO window -- so it is matched instead against
143
+ * the entry's own LABEL for a whole-word "RAM" token, which is exactly how
144
+ * the map's own "depends on the processor port" entries describe the RAM
145
+ * alternative (e.g. "I/O Area (memory mapped chip registers), Character ROM
146
+ * or RAM area"; "KERNAL ROM or RAM area"; "BASIC ROM or RAM area").
147
+ */
148
+ const SECTION_MARKERS: Readonly<Record<Exclude<BankedRegion, "ram" | "not_applicable">, readonly string[]>> = Object.freeze({
149
+ io_area: Object.freeze(["I/O Area", "VIC", "SID", "CIA", "Color RAM"]),
150
+ character_rom: Object.freeze(["Character ROM"]),
151
+ basic_rom: Object.freeze(["BASIC ROM"]),
152
+ kernal_rom: Object.freeze(["KERNAL ROM"]),
153
+ });
154
+
155
+ /** Whole-word "RAM" -- deliberately NOT a bare substring match, so a label
156
+ * like "PROGRAM" or "RAMPAGE" (neither of which exists in the committed map
157
+ * today, but the rule should not depend on that) never false-matches. */
158
+ const RAM_LABEL_WORD = /\bRAM\b/;
159
+
160
+ /**
161
+ * Does `entry` represent `region`? `not_applicable` never reaches here -- the
162
+ * join only calls this once a real bank-conditional region has been
163
+ * resolved for a specific address. An entry matching NO region member
164
+ * returns `false`, so the caller's candidate-narrowing EXCLUDES it rather
165
+ * than silently admitting it (D-37-22's own prohibition).
166
+ */
167
+ export function regionAdmitsEntry(entry: MemmapEntry, region: Exclude<BankedRegion, "not_applicable">): boolean {
168
+ if (region === "ram") return RAM_LABEL_WORD.test(entry.label);
169
+ const markers = SECTION_MARKERS[region];
170
+ return typeof entry.section === "string" && markers.some((marker) => entry.section.includes(marker));
171
+ }