@henols/vice-mcp 0.2.1 → 0.2.2

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 (57) hide show
  1. package/README.md +2 -1
  2. package/THIRD-PARTY-NOTICES.md +1 -24
  3. package/{r2000-acme-ident.ts → anno-acme-ident.ts} +13 -13
  4. package/anno-cli.ts +1465 -0
  5. package/{r2000-confidence.ts → anno-confidence.ts} +22 -22
  6. package/anno-coverage.ts +2465 -0
  7. package/{r2000-d64.ts → anno-d64.ts} +5 -5
  8. package/anno-derive.ts +590 -0
  9. package/anno-details.ts +169 -0
  10. package/anno-enum-gen.ts +533 -0
  11. package/anno-export-asm.ts +1310 -0
  12. package/anno-index.ts +150 -0
  13. package/{r2000-memmap-render.ts → anno-memmap-render.ts} +236 -95
  14. package/{r2000-regbits-gen.ts → anno-regbits-gen.ts} +20 -15
  15. package/{r2000-regbits.json → anno-regbits.json} +2 -2
  16. package/anno-register.ts +240 -0
  17. package/anno-store.ts +3486 -0
  18. package/anno-symbols.ts +266 -0
  19. package/anno-tools.ts +2111 -0
  20. package/anno-types.ts +1636 -0
  21. package/block-class.ts +201 -0
  22. package/build.ts +1 -1
  23. package/capability-registry.ts +3 -1
  24. package/disasm-decoder.ts +14 -14
  25. package/disasm-opcodes.ts +4 -4
  26. package/disasm-renderer.ts +2 -2
  27. package/hostpath.ts +1 -1
  28. package/install-resources.ts +1 -1
  29. package/package.json +23 -17
  30. package/prg-image.ts +119 -0
  31. package/repo-root.ts +20 -5
  32. package/resources/broker-launch.mjs +8 -4
  33. package/resources/vice-launcher.sh +3 -3
  34. package/stock-address.ts +5 -5
  35. package/stock-cia.ts +2 -2
  36. package/stock-condition.ts +7 -7
  37. package/stock-connect.ts +1 -1
  38. package/stock-dispatch.ts +35 -5
  39. package/stock-execution.ts +5 -3
  40. package/stock-input.ts +9 -9
  41. package/stock-machine.ts +17 -6
  42. package/stock-protocol.ts +16 -11
  43. package/stock-registers.ts +54 -29
  44. package/stock-sprites.ts +3 -3
  45. package/stock-symbols.ts +9 -9
  46. package/stock-timing.ts +1 -1
  47. package/stock-vicii.ts +1 -1
  48. package/version.ts +1 -1
  49. package/vice-proxy.ts +68 -46
  50. package/r2000-cli.ts +0 -1103
  51. package/r2000-enum-gen.ts +0 -574
  52. package/r2000-launch.ts +0 -357
  53. package/r2000-mcp-client.ts +0 -596
  54. package/r2000-project.ts +0 -190
  55. package/r2000-symbols.ts +0 -388
  56. package/r2000-tools.ts +0 -914
  57. package/r2000-verify.ts +0 -184
