@henols/vice-mcp 0.2.2 → 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.
- package/README.md +2 -2
- package/THIRD-PARTY-NOTICES.md +422 -1
- package/anno-bank.ts +171 -0
- package/anno-cli.ts +1674 -99
- package/anno-enum-gen.ts +416 -30
- package/anno-export-asm.ts +1175 -89
- package/anno-graphics.ts +338 -0
- package/anno-hazard-report.ts +1367 -0
- package/anno-import.ts +495 -0
- package/anno-join.ts +480 -0
- package/anno-provenance-ledger.ts +472 -0
- package/anno-register.ts +159 -0
- package/anno-store-export.ts +661 -0
- package/anno-store.ts +518 -2
- package/anno-tools.ts +1169 -16
- package/anno-types.ts +275 -2
- package/backend-detect.mts +124 -312
- package/build.ts +3 -1
- package/capture-predicate.ts +597 -0
- package/channel-lock.ts +349 -0
- package/evid-ingest.ts +217 -0
- package/evid-reconcile.ts +316 -0
- package/host-tool-client.ts +430 -0
- package/incident-record.ts +23 -12
- package/install-resources.ts +29 -13
- package/memmap-lookup.ts +285 -0
- package/package.json +27 -8
- package/prg-image.ts +1 -2
- package/repo-root.ts +87 -3
- package/resources/backend-detect.mjs +98 -236
- package/resources/broker-control.mjs +189 -16
- package/resources/broker-epoch.mjs +1 -1
- package/resources/broker-kill.mjs +8 -2
- package/resources/broker-launch.mjs +365 -210
- package/resources/broker-state.mjs +64 -18
- package/resources/container-guard.mjs +1 -1
- package/resources/ghidra-project.mjs +790 -0
- package/resources/host-tool.mjs +2561 -0
- package/resources/vice-broker.mjs +330 -184
- package/resources/vice-launcher.sh +127 -9
- package/stock-address.ts +1 -1
- package/stock-condition.ts +1 -1
- package/stock-connect.ts +9 -5
- package/stock-derived.ts +29 -37
- package/stock-diagnose.ts +200 -36
- package/stock-dispatch.ts +179 -77
- package/stock-handler.ts +1 -1
- package/stock-paths.ts +18 -14
- package/stock-petscii.ts +1 -1
- package/stock-protocol.ts +1 -1
- package/stock-recycle.ts +83 -2
- package/stock-reproducible-run.ts +811 -0
- package/stock-run-until.ts +100 -1
- package/stock-symbols.ts +4 -4
- package/stock-timing.ts +1 -1
- package/stop-oracle.ts +167 -0
- package/text-capability-probe.ts +660 -0
- package/text-connect.ts +157 -0
- package/text-protocol.ts +810 -0
- package/text-tools.ts +778 -0
- package/textmon-backtrace.ts +385 -0
- package/textmon-cpuhistory.ts +335 -0
- package/textmon-memmap.ts +494 -0
- package/textmon-profile.ts +458 -0
- package/textmon-registers.ts +748 -0
- package/tools-manifest.stock.json +864 -3
- package/vice-broker-client.ts +189 -42
- package/vice-errors.ts +268 -0
- package/vice-proxy.ts +339 -2144
- package/vsf-slice.ts +640 -0
- package/anno-d64.ts +0 -310
- package/capability-registry.ts +0 -390
- package/refresh-manifest.ts +0 -124
- package/tools-manifest.json +0 -1223
- package/vice-probe.ts +0 -278
- package/vice-sync.ts +0 -336
- package/vice.ts +0 -772
package/anno-graphics.ts
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// anno-graphics.ts
|
|
3
|
+
//
|
|
4
|
+
// Phase 37, plan 37-07 (AUTO-06): derives the VIC-II graphics areas -- screen
|
|
5
|
+
// matrix, character set or bitmap, and sprite POINTERS -- from RECOVERED VIC
|
|
6
|
+
// register VALUES, never from cross-references. The chip fetches its
|
|
7
|
+
// character/bitmap/screen data by direct memory access, so a character set
|
|
8
|
+
// may be referenced by NO INSTRUCTION ANYWHERE IN THE PROGRAM -- exactly the
|
|
9
|
+
// case a cross-reference-driven join structurally cannot find. That is
|
|
10
|
+
// AUTO-06's entire reason to exist (`37-RESEARCH.md` SS E: no existing code in
|
|
11
|
+
// this repository decodes these three registers for this purpose -- the
|
|
12
|
+
// stock-backend modules that name them read LIVE emulator state for a
|
|
13
|
+
// different question).
|
|
14
|
+
//
|
|
15
|
+
// THIS MODULE TAKES PLAIN REGISTER-WRITE VALUES AND RETURNS PLAIN RANGES --
|
|
16
|
+
// AND NOTHING ELSE (D-37-26). It never receives a store handle, never opens
|
|
17
|
+
// or names `node:sqlite`, never imports `anno-store.ts`, `anno-join.ts`, or
|
|
18
|
+
// any module that reads the stored cross-reference graph, and never imports
|
|
19
|
+
// `hostpath.ts`/`containerpath.ts` or the emulator backend
|
|
20
|
+
// (`stock-vicii.ts`/`stock-sprites.ts`). A module that could see the
|
|
21
|
+
// reference graph would let an implementation quietly lean on it for the
|
|
22
|
+
// very case this derivation exists to cover -- so its import list is scanned
|
|
23
|
+
// structurally (`anno-graphics.test.ts`) rather than merely reviewed.
|
|
24
|
+
//
|
|
25
|
+
// SPRITE BITMAP LOCATIONS ARE OUT OF SCOPE (D-37-29): this module derives the
|
|
26
|
+
// eight-byte sprite POINTER TABLE range only. The pointer VALUES themselves
|
|
27
|
+
// are program data, usually written at run time, and are not register values
|
|
28
|
+
// a static analysis recovers -- this module must never appear to promise
|
|
29
|
+
// them.
|
|
30
|
+
//
|
|
31
|
+
// A MISSING REGISTER IS A STATED ABSENCE, NEVER A DEFAULT (D-37-28): where a
|
|
32
|
+
// required register value was never recovered, the ranges that depend on it
|
|
33
|
+
// are omitted from that map and the map names the missing register in
|
|
34
|
+
// `missingRegisters`. Power-on defaults exist and are well known, which is
|
|
35
|
+
// exactly why substituting one is tempting and wrong: a range built on an
|
|
36
|
+
// assumed value is indistinguishable in the output from one built on a
|
|
37
|
+
// recovered value, and this phase exists to prevent exactly that class of
|
|
38
|
+
// confident wrong output.
|
|
39
|
+
//
|
|
40
|
+
// SEVERAL COMBINATIONS ARE SEVERAL MAPS, NEVER ONE MERGED MAP (D-37-27): a
|
|
41
|
+
// program that reprograms bank, screen or charset per raster split has
|
|
42
|
+
// several valid graphics maps, and a single derived map would be wrong for
|
|
43
|
+
// all but one -- the same path-dependence limit AUTO-05 names for the
|
|
44
|
+
// processor port. `deriveGraphicsRanges()` returns one map per DISTINCT
|
|
45
|
+
// combination of the three registers' own recovered values: the cross
|
|
46
|
+
// product of each register's own distinct-value set, with a register that
|
|
47
|
+
// carries zero facts contributing a single "missing" slot rather than an
|
|
48
|
+
// axis (never a synthesized value). Combinations are produced in ascending
|
|
49
|
+
// tuple order (missing sorts first), so two runs over the same facts return
|
|
50
|
+
// deeply equal arrays.
|
|
51
|
+
//
|
|
52
|
+
// NO GRAPHICS-SPECIFIC DATA TYPE EXISTS (D-37-30): the store's `DataType`
|
|
53
|
+
// vocabulary (`anno-types.ts`) is a frozen twelve with no graphics-specific
|
|
54
|
+
// member, and widening it is a schema decision no plan in this phase makes --
|
|
55
|
+
// widening it would require re-auditing every consumer of that frozen list,
|
|
56
|
+
// including `dxa-blocks.ts`'s own derivation of which types are data-bearing.
|
|
57
|
+
// Every range this module emits therefore carries the closest existing
|
|
58
|
+
// member, `"byte"`.
|
|
59
|
+
//
|
|
60
|
+
// BIT ARITHMETIC CROSS-CHECKED AGAINST `stock-vicii.ts`/`stock-sprites.ts`
|
|
61
|
+
// (READ-ONLY, NEVER IMPORTED): this module's bank/screen/charset/bitmap
|
|
62
|
+
// arithmetic was checked by hand against those two files' existing, tested
|
|
63
|
+
// decode of the SAME three registers for the live-emulator backend, and
|
|
64
|
+
// found to agree exactly -- see the plan 37-07 SUMMARY for the comparison.
|
|
65
|
+
// This module REIMPLEMENTS that arithmetic rather than importing it, because
|
|
66
|
+
// importing either file would reach the emulator backend's own transport
|
|
67
|
+
// seam, which this module must never touch.
|
|
68
|
+
//
|
|
69
|
+
// WHAT NOT TO DO:
|
|
70
|
+
// - Never derive `kind` from a range's own size, or vice versa. The mode
|
|
71
|
+
// bit decides `kind`; `kind` then decides size. Deriving the kind from
|
|
72
|
+
// the size would make a wrong mode bit produce a plausible range of the
|
|
73
|
+
// wrong length rather than a visibly wrong kind.
|
|
74
|
+
// - Never merge two distinct register-value combinations into one map.
|
|
75
|
+
// Each combination this module observes gets its own entry in the
|
|
76
|
+
// returned array, even when two combinations' ranges happen to overlap.
|
|
77
|
+
// - Never widen `missingRegisters` handling to substitute a guessed value
|
|
78
|
+
// "just this once" -- every range depending on a missing register is
|
|
79
|
+
// omitted, with no exception.
|
|
80
|
+
|
|
81
|
+
import type { DataType } from "./anno-types.ts";
|
|
82
|
+
|
|
83
|
+
/** The address of the CIA2 Data Port A register (VIC bank select, bits 0-1)
|
|
84
|
+
* this module watches. Matches `CONST_WRITE_WATCHED_ADDRESSES` in
|
|
85
|
+
* `anno-import.ts` (kept in step there, not re-imported here -- see the
|
|
86
|
+
* module header on why this file imports nothing from that module). */
|
|
87
|
+
export const BANK_SELECT_ADDRESS = 0xdd00;
|
|
88
|
+
/** The address of the VIC-II memory setup register (screen/charset-or-bitmap
|
|
89
|
+
* pointers) this module watches. */
|
|
90
|
+
export const MEMORY_CONTROL_ADDRESS = 0xd018;
|
|
91
|
+
/** The address of VIC-II control register #1 (bit 5: bitmap mode) this
|
|
92
|
+
* module watches. */
|
|
93
|
+
export const CONTROL_REGISTER_1_ADDRESS = 0xd011;
|
|
94
|
+
|
|
95
|
+
/** The three watched registers' own stable names, used both as
|
|
96
|
+
* `GraphicsMap.registerValues`/`missingRegisters` keys and as
|
|
97
|
+
* `GraphicsRange.derivedFrom` keys -- one place names them so a caller sees
|
|
98
|
+
* the identical string whichever field it reads. */
|
|
99
|
+
const REGISTER_NAMES: Readonly<Record<number, string>> = Object.freeze({
|
|
100
|
+
[BANK_SELECT_ADDRESS]: "bank-select",
|
|
101
|
+
[MEMORY_CONTROL_ADDRESS]: "memory-control",
|
|
102
|
+
[CONTROL_REGISTER_1_ADDRESS]: "control-register-1",
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const BANK_SELECT_NAME = REGISTER_NAMES[BANK_SELECT_ADDRESS]!;
|
|
106
|
+
const MEMORY_CONTROL_NAME = REGISTER_NAMES[MEMORY_CONTROL_ADDRESS]!;
|
|
107
|
+
const CONTROL_REGISTER_1_NAME = REGISTER_NAMES[CONTROL_REGISTER_1_ADDRESS]!;
|
|
108
|
+
|
|
109
|
+
/** 16 KB per VIC-II bank ($DD00 bits 0-1, four banks). */
|
|
110
|
+
const VIC_BANK_SIZE = 16384;
|
|
111
|
+
/** $D018's high nibble is a screen-pointer unit of this many bytes. */
|
|
112
|
+
const SCREEN_MATRIX_GRANULARITY = 1024;
|
|
113
|
+
/** The fixed size of the screen matrix itself. */
|
|
114
|
+
const SCREEN_MATRIX_SIZE = 1024;
|
|
115
|
+
/** $D018's low nibble's upper three bits are a character/bitmap-pointer unit
|
|
116
|
+
* of this many bytes. */
|
|
117
|
+
const CHARACTER_BASE_GRANULARITY = 2048;
|
|
118
|
+
/** The fixed size of a character set (256 characters, 8 bytes each). */
|
|
119
|
+
const CHARACTER_SET_SIZE = 2048;
|
|
120
|
+
/** The fixed size of a hi-res/multicolour bitmap. */
|
|
121
|
+
const BITMAP_SIZE = 8192;
|
|
122
|
+
/** The sprite pointer table's fixed offset from the screen matrix base. */
|
|
123
|
+
export const SPRITE_POINTER_OFFSET = 0x3f8;
|
|
124
|
+
/** The sprite pointer table's fixed size -- eight pointer bytes, one per
|
|
125
|
+
* sprite. */
|
|
126
|
+
const SPRITE_POINTER_SIZE = 8;
|
|
127
|
+
|
|
128
|
+
/** The existing, byte-shaped `DataType` every range this module emits
|
|
129
|
+
* carries (D-37-30). */
|
|
130
|
+
const GRAPHICS_DATA_TYPE: DataType = "byte";
|
|
131
|
+
|
|
132
|
+
/** One recovered register write, in the same shape `ConstWriteFact` in
|
|
133
|
+
* `anno-import.ts` exports (`{ storeAddress, targetAddress, value }`).
|
|
134
|
+
* Declared locally -- not imported -- so this module's own import list stays
|
|
135
|
+
* free of any dependency on `anno-import.ts` (see the module header); plain
|
|
136
|
+
* structural typing means a caller can still pass `ConstWriteFact[]`
|
|
137
|
+
* directly with no translation layer. */
|
|
138
|
+
export interface GraphicsConstWriteFact {
|
|
139
|
+
storeAddress: number;
|
|
140
|
+
targetAddress: number;
|
|
141
|
+
value: number;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** One derived graphics range. Shaped to match `dxa-blocks.ts`'s
|
|
145
|
+
* `KnownDataRow` (`start`/`endInclusive`/`dataType`) so a caller can produce
|
|
146
|
+
* a `KnownDataRow` from one without a translation layer -- `kind` and
|
|
147
|
+
* `derivedFrom` are this module's own additions, carrying which of the four
|
|
148
|
+
* things the range is and which register values produced it. Both ends are
|
|
149
|
+
* INCLUSIVE. */
|
|
150
|
+
export interface GraphicsRange {
|
|
151
|
+
start: number;
|
|
152
|
+
endInclusive: number;
|
|
153
|
+
kind: string;
|
|
154
|
+
dataType: DataType;
|
|
155
|
+
derivedFrom: Record<string, number>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** One derived graphics map -- the ranges implied by one distinct
|
|
159
|
+
* combination of the three registers' own recovered values.
|
|
160
|
+
* `registerValues` carries every register value THIS map actually had;
|
|
161
|
+
* `missingRegisters` names, by `REGISTER_NAMES`'s own spelling, every
|
|
162
|
+
* register this map had no recovered value for at all. A non-empty
|
|
163
|
+
* `missingRegisters` map carries only the ranges that did not depend on the
|
|
164
|
+
* missing value(s) -- never nothing, and never a guess. */
|
|
165
|
+
export interface GraphicsMap {
|
|
166
|
+
ranges: GraphicsRange[];
|
|
167
|
+
registerValues: Record<string, number>;
|
|
168
|
+
missingRegisters: string[];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** VIC-II bank base: $DD00 bits 0-1, INVERTED (the chip's own convention --
|
|
172
|
+
* the stored value is the complement of the bank number), times the 16 KB
|
|
173
|
+
* bank size. A value with both low bits set (`0b11`) selects bank 0 (base
|
|
174
|
+
* 0); a value with both low bits clear (`0b00`) selects bank 3 (the highest
|
|
175
|
+
* base, $C000). Matches `stock-sprites.ts`'s `vicBank()`/`vicBankBase()`
|
|
176
|
+
* exactly (read-only cross-check, never imported -- see module header). */
|
|
177
|
+
function vicBankBase(bankSelectValue: number): number {
|
|
178
|
+
const invertedBankBits = 3 - (bankSelectValue & 0x03);
|
|
179
|
+
return invertedBankBits * VIC_BANK_SIZE;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Screen matrix base: $D018's high nibble, scaled by the screen-matrix
|
|
183
|
+
* granularity, added to the bank base. Matches `stock-sprites.ts`'s
|
|
184
|
+
* `screenBase()`/`stock-vicii.ts`'s `memorySetup.screenOffset` exactly. */
|
|
185
|
+
function screenMatrixStart(bankBase: number, memoryControlValue: number): number {
|
|
186
|
+
return bankBase + ((memoryControlValue >> 4) & 0x0f) * SCREEN_MATRIX_GRANULARITY;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Character-or-bitmap base: $D018's low nibble's upper three bits (bits
|
|
190
|
+
* #1-#3), scaled by the character-base granularity, added to the bank base.
|
|
191
|
+
* Matches `stock-vicii.ts`'s `memorySetup.charsetOffset` exactly. */
|
|
192
|
+
function characterOrBitmapStart(bankBase: number, memoryControlValue: number): number {
|
|
193
|
+
return bankBase + ((memoryControlValue >> 1) & 0x07) * CHARACTER_BASE_GRANULARITY;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** True iff $D011 bit 5 (bitmap mode) is set. Matches `stock-vicii.ts`'s
|
|
197
|
+
* `control1.bitmapMode` exactly. Do not derive `kind` from a range's size --
|
|
198
|
+
* this bit decides `kind`, and `kind` then decides size (module header). */
|
|
199
|
+
function isBitmapMode(controlRegister1Value: number): boolean {
|
|
200
|
+
return ((controlRegister1Value >> 5) & 1) === 1;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Every DISTINCT value `facts` records for a write to `targetAddress`,
|
|
204
|
+
* ascending. Two facts recording the SAME value are one combination axis
|
|
205
|
+
* value, not two. */
|
|
206
|
+
function distinctValuesFor(facts: readonly GraphicsConstWriteFact[], targetAddress: number): number[] {
|
|
207
|
+
const seen = new Set<number>();
|
|
208
|
+
for (const fact of facts) {
|
|
209
|
+
if (fact.targetAddress === targetAddress) seen.add(fact.value);
|
|
210
|
+
}
|
|
211
|
+
return [...seen].sort((a, b) => a - b);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** One combination of the three registers' own recovered values --
|
|
215
|
+
* `undefined` names a register with zero recovered facts (D-37-28). */
|
|
216
|
+
interface RegisterCombo {
|
|
217
|
+
bankSelect: number | undefined;
|
|
218
|
+
memoryControl: number | undefined;
|
|
219
|
+
controlRegister1: number | undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Ascending, with `undefined` (missing) sorting first -- a stated,
|
|
223
|
+
* deterministic order over the tuple (D-37-27), not "whatever `Set`
|
|
224
|
+
* iteration happened to produce". */
|
|
225
|
+
function compareSlot(a: number | undefined, b: number | undefined): number {
|
|
226
|
+
const av = a ?? -1;
|
|
227
|
+
const bv = b ?? -1;
|
|
228
|
+
return av - bv;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function compareCombo(a: RegisterCombo, b: RegisterCombo): number {
|
|
232
|
+
return (
|
|
233
|
+
compareSlot(a.bankSelect, b.bankSelect) ||
|
|
234
|
+
compareSlot(a.memoryControl, b.memoryControl) ||
|
|
235
|
+
compareSlot(a.controlRegister1, b.controlRegister1)
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** One map for one combination. Screen matrix and sprite pointers depend
|
|
240
|
+
* only on `bankSelect`/`memoryControl`; the character-or-bitmap range
|
|
241
|
+
* additionally depends on `controlRegister1` (the mode bit) and is omitted
|
|
242
|
+
* on its own when that register alone is missing -- the other two ranges
|
|
243
|
+
* still derive. A missing `bankSelect` OR `memoryControl` removes every
|
|
244
|
+
* range (all three ultimately need the bank base and the screen-matrix
|
|
245
|
+
* base). */
|
|
246
|
+
function buildGraphicsMap(combo: RegisterCombo): GraphicsMap {
|
|
247
|
+
const { bankSelect, memoryControl, controlRegister1 } = combo;
|
|
248
|
+
|
|
249
|
+
const registerValues: Record<string, number> = {};
|
|
250
|
+
const missingRegisters: string[] = [];
|
|
251
|
+
if (bankSelect === undefined) missingRegisters.push(BANK_SELECT_NAME);
|
|
252
|
+
else registerValues[BANK_SELECT_NAME] = bankSelect;
|
|
253
|
+
if (memoryControl === undefined) missingRegisters.push(MEMORY_CONTROL_NAME);
|
|
254
|
+
else registerValues[MEMORY_CONTROL_NAME] = memoryControl;
|
|
255
|
+
if (controlRegister1 === undefined) missingRegisters.push(CONTROL_REGISTER_1_NAME);
|
|
256
|
+
else registerValues[CONTROL_REGISTER_1_NAME] = controlRegister1;
|
|
257
|
+
|
|
258
|
+
const ranges: GraphicsRange[] = [];
|
|
259
|
+
|
|
260
|
+
if (bankSelect !== undefined && memoryControl !== undefined) {
|
|
261
|
+
const bankBase = vicBankBase(bankSelect);
|
|
262
|
+
const screenStart = screenMatrixStart(bankBase, memoryControl);
|
|
263
|
+
const screenAndSpriteDerivedFrom: Record<string, number> = {
|
|
264
|
+
[BANK_SELECT_NAME]: bankSelect,
|
|
265
|
+
[MEMORY_CONTROL_NAME]: memoryControl,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
ranges.push({
|
|
269
|
+
start: screenStart,
|
|
270
|
+
endInclusive: screenStart + SCREEN_MATRIX_SIZE - 1,
|
|
271
|
+
kind: "screen-matrix",
|
|
272
|
+
dataType: GRAPHICS_DATA_TYPE,
|
|
273
|
+
derivedFrom: { ...screenAndSpriteDerivedFrom },
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
if (controlRegister1 !== undefined) {
|
|
277
|
+
const charOrBitmapStart = characterOrBitmapStart(bankBase, memoryControl);
|
|
278
|
+
const bitmap = isBitmapMode(controlRegister1);
|
|
279
|
+
ranges.push({
|
|
280
|
+
start: charOrBitmapStart,
|
|
281
|
+
endInclusive: charOrBitmapStart + (bitmap ? BITMAP_SIZE : CHARACTER_SET_SIZE) - 1,
|
|
282
|
+
kind: bitmap ? "bitmap" : "character-set",
|
|
283
|
+
dataType: GRAPHICS_DATA_TYPE,
|
|
284
|
+
derivedFrom: {
|
|
285
|
+
[BANK_SELECT_NAME]: bankSelect,
|
|
286
|
+
[MEMORY_CONTROL_NAME]: memoryControl,
|
|
287
|
+
[CONTROL_REGISTER_1_NAME]: controlRegister1,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const spriteStart = screenStart + SPRITE_POINTER_OFFSET;
|
|
293
|
+
ranges.push({
|
|
294
|
+
start: spriteStart,
|
|
295
|
+
endInclusive: spriteStart + SPRITE_POINTER_SIZE - 1,
|
|
296
|
+
kind: "sprite-pointers",
|
|
297
|
+
dataType: GRAPHICS_DATA_TYPE,
|
|
298
|
+
derivedFrom: { ...screenAndSpriteDerivedFrom },
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { ranges, registerValues, missingRegisters };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Derives the graphics areas -- screen matrix, character set or bitmap, and
|
|
307
|
+
* sprite pointers -- from recovered VIC register values ALONE (D-37-26).
|
|
308
|
+
* Returns one `GraphicsMap` per distinct combination of the three registers'
|
|
309
|
+
* own recovered values (D-37-27), in ascending tuple order, so two calls
|
|
310
|
+
* with the same `facts` return deeply equal arrays. A register with zero
|
|
311
|
+
* recovered facts contributes a single "missing" combination slot rather
|
|
312
|
+
* than an axis; every range depending on it is omitted from every map, and
|
|
313
|
+
* the map names it in `missingRegisters` (D-37-28). `facts` carrying values
|
|
314
|
+
* for addresses other than the three this module watches (e.g. the
|
|
315
|
+
* processor port, $0001) are ignored -- this module only ever reads writes
|
|
316
|
+
* to `BANK_SELECT_ADDRESS`/`MEMORY_CONTROL_ADDRESS`/`CONTROL_REGISTER_1_ADDRESS`.
|
|
317
|
+
*/
|
|
318
|
+
export function deriveGraphicsRanges(facts: readonly GraphicsConstWriteFact[]): GraphicsMap[] {
|
|
319
|
+
const bankSelectValues = distinctValuesFor(facts, BANK_SELECT_ADDRESS);
|
|
320
|
+
const memoryControlValues = distinctValuesFor(facts, MEMORY_CONTROL_ADDRESS);
|
|
321
|
+
const controlRegister1Values = distinctValuesFor(facts, CONTROL_REGISTER_1_ADDRESS);
|
|
322
|
+
|
|
323
|
+
const bankSlots: (number | undefined)[] = bankSelectValues.length > 0 ? bankSelectValues : [undefined];
|
|
324
|
+
const memorySlots: (number | undefined)[] = memoryControlValues.length > 0 ? memoryControlValues : [undefined];
|
|
325
|
+
const controlSlots: (number | undefined)[] = controlRegister1Values.length > 0 ? controlRegister1Values : [undefined];
|
|
326
|
+
|
|
327
|
+
const combos: RegisterCombo[] = [];
|
|
328
|
+
for (const bankSelect of bankSlots) {
|
|
329
|
+
for (const memoryControl of memorySlots) {
|
|
330
|
+
for (const controlRegister1 of controlSlots) {
|
|
331
|
+
combos.push({ bankSelect, memoryControl, controlRegister1 });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
combos.sort(compareCombo);
|
|
336
|
+
|
|
337
|
+
return combos.map(buildGraphicsMap);
|
|
338
|
+
}
|