@henols/vice-mcp 0.1.11 → 0.1.12
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/disasm-decoder.ts +28 -4
- package/package.json +1 -1
package/disasm-decoder.ts
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
// CPU-history decode (GAIN-01) import THIS file directly, never a tool
|
|
6
6
|
// module, so a protocol import here would force those consumers to pull in
|
|
7
7
|
// transport code they do not need. This module has no emulator, no
|
|
8
|
-
// protocol, no network -- its only input is a byte array.
|
|
8
|
+
// protocol, no network -- its only input is a byte array. Note: DERIV-02 and
|
|
9
|
+
// GAIN-01 were both cut from v0.2.0 scope on 2026-08-17 -- see the
|
|
10
|
+
// startAddress bound below, which is now defense-in-depth on a currently
|
|
11
|
+
// unreachable path rather than a guard against a live in-process caller.
|
|
9
12
|
//
|
|
10
13
|
// ---------------------------------------------------------------------------
|
|
11
14
|
// WHY THIS FILE EXISTS RATHER THAN LIVING INSIDE THE TOOL HANDLER
|
|
@@ -30,6 +33,13 @@
|
|
|
30
33
|
// - Never add recursion or an unbounded loop -- the bound is what makes an
|
|
31
34
|
// attacker-controlled memory image safe to decode (04-RESEARCH.md
|
|
32
35
|
// Security Domain, T-04-03-01).
|
|
36
|
+
// - Never fold the `<= 0xffff` upper bound into `isNonNegativeSafeInteger()`
|
|
37
|
+
// or apply it to `opts.count`/`opts.end` -- those two stay unbounded by
|
|
38
|
+
// design (04-REVIEW.md IN-03: the loop is always bounded by
|
|
39
|
+
// `bytes.length`, so an absurd value degrades to "no effective limit",
|
|
40
|
+
// never a crash or hang). Bounding them would be a behaviour change
|
|
41
|
+
// dressed up as a consistency fix. The upper bound belongs only in the
|
|
42
|
+
// separate `isValidStartAddress()` guard below.
|
|
33
43
|
|
|
34
44
|
import { OPCODES, type AddressingMode } from "./disasm-opcodes.ts";
|
|
35
45
|
|
|
@@ -94,6 +104,18 @@ function isNonNegativeSafeInteger(value: unknown): value is number {
|
|
|
94
104
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
95
105
|
}
|
|
96
106
|
|
|
107
|
+
/** True iff `value` is a valid `startAddress` -- a safe, non-negative integer
|
|
108
|
+
* that additionally fits in the C64's 16-bit address space (`<= 0xffff`).
|
|
109
|
+
* Deliberately separate from `isNonNegativeSafeInteger()` rather than an
|
|
110
|
+
* upper bound folded into it: `opts.count`/`opts.end` must stay unbounded
|
|
111
|
+
* (see the `WHAT NOT TO DO` block above and 04-REVIEW.md IN-03), so only
|
|
112
|
+
* `startAddress` gets this stricter narrowing. Mirrors `stock-address.ts`'s
|
|
113
|
+
* `inAddressRange()` without importing it -- D-05 keeps this module's only
|
|
114
|
+
* import to `./disasm-opcodes.ts`. */
|
|
115
|
+
function isValidStartAddress(value: unknown): value is number {
|
|
116
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= 0xffff;
|
|
117
|
+
}
|
|
118
|
+
|
|
97
119
|
/** Interprets `b` as an 8-bit two's-complement signed byte, per DISASM-04's
|
|
98
120
|
* branch resolution rule: `signed8(b) = b < 0x80 ? b : b - 0x100`. */
|
|
99
121
|
function signed8(b: number): number {
|
|
@@ -118,12 +140,14 @@ function buildNotes(flags: { truncated: boolean; pageWrap: boolean; illegal: boo
|
|
|
118
140
|
* `startAddress`. Bounded by construction (T-04-03-01): a single `while`
|
|
119
141
|
* loop over a byte cursor, every iteration consumes at least one byte, no
|
|
120
142
|
* recursion anywhere in this file. Never throws -- malformed input (a
|
|
121
|
-
* non-`Uint8Array` `bytes`, a negative or non-integer `startAddress
|
|
122
|
-
*
|
|
143
|
+
* non-`Uint8Array` `bytes`, a negative or non-integer `startAddress`, or a
|
|
144
|
+
* `startAddress` above `0xffff`, the top of the 16-bit address space)
|
|
145
|
+
* returns `[]` rather than wrapping the address into range and returning a
|
|
146
|
+
* plausible-looking but wrong listing.
|
|
123
147
|
*/
|
|
124
148
|
export function decode(bytes: Uint8Array, startAddress: number, opts: DecodeOptions = {}): Instruction[] {
|
|
125
149
|
if (!(bytes instanceof Uint8Array)) return [];
|
|
126
|
-
if (!
|
|
150
|
+
if (!isValidStartAddress(startAddress)) return [];
|
|
127
151
|
|
|
128
152
|
const options = isPlainObject(opts) ? opts : {};
|
|
129
153
|
const count = isNonNegativeSafeInteger(options.count) ? options.count : undefined;
|
package/package.json
CHANGED