package/anno-index.ts ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+ // anno-index.ts
3
+ //
4
+ // The ONE place that answers "which annotated range owns this address" --
5
+ // a pure, narrowest-range-wins paint index over the whole 64K address space,
6
+ // rebuilt from rows and never maintained incrementally (STORE-03).
7
+ //
8
+ // ---------------------------------------------------------------------------
9
+ // WHY THIS FILE EXISTS
10
+ // ---------------------------------------------------------------------------
11
+ // The resolution rule is one sentence -- the SHORTEST range covering an address
12
+ // wins, and among equally short ones the LATER-inserted (higher id) wins -- and
13
+ // it has to be checkable against a second, independently written
14
+ // implementation. That cross-check is only possible if the rule is a pure
15
+ // function of a row list: an implementation that can only be reached by writing
16
+ // to a database cannot be fed the overlapping rows the tie-break case needs,
17
+ // because the store's write path exists precisely to prevent overlapping rows
18
+ // from ever being stored.
19
+ //
20
+ // So the rule lives here, taking rows as an argument, with no filesystem, no
21
+ // SQL, and no module-level state. The store calls it over `listRanges()`
22
+ // output; a test calls it over hand-built rows the store would never produce.
23
+ // Those are the same code path, which is the property that makes the
24
+ // cross-check mean anything.
25
+ //
26
+ // Rebuilding is affordable and was measured: a full rebuild over 2,000 rows is
27
+ // 8.56 ms, so there is no performance argument for the incremental variant that
28
+ // trap 4 forbids.
29
+ //
30
+ // ---------------------------------------------------------------------------
31
+ // WHAT NOT TO DO -- each entry names a specific, measured trap
32
+ // ---------------------------------------------------------------------------
33
+ // 1. NEVER move narrowest-wins behind the store's write path. That makes the
34
+ // equal-length tie-break unreachable: the write path's split-and-preserve
35
+ // is DESIGNED so overlapping rows never exist on disk, and the tie-break
36
+ // pin needs overlapping rows fed in directly. A pure module taking rows as
37
+ // an argument is the only shape that can be tested for it.
38
+ // 2. NEVER cache this index on disk. A cached index is a second truth that
39
+ // can disagree with the range table -- exactly the failure the
40
+ // derived-from-bytes coverage census (`COV-01`) exists to make impossible.
41
+ // Rebuild it; see the measurement above.
42
+ // 3. NEVER add an adjacency, coalescing or merge pass. `STORE-02` stores
43
+ // ranges AS ranges and never merges them, so there is no splitter
44
+ // primitive here to introduce. Merging is the named blocker behind
45
+ // `DECOMP-01` and `BUILD-02` and the predicted over-merge bias behind
46
+ // `COV-01`; all three are designed out by construction here, not
47
+ // documented as hazards to remember.
48
+ // 4. NEVER maintain the index incrementally. An incremental update has to
49
+ // know what the row it is removing was covering UNDERNEATH, which the
50
+ // index cannot say -- a painted cell records the winner, not the losers.
51
+ // 5. NEVER let the sort's stability decide the equal-length tie-break. It is
52
+ // pinned explicitly in the comparator below (decision `A4`) because the
53
+ // resolution rule requires SOME pinned tie-break and a stability-derived
54
+ // one is an implementation detail that two independent implementations
55
+ // would not share.
56
+ import { ADDRESS_MAX, ADDRESS_MIN, AnnoAddressError, AnnoRangeShapeError } from "./anno-types.ts";
57
+
58
+ /** One entry per 6510 address, 0x0000..0xFFFF inclusive. */
59
+ export const PAINT_INDEX_SIZE = 0x10000;
60
+
61
+ /** The value a cell holds when no range covers that address. Negative so it
62
+ * can never collide with a row id (SQLite `autoincrement` ids start at 1). */
63
+ export const NO_ROW = -1;
64
+
65
+ /** The painted index: `index[address]` is the winning row id, or `NO_ROW`.
66
+ * `Int32Array` rather than `number[]` so 65,536 cells cost 256 KB flat and the
67
+ * `fill()` per row is a typed-array memset rather than a per-cell loop. */
68
+ export type PaintIndex = Int32Array;
69
+
70
+ /** The minimum a row must carry to be paintable: an id and an inclusive span.
71
+ * Deliberately NOT `RangeRow` -- the index does not read, and must not read,
72
+ * the data type or the bank. */
73
+ export interface IndexableRange {
74
+ id: number;
75
+ start: number;
76
+ endInclusive: number;
77
+ }
78
+
79
+ /**
80
+ * Builds the paint index from `rows`. Does not mutate `rows` -- it sorts a
81
+ * copy.
82
+ *
83
+ * The algorithm is one line of insight: paint LONGEST span first, so any
84
+ * shorter range covering the same address is painted over it and therefore
85
+ * wins. Narrowest-wins falls out of the paint order rather than being
86
+ * re-checked per cell. Among equal-length rows the comparator orders by
87
+ * ASCENDING id, so the HIGHER id paints last and wins -- decision `A4`,
88
+ * "equal length, later insertion wins", pinned here rather than left to
89
+ * `Array.prototype.sort`'s stability (trap 5).
90
+ *
91
+ * Refuses a row whose span leaves the address space. This is not defensive
92
+ * decoration: `TypedArray.prototype.fill` CLAMPS out-of-range indices silently,
93
+ * so an unrefused bad row would paint a different span than it named, or
94
+ * nothing at all, with no error anywhere.
95
+ */
96
+ export function buildPaintIndex(rows: readonly IndexableRange[]): PaintIndex {
97
+ const index = new Int32Array(PAINT_INDEX_SIZE).fill(NO_ROW);
98
+
99
+ for (const row of rows) {
100
+ if (!Number.isInteger(row.start) || row.start < ADDRESS_MIN || row.start > ADDRESS_MAX) {
101
+ throw new AnnoRangeShapeError(
102
+ `range id ${row.id}: start ${String(row.start)} is outside the address space -- expected an integer ${ADDRESS_MIN}..${ADDRESS_MAX} ($0000-$ffff)`,
103
+ { start: row.start, endInclusive: row.endInclusive },
104
+ );
105
+ }
106
+ if (!Number.isInteger(row.endInclusive) || row.endInclusive < ADDRESS_MIN || row.endInclusive > ADDRESS_MAX) {
107
+ throw new AnnoRangeShapeError(
108
+ `range id ${row.id}: endInclusive ${String(row.endInclusive)} is outside the address space -- expected an integer ${ADDRESS_MIN}..${ADDRESS_MAX} ($0000-$ffff)`,
109
+ { start: row.start, endInclusive: row.endInclusive },
110
+ );
111
+ }
112
+ if (row.endInclusive < row.start) {
113
+ throw new AnnoRangeShapeError(
114
+ `range id ${row.id}: endInclusive ${row.endInclusive} is below start ${row.start} -- both ends are INCLUSIVE`,
115
+ { start: row.start, endInclusive: row.endInclusive },
116
+ );
117
+ }
118
+ }
119
+
120
+ const ordered = [...rows].sort((a, b) => {
121
+ const spanA = a.endInclusive - a.start + 1;
122
+ const spanB = b.endInclusive - b.start + 1;
123
+ if (spanA !== spanB) {
124
+ return spanB - spanA;
125
+ }
126
+ return a.id - b.id;
127
+ });
128
+
129
+ for (const row of ordered) {
130
+ index.fill(row.id, row.start, row.endInclusive + 1);
131
+ }
132
+
133
+ return index;
134
+ }
135
+
136
+ /**
137
+ * The winning row id at `address`, or `NO_ROW` when nothing covers it. Throws
138
+ * `AnnoAddressError` for an address outside the space rather than returning
139
+ * `undefined` -- an out-of-range probe is a caller bug, and `undefined` would
140
+ * be indistinguishable from "nothing here" at the call site.
141
+ */
142
+ export function resolveAt(index: PaintIndex, address: number): number {
143
+ if (!Number.isInteger(address) || address < ADDRESS_MIN || address > ADDRESS_MAX) {
144
+ throw new AnnoAddressError(
145
+ `address ${String(address)} is out of range -- expected an integer ${ADDRESS_MIN}..${ADDRESS_MAX} ($0000-$ffff)`,
146
+ { input: address, what: "address" },
147
+ );
148
+ }
149
+ return index[address];
150
+ }