@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.
- package/README.md +2 -1
- package/THIRD-PARTY-NOTICES.md +1 -24
- package/{r2000-acme-ident.ts → anno-acme-ident.ts} +13 -13
- package/anno-cli.ts +1465 -0
- package/{r2000-confidence.ts → anno-confidence.ts} +22 -22
- package/anno-coverage.ts +2465 -0
- package/{r2000-d64.ts → anno-d64.ts} +5 -5
- package/anno-derive.ts +590 -0
- package/anno-details.ts +169 -0
- package/anno-enum-gen.ts +533 -0
- package/anno-export-asm.ts +1310 -0
- package/anno-index.ts +150 -0
- package/{r2000-memmap-render.ts → anno-memmap-render.ts} +236 -95
- package/{r2000-regbits-gen.ts → anno-regbits-gen.ts} +20 -15
- package/{r2000-regbits.json → anno-regbits.json} +2 -2
- package/anno-register.ts +240 -0
- package/anno-store.ts +3486 -0
- package/anno-symbols.ts +266 -0
- package/anno-tools.ts +2111 -0
- package/anno-types.ts +1636 -0
- package/block-class.ts +201 -0
- package/build.ts +1 -1
- package/capability-registry.ts +3 -1
- package/disasm-decoder.ts +14 -14
- package/disasm-opcodes.ts +4 -4
- package/disasm-renderer.ts +2 -2
- package/hostpath.ts +1 -1
- package/install-resources.ts +1 -1
- package/package.json +23 -17
- package/prg-image.ts +119 -0
- package/repo-root.ts +20 -5
- package/resources/broker-launch.mjs +8 -4
- package/resources/vice-launcher.sh +3 -3
- package/stock-address.ts +5 -5
- package/stock-cia.ts +2 -2
- package/stock-condition.ts +7 -7
- package/stock-connect.ts +1 -1
- package/stock-dispatch.ts +35 -5
- package/stock-execution.ts +5 -3
- package/stock-input.ts +9 -9
- package/stock-machine.ts +17 -6
- package/stock-protocol.ts +16 -11
- package/stock-registers.ts +54 -29
- package/stock-sprites.ts +3 -3
- package/stock-symbols.ts +9 -9
- package/stock-timing.ts +1 -1
- package/stock-vicii.ts +1 -1
- package/version.ts +1 -1
- package/vice-proxy.ts +68 -46
- package/r2000-cli.ts +0 -1103
- package/r2000-enum-gen.ts +0 -574
- package/r2000-launch.ts +0 -357
- package/r2000-mcp-client.ts +0 -596
- package/r2000-project.ts +0 -190
- package/r2000-symbols.ts +0 -388
- package/r2000-tools.ts +0 -914
- 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
|
+
}
|