@emu198x/zx-spectrum 0.2.0 → 0.4.0
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 +75 -0
- package/emu198x_spectrum_web.d.ts +142 -4
- package/emu198x_spectrum_web.js +290 -3
- package/emu198x_spectrum_web_bg.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,3 +83,78 @@ machines, or Spectrum clones.
|
|
|
83
83
|
The emulator is licensed under the terms in the Emu198x repository. The ROM is
|
|
84
84
|
copyright Amstrad plc and is redistributed under the permission above; it is
|
|
85
85
|
not covered by that licence.
|
|
86
|
+
|
|
87
|
+
### Running inside a Web Worker
|
|
88
|
+
|
|
89
|
+
`Spectrum.createHeadless(rom)` and, in a firmware-bundled build,
|
|
90
|
+
`Spectrum.createHeadlessBundled()` create the same 48K machine without a DOM
|
|
91
|
+
canvas. Run `autoload()` and `tick(elapsedMs)` in the worker, then transfer the
|
|
92
|
+
`frameRgba()` buffer and `frameSize()` dimensions to the page for presentation.
|
|
93
|
+
The existing canvas-based constructors remain available.
|
|
94
|
+
|
|
95
|
+
Boot and turbo tape loading are synchronous operations. Keeping them in a worker
|
|
96
|
+
prevents those operations from blocking editing and other page interactions; it
|
|
97
|
+
does not remove their emulation cost. Keep at most one tick request in flight,
|
|
98
|
+
terminate the previous worker when starting a new run, and pause requests when
|
|
99
|
+
the page is hidden. The caller owns worker lifecycle, frame transfer and audio
|
|
100
|
+
presentation. No snapshot injection or tape-loading shortcut is implied.
|
|
101
|
+
|
|
102
|
+
## Numbered BASIC listings
|
|
103
|
+
|
|
104
|
+
The additive `basicTape(source, name)` export converts a numbered ASCII listing
|
|
105
|
+
into a self-starting BASIC TAP. It uses the format crate's `tokenise_listing`
|
|
106
|
+
path and the shared TAP writer, preserving expression text rather than passing
|
|
107
|
+
it through the analysis AST. Line numbers are sorted; duplicate line numbers,
|
|
108
|
+
empty programs, unsupported characters, unterminated strings and out-of-range
|
|
109
|
+
numbers return errors. This is tokenisation, not a BASIC grammar validator:
|
|
110
|
+
the ROM still reports execution and syntax errors when the tape runs.
|
|
111
|
+
|
|
112
|
+
This route is currently a bounded Code198x browser trial (Meet BASIC greeting
|
|
113
|
+
and Sonar). DEF FN parameter markers, embedded graphics/control codes and broader
|
|
114
|
+
source-notation compatibility remain outside it. The listing and headless worker APIs are available from npm version 0.4.0.
|
|
115
|
+
|
|
116
|
+
### Direct program execution
|
|
117
|
+
|
|
118
|
+
On a fresh 48K instance, `runBasic(source)` installs the tokenised listing in
|
|
119
|
+
RAM through the shared native BASIC loader, updates its system variables, and
|
|
120
|
+
types RUN through the ROM. It mounts no tape. `basicTape(source, name)` remains
|
|
121
|
+
the independent download/export path.
|
|
122
|
+
|
|
123
|
+
`runCode(bytes, origin, entry)` boots the ROM, installs the machine code and a
|
|
124
|
+
small BASIC launcher, then executes CLEAR followed by RANDOMIZE USR. This
|
|
125
|
+
preserves ROM services and a return stack. Code must start at or above 24576,
|
|
126
|
+
fit below 65536, and contain its entry point. Use a fresh instance for each run.
|
|
127
|
+
Both APIs are synchronous: worker hosts keep boot work off the page thread.
|
|
128
|
+
These APIs are available from npm version 0.4.0.
|
|
129
|
+
|
|
130
|
+
`readMemory(address, length)` reads the current visible address space for lesson
|
|
131
|
+
inspectors. It is read-only and rejects ranges outside the 64 KiB address space.
|
|
132
|
+
This API is available from npm version 0.4.0.
|
|
133
|
+
|
|
134
|
+
For lesson replay, call `enableScreenWriteTrace(address, length)` on a fresh machine before
|
|
135
|
+
`runCode()`, then read `screenWriteTrace()`. Choose a narrow bitmap range to avoid filling the capture with ROM clearing.
|
|
136
|
+
It returns captured writes within that range
|
|
137
|
+
(including ROM writes) and a `full` flag when the shared 8192-record cap is
|
|
138
|
+
reached. The caller can filter by program PC range. A replay is a recording,
|
|
139
|
+
not live stepping; a full capture must not be presented as complete. These
|
|
140
|
+
APIs are available from npm version 0.4.0.
|
|
141
|
+
|
|
142
|
+
For the bounded routine lesson, call `enableRoutineTrace(stop)` before
|
|
143
|
+
`runCode()`, then `routineTrace()`. The JSON recording contains actual unconditional
|
|
144
|
+
CALL/RET transitions and bitmap writes in $4000–$47FF, with before/after register
|
|
145
|
+
snapshots. Existing debugger stepping stops at the supplied PC, on leaving the
|
|
146
|
+
program, or after 4096 steps. Check `complete`: only reaching the supplied stop
|
|
147
|
+
address makes it true. The intended stop is the lesson's `hold` label. This is
|
|
148
|
+
bounded teaching instrumentation, not a general instruction trace. It is available
|
|
149
|
+
from npm version 0.4.0.
|
|
150
|
+
|
|
151
|
+
### Debugger controls
|
|
152
|
+
|
|
153
|
+
`debugState()` serialises shared debugger CPU state and disassembly/bytes at PC.
|
|
154
|
+
`debugStep()` invokes the existing bounded native step; `debugRunTo(address)`
|
|
155
|
+
runs to an instruction boundary with a fixed 14-million-half-cycle budget and
|
|
156
|
+
returns whether it reached the address. Invalid addresses are rejected. Both
|
|
157
|
+
execution methods deliver queued input first. Suspend normal frame ticks before
|
|
158
|
+
using them; no implicit rendering or real-time playback runs while paused.
|
|
159
|
+
A failed run-to is not a breakpoint hit, and a step at HALT can remain waiting.
|
|
160
|
+
These exports are available from npm version 0.4.0.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/* eslint-disable */
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* A ZX Spectrum
|
|
5
|
+
* A ZX Spectrum with optional canvas presentation.
|
|
6
6
|
*/
|
|
7
7
|
export class Spectrum {
|
|
8
8
|
private constructor();
|
|
@@ -73,6 +73,61 @@ export class Spectrum {
|
|
|
73
73
|
* Returns a JavaScript error if the canvas has no 2-D context.
|
|
74
74
|
*/
|
|
75
75
|
static createBundled(canvas: HTMLCanvasElement): Promise<Spectrum>;
|
|
76
|
+
/**
|
|
77
|
+
* Builds a canvas-free 48K suitable for a Web Worker.
|
|
78
|
+
*
|
|
79
|
+
* Use `tick`, `frameSize` and `frameRgba` to run the same machine and
|
|
80
|
+
* transfer completed frames to a presenter. No DOM object is accessed.
|
|
81
|
+
*
|
|
82
|
+
* # Errors
|
|
83
|
+
*
|
|
84
|
+
* Returns an error if the supplied firmware cannot build a 48K machine.
|
|
85
|
+
*/
|
|
86
|
+
static createHeadless(rom: Uint8Array): Spectrum;
|
|
87
|
+
/**
|
|
88
|
+
* Builds a canvas-free 48K with the package's bundled ROM.
|
|
89
|
+
*
|
|
90
|
+
* # Errors
|
|
91
|
+
*
|
|
92
|
+
* Returns an error if the bundled firmware cannot build the machine.
|
|
93
|
+
*/
|
|
94
|
+
static createHeadlessBundled(): Spectrum;
|
|
95
|
+
/**
|
|
96
|
+
* Run to an instruction boundary, bounded to 14 million half-cycles.
|
|
97
|
+
* Suspend the host frame loop before calling this method.
|
|
98
|
+
*
|
|
99
|
+
* # Errors
|
|
100
|
+
* Rejects invalid addresses and input delivery errors.
|
|
101
|
+
*/
|
|
102
|
+
debugRunTo(address: number): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Inspect shared debugger registers and disassembly at PC.
|
|
105
|
+
*
|
|
106
|
+
* # Errors
|
|
107
|
+
* Returns an error if state cannot be serialised.
|
|
108
|
+
*/
|
|
109
|
+
debugState(): string;
|
|
110
|
+
/**
|
|
111
|
+
* Use the existing bounded native step; suspend the host frame loop first.
|
|
112
|
+
*
|
|
113
|
+
* # Errors
|
|
114
|
+
* Returns an error if pending input cannot be delivered.
|
|
115
|
+
*/
|
|
116
|
+
debugStep(): bigint;
|
|
117
|
+
/**
|
|
118
|
+
* Enables a bounded debugger recording of CALL nn, RET and bitmap writes,
|
|
119
|
+
* stopping before the named hold address. Use on a fresh machine.
|
|
120
|
+
*/
|
|
121
|
+
enableRoutineTrace(stop: number): void;
|
|
122
|
+
/**
|
|
123
|
+
* Records writes in a selected bitmap range during the next direct code run.
|
|
124
|
+
* Call on a fresh machine. Narrow ranges leave room for the program's writes
|
|
125
|
+
* after the ROM clears the display.
|
|
126
|
+
*
|
|
127
|
+
* # Errors
|
|
128
|
+
* Rejects empty ranges or ranges outside bitmap RAM ($4000..$5800).
|
|
129
|
+
*/
|
|
130
|
+
enableScreenWriteTrace(address: number, length: number): void;
|
|
76
131
|
/**
|
|
77
132
|
* The machine's picture as RGBA bytes, for a page that wants to present
|
|
78
133
|
* it itself.
|
|
@@ -118,10 +173,71 @@ export class Spectrum {
|
|
|
118
173
|
* parse.
|
|
119
174
|
*/
|
|
120
175
|
loadSnapshot(bytes: Uint8Array, format: string): void;
|
|
176
|
+
mediaSlots(): string[];
|
|
121
177
|
/**
|
|
122
178
|
* The machine's media slots, for a page that wants to name one.
|
|
179
|
+
* Asks the machine a question, and hands back the answer as JSON.
|
|
180
|
+
*
|
|
181
|
+
* The same query surface the headless session and the MCP server use, so
|
|
182
|
+
* a page sees what a script sees rather than a browser-only subset. The
|
|
183
|
+
* paths a Spectrum answers include `cpu.pc`, `cpu.halted`, `cpu.iff1`,
|
|
184
|
+
* `cpu.instructions_retired`, `screen.text.lines`, `tape.playing` and
|
|
185
|
+
* `boot.detected`.
|
|
186
|
+
*
|
|
187
|
+
* This is what lets a lesson say *why* a machine stopped rather than
|
|
188
|
+
* offering a reset and moving on: a program that ran past its own last
|
|
189
|
+
* instruction has a `cpu.pc` outside the bytes it was assembled into, and
|
|
190
|
+
* one that halted with interrupts disabled is `cpu.halted` with
|
|
191
|
+
* `cpu.iff1` false. Both are mistakes a unit is teaching against.
|
|
192
|
+
*
|
|
193
|
+
* JSON rather than a native value: the answers are already JSON inside
|
|
194
|
+
* the query layer, and a page parses one string more cheaply than this
|
|
195
|
+
* crate grows a serialisation dependency.
|
|
196
|
+
*
|
|
197
|
+
* # Errors
|
|
198
|
+
*
|
|
199
|
+
* Returns a JavaScript error if the machine does not know the path.
|
|
123
200
|
*/
|
|
124
|
-
|
|
201
|
+
query(path: string): string;
|
|
202
|
+
/**
|
|
203
|
+
* Reads visible memory for a lesson's live inspection panel.
|
|
204
|
+
*
|
|
205
|
+
* # Errors
|
|
206
|
+
* Rejects ranges extending beyond the 64 KiB address space.
|
|
207
|
+
*/
|
|
208
|
+
readMemory(address: number, length: number): Uint8Array;
|
|
209
|
+
/**
|
|
210
|
+
* Returns the executed routine events and whether the stop was reached.
|
|
211
|
+
*
|
|
212
|
+
* # Errors
|
|
213
|
+
* Returns a JSON serialisation error if the recording cannot be encoded.
|
|
214
|
+
*/
|
|
215
|
+
routineTrace(): string;
|
|
216
|
+
/**
|
|
217
|
+
* Installs a numbered BASIC listing directly into RAM and asks the ROM
|
|
218
|
+
* to RUN it. Call on a fresh 48K; no tape is mounted or played.
|
|
219
|
+
*
|
|
220
|
+
* # Errors
|
|
221
|
+
* Returns conversion, boot or editor-prompt errors.
|
|
222
|
+
*/
|
|
223
|
+
runBasic(source: string): void;
|
|
224
|
+
/**
|
|
225
|
+
* Installs machine code in a fresh 48K and calls it through the ROM's
|
|
226
|
+
* RANDOMIZE USR, with CLEAR reserving its RAM and a valid return stack.
|
|
227
|
+
*
|
|
228
|
+
* # Errors
|
|
229
|
+
* Rejects empty code, overlap with BASIC/system RAM, overflow, entry
|
|
230
|
+
* outside the code, and ROM boot or prompt failures.
|
|
231
|
+
*/
|
|
232
|
+
runCode(bytes: Uint8Array, origin: number, entry: number): void;
|
|
233
|
+
/**
|
|
234
|
+
* Returns captured writes (including ROM writes) and saturation status.
|
|
235
|
+
* Consumers can select the program's PC range without inventing a trace.
|
|
236
|
+
*
|
|
237
|
+
* # Errors
|
|
238
|
+
* Returns a serialisation error if the capture cannot be encoded.
|
|
239
|
+
*/
|
|
240
|
+
screenWriteTrace(): string;
|
|
125
241
|
/**
|
|
126
242
|
* Starts or stops machine audio.
|
|
127
243
|
*/
|
|
@@ -146,16 +262,32 @@ export class Spectrum {
|
|
|
146
262
|
tick(elapsed_ms: number): number;
|
|
147
263
|
}
|
|
148
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Tokenise source and create an auto-starting BASIC TAP, without a machine.
|
|
267
|
+
*
|
|
268
|
+
* # Errors
|
|
269
|
+
* Returns a JavaScript error for unsupported or malformed listing input.
|
|
270
|
+
*/
|
|
271
|
+
export function basicTape(source: string, name: string): Uint8Array;
|
|
272
|
+
|
|
149
273
|
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
150
274
|
|
|
151
275
|
export interface InitOutput {
|
|
152
276
|
readonly memory: WebAssembly.Memory;
|
|
153
277
|
readonly __wbg_spectrum_free: (a: number, b: number) => void;
|
|
278
|
+
readonly basicTape: (a: number, b: number, c: number, d: number) => [number, number, number, number];
|
|
154
279
|
readonly spectrum_audioDrain: (a: number) => [number, number];
|
|
155
280
|
readonly spectrum_autoload: (a: number, b: number) => [number, number, number];
|
|
156
281
|
readonly spectrum_configureAudio: (a: number, b: number, c: number, d: number) => void;
|
|
157
282
|
readonly spectrum_create: (a: any, b: number, c: number) => any;
|
|
158
283
|
readonly spectrum_createBundled: (a: any) => any;
|
|
284
|
+
readonly spectrum_createHeadless: (a: number, b: number) => [number, number, number];
|
|
285
|
+
readonly spectrum_createHeadlessBundled: () => [number, number, number];
|
|
286
|
+
readonly spectrum_debugRunTo: (a: number, b: number) => [number, number, number];
|
|
287
|
+
readonly spectrum_debugState: (a: number) => [number, number, number, number];
|
|
288
|
+
readonly spectrum_debugStep: (a: number) => [bigint, number, number];
|
|
289
|
+
readonly spectrum_enableRoutineTrace: (a: number, b: number) => void;
|
|
290
|
+
readonly spectrum_enableScreenWriteTrace: (a: number, b: number, c: number) => [number, number];
|
|
159
291
|
readonly spectrum_frameRgba: (a: number) => [number, number];
|
|
160
292
|
readonly spectrum_frameSize: (a: number) => [number, number];
|
|
161
293
|
readonly spectrum_keyDown: (a: number, b: number, c: number) => number;
|
|
@@ -163,6 +295,12 @@ export interface InitOutput {
|
|
|
163
295
|
readonly spectrum_load: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number];
|
|
164
296
|
readonly spectrum_loadSnapshot: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
165
297
|
readonly spectrum_mediaSlots: (a: number) => [number, number];
|
|
298
|
+
readonly spectrum_query: (a: number, b: number, c: number) => [number, number, number, number];
|
|
299
|
+
readonly spectrum_readMemory: (a: number, b: number, c: number) => [number, number, number, number];
|
|
300
|
+
readonly spectrum_routineTrace: (a: number) => [number, number, number, number];
|
|
301
|
+
readonly spectrum_runBasic: (a: number, b: number, c: number) => [number, number];
|
|
302
|
+
readonly spectrum_runCode: (a: number, b: number, c: number, d: number, e: number) => [number, number];
|
|
303
|
+
readonly spectrum_screenWriteTrace: (a: number) => [number, number, number, number];
|
|
166
304
|
readonly spectrum_setAudioEnabled: (a: number, b: number) => void;
|
|
167
305
|
readonly spectrum_tick: (a: number, b: number) => [number, number, number];
|
|
168
306
|
readonly wasm_bindgen_d6cb7d81ec28c0ae___convert__closures_____invoke___wasm_bindgen_d6cb7d81ec28c0ae___JsValue__core_f0fd674eaa06beef___result__Result_____wasm_bindgen_d6cb7d81ec28c0ae___JsError___true_: (a: number, b: number, c: any) => [number, number];
|
|
@@ -171,10 +309,10 @@ export interface InitOutput {
|
|
|
171
309
|
readonly __externref_table_alloc: () => number;
|
|
172
310
|
readonly __wbindgen_externrefs: WebAssembly.Table;
|
|
173
311
|
readonly __wbindgen_destroy_closure: (a: number, b: number) => void;
|
|
174
|
-
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
175
|
-
readonly __externref_table_dealloc: (a: number) => void;
|
|
176
312
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
177
313
|
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
314
|
+
readonly __externref_table_dealloc: (a: number) => void;
|
|
315
|
+
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
178
316
|
readonly __externref_drop_slice: (a: number, b: number) => void;
|
|
179
317
|
readonly __wbindgen_start: () => void;
|
|
180
318
|
}
|
package/emu198x_spectrum_web.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* @ts-self-types="./emu198x_spectrum_web.d.ts" */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* A ZX Spectrum
|
|
4
|
+
* A ZX Spectrum with optional canvas presentation.
|
|
5
5
|
*/
|
|
6
6
|
export class Spectrum {
|
|
7
7
|
static __wrap(ptr) {
|
|
@@ -117,6 +117,121 @@ export class Spectrum {
|
|
|
117
117
|
const ret = wasm.spectrum_createBundled(canvas);
|
|
118
118
|
return ret;
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Builds a canvas-free 48K suitable for a Web Worker.
|
|
122
|
+
*
|
|
123
|
+
* Use `tick`, `frameSize` and `frameRgba` to run the same machine and
|
|
124
|
+
* transfer completed frames to a presenter. No DOM object is accessed.
|
|
125
|
+
*
|
|
126
|
+
* # Errors
|
|
127
|
+
*
|
|
128
|
+
* Returns an error if the supplied firmware cannot build a 48K machine.
|
|
129
|
+
* @param {Uint8Array} rom
|
|
130
|
+
* @returns {Spectrum}
|
|
131
|
+
*/
|
|
132
|
+
static createHeadless(rom) {
|
|
133
|
+
const ptr0 = passArray8ToWasm0(rom, wasm.__wbindgen_malloc);
|
|
134
|
+
const len0 = WASM_VECTOR_LEN;
|
|
135
|
+
const ret = wasm.spectrum_createHeadless(ptr0, len0);
|
|
136
|
+
if (ret[2]) {
|
|
137
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
138
|
+
}
|
|
139
|
+
return Spectrum.__wrap(ret[0]);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Builds a canvas-free 48K with the package's bundled ROM.
|
|
143
|
+
*
|
|
144
|
+
* # Errors
|
|
145
|
+
*
|
|
146
|
+
* Returns an error if the bundled firmware cannot build the machine.
|
|
147
|
+
* @returns {Spectrum}
|
|
148
|
+
*/
|
|
149
|
+
static createHeadlessBundled() {
|
|
150
|
+
const ret = wasm.spectrum_createHeadlessBundled();
|
|
151
|
+
if (ret[2]) {
|
|
152
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
153
|
+
}
|
|
154
|
+
return Spectrum.__wrap(ret[0]);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Run to an instruction boundary, bounded to 14 million half-cycles.
|
|
158
|
+
* Suspend the host frame loop before calling this method.
|
|
159
|
+
*
|
|
160
|
+
* # Errors
|
|
161
|
+
* Rejects invalid addresses and input delivery errors.
|
|
162
|
+
* @param {number} address
|
|
163
|
+
* @returns {boolean}
|
|
164
|
+
*/
|
|
165
|
+
debugRunTo(address) {
|
|
166
|
+
const ret = wasm.spectrum_debugRunTo(this.__wbg_ptr, address);
|
|
167
|
+
if (ret[2]) {
|
|
168
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
169
|
+
}
|
|
170
|
+
return ret[0] !== 0;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Inspect shared debugger registers and disassembly at PC.
|
|
174
|
+
*
|
|
175
|
+
* # Errors
|
|
176
|
+
* Returns an error if state cannot be serialised.
|
|
177
|
+
* @returns {string}
|
|
178
|
+
*/
|
|
179
|
+
debugState() {
|
|
180
|
+
let deferred2_0;
|
|
181
|
+
let deferred2_1;
|
|
182
|
+
try {
|
|
183
|
+
const ret = wasm.spectrum_debugState(this.__wbg_ptr);
|
|
184
|
+
var ptr1 = ret[0];
|
|
185
|
+
var len1 = ret[1];
|
|
186
|
+
if (ret[3]) {
|
|
187
|
+
ptr1 = 0; len1 = 0;
|
|
188
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
189
|
+
}
|
|
190
|
+
deferred2_0 = ptr1;
|
|
191
|
+
deferred2_1 = len1;
|
|
192
|
+
return getStringFromWasm0(ptr1, len1);
|
|
193
|
+
} finally {
|
|
194
|
+
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Use the existing bounded native step; suspend the host frame loop first.
|
|
199
|
+
*
|
|
200
|
+
* # Errors
|
|
201
|
+
* Returns an error if pending input cannot be delivered.
|
|
202
|
+
* @returns {bigint}
|
|
203
|
+
*/
|
|
204
|
+
debugStep() {
|
|
205
|
+
const ret = wasm.spectrum_debugStep(this.__wbg_ptr);
|
|
206
|
+
if (ret[2]) {
|
|
207
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
208
|
+
}
|
|
209
|
+
return BigInt.asUintN(64, ret[0]);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Enables a bounded debugger recording of CALL nn, RET and bitmap writes,
|
|
213
|
+
* stopping before the named hold address. Use on a fresh machine.
|
|
214
|
+
* @param {number} stop
|
|
215
|
+
*/
|
|
216
|
+
enableRoutineTrace(stop) {
|
|
217
|
+
wasm.spectrum_enableRoutineTrace(this.__wbg_ptr, stop);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Records writes in a selected bitmap range during the next direct code run.
|
|
221
|
+
* Call on a fresh machine. Narrow ranges leave room for the program's writes
|
|
222
|
+
* after the ROM clears the display.
|
|
223
|
+
*
|
|
224
|
+
* # Errors
|
|
225
|
+
* Rejects empty ranges or ranges outside bitmap RAM ($4000..$5800).
|
|
226
|
+
* @param {number} address
|
|
227
|
+
* @param {number} length
|
|
228
|
+
*/
|
|
229
|
+
enableScreenWriteTrace(address, length) {
|
|
230
|
+
const ret = wasm.spectrum_enableScreenWriteTrace(this.__wbg_ptr, address, length);
|
|
231
|
+
if (ret[1]) {
|
|
232
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
120
235
|
/**
|
|
121
236
|
* The machine's picture as RGBA bytes, for a page that wants to present
|
|
122
237
|
* it itself.
|
|
@@ -214,7 +329,6 @@ export class Spectrum {
|
|
|
214
329
|
}
|
|
215
330
|
}
|
|
216
331
|
/**
|
|
217
|
-
* The machine's media slots, for a page that wants to name one.
|
|
218
332
|
* @returns {string[]}
|
|
219
333
|
*/
|
|
220
334
|
mediaSlots() {
|
|
@@ -223,6 +337,156 @@ export class Spectrum {
|
|
|
223
337
|
wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
|
|
224
338
|
return v1;
|
|
225
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* The machine's media slots, for a page that wants to name one.
|
|
342
|
+
* Asks the machine a question, and hands back the answer as JSON.
|
|
343
|
+
*
|
|
344
|
+
* The same query surface the headless session and the MCP server use, so
|
|
345
|
+
* a page sees what a script sees rather than a browser-only subset. The
|
|
346
|
+
* paths a Spectrum answers include `cpu.pc`, `cpu.halted`, `cpu.iff1`,
|
|
347
|
+
* `cpu.instructions_retired`, `screen.text.lines`, `tape.playing` and
|
|
348
|
+
* `boot.detected`.
|
|
349
|
+
*
|
|
350
|
+
* This is what lets a lesson say *why* a machine stopped rather than
|
|
351
|
+
* offering a reset and moving on: a program that ran past its own last
|
|
352
|
+
* instruction has a `cpu.pc` outside the bytes it was assembled into, and
|
|
353
|
+
* one that halted with interrupts disabled is `cpu.halted` with
|
|
354
|
+
* `cpu.iff1` false. Both are mistakes a unit is teaching against.
|
|
355
|
+
*
|
|
356
|
+
* JSON rather than a native value: the answers are already JSON inside
|
|
357
|
+
* the query layer, and a page parses one string more cheaply than this
|
|
358
|
+
* crate grows a serialisation dependency.
|
|
359
|
+
*
|
|
360
|
+
* # Errors
|
|
361
|
+
*
|
|
362
|
+
* Returns a JavaScript error if the machine does not know the path.
|
|
363
|
+
* @param {string} path
|
|
364
|
+
* @returns {string}
|
|
365
|
+
*/
|
|
366
|
+
query(path) {
|
|
367
|
+
let deferred3_0;
|
|
368
|
+
let deferred3_1;
|
|
369
|
+
try {
|
|
370
|
+
const ptr0 = passStringToWasm0(path, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
371
|
+
const len0 = WASM_VECTOR_LEN;
|
|
372
|
+
const ret = wasm.spectrum_query(this.__wbg_ptr, ptr0, len0);
|
|
373
|
+
var ptr2 = ret[0];
|
|
374
|
+
var len2 = ret[1];
|
|
375
|
+
if (ret[3]) {
|
|
376
|
+
ptr2 = 0; len2 = 0;
|
|
377
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
378
|
+
}
|
|
379
|
+
deferred3_0 = ptr2;
|
|
380
|
+
deferred3_1 = len2;
|
|
381
|
+
return getStringFromWasm0(ptr2, len2);
|
|
382
|
+
} finally {
|
|
383
|
+
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Reads visible memory for a lesson's live inspection panel.
|
|
388
|
+
*
|
|
389
|
+
* # Errors
|
|
390
|
+
* Rejects ranges extending beyond the 64 KiB address space.
|
|
391
|
+
* @param {number} address
|
|
392
|
+
* @param {number} length
|
|
393
|
+
* @returns {Uint8Array}
|
|
394
|
+
*/
|
|
395
|
+
readMemory(address, length) {
|
|
396
|
+
const ret = wasm.spectrum_readMemory(this.__wbg_ptr, address, length);
|
|
397
|
+
if (ret[3]) {
|
|
398
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
399
|
+
}
|
|
400
|
+
var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
401
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
402
|
+
return v1;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Returns the executed routine events and whether the stop was reached.
|
|
406
|
+
*
|
|
407
|
+
* # Errors
|
|
408
|
+
* Returns a JSON serialisation error if the recording cannot be encoded.
|
|
409
|
+
* @returns {string}
|
|
410
|
+
*/
|
|
411
|
+
routineTrace() {
|
|
412
|
+
let deferred2_0;
|
|
413
|
+
let deferred2_1;
|
|
414
|
+
try {
|
|
415
|
+
const ret = wasm.spectrum_routineTrace(this.__wbg_ptr);
|
|
416
|
+
var ptr1 = ret[0];
|
|
417
|
+
var len1 = ret[1];
|
|
418
|
+
if (ret[3]) {
|
|
419
|
+
ptr1 = 0; len1 = 0;
|
|
420
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
421
|
+
}
|
|
422
|
+
deferred2_0 = ptr1;
|
|
423
|
+
deferred2_1 = len1;
|
|
424
|
+
return getStringFromWasm0(ptr1, len1);
|
|
425
|
+
} finally {
|
|
426
|
+
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Installs a numbered BASIC listing directly into RAM and asks the ROM
|
|
431
|
+
* to RUN it. Call on a fresh 48K; no tape is mounted or played.
|
|
432
|
+
*
|
|
433
|
+
* # Errors
|
|
434
|
+
* Returns conversion, boot or editor-prompt errors.
|
|
435
|
+
* @param {string} source
|
|
436
|
+
*/
|
|
437
|
+
runBasic(source) {
|
|
438
|
+
const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
439
|
+
const len0 = WASM_VECTOR_LEN;
|
|
440
|
+
const ret = wasm.spectrum_runBasic(this.__wbg_ptr, ptr0, len0);
|
|
441
|
+
if (ret[1]) {
|
|
442
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Installs machine code in a fresh 48K and calls it through the ROM's
|
|
447
|
+
* RANDOMIZE USR, with CLEAR reserving its RAM and a valid return stack.
|
|
448
|
+
*
|
|
449
|
+
* # Errors
|
|
450
|
+
* Rejects empty code, overlap with BASIC/system RAM, overflow, entry
|
|
451
|
+
* outside the code, and ROM boot or prompt failures.
|
|
452
|
+
* @param {Uint8Array} bytes
|
|
453
|
+
* @param {number} origin
|
|
454
|
+
* @param {number} entry
|
|
455
|
+
*/
|
|
456
|
+
runCode(bytes, origin, entry) {
|
|
457
|
+
const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc);
|
|
458
|
+
const len0 = WASM_VECTOR_LEN;
|
|
459
|
+
const ret = wasm.spectrum_runCode(this.__wbg_ptr, ptr0, len0, origin, entry);
|
|
460
|
+
if (ret[1]) {
|
|
461
|
+
throw takeFromExternrefTable0(ret[0]);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Returns captured writes (including ROM writes) and saturation status.
|
|
466
|
+
* Consumers can select the program's PC range without inventing a trace.
|
|
467
|
+
*
|
|
468
|
+
* # Errors
|
|
469
|
+
* Returns a serialisation error if the capture cannot be encoded.
|
|
470
|
+
* @returns {string}
|
|
471
|
+
*/
|
|
472
|
+
screenWriteTrace() {
|
|
473
|
+
let deferred2_0;
|
|
474
|
+
let deferred2_1;
|
|
475
|
+
try {
|
|
476
|
+
const ret = wasm.spectrum_screenWriteTrace(this.__wbg_ptr);
|
|
477
|
+
var ptr1 = ret[0];
|
|
478
|
+
var len1 = ret[1];
|
|
479
|
+
if (ret[3]) {
|
|
480
|
+
ptr1 = 0; len1 = 0;
|
|
481
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
482
|
+
}
|
|
483
|
+
deferred2_0 = ptr1;
|
|
484
|
+
deferred2_1 = len1;
|
|
485
|
+
return getStringFromWasm0(ptr1, len1);
|
|
486
|
+
} finally {
|
|
487
|
+
wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
226
490
|
/**
|
|
227
491
|
* Starts or stops machine audio.
|
|
228
492
|
* @param {boolean} enabled
|
|
@@ -258,6 +522,29 @@ export class Spectrum {
|
|
|
258
522
|
}
|
|
259
523
|
}
|
|
260
524
|
if (Symbol.dispose) Spectrum.prototype[Symbol.dispose] = Spectrum.prototype.free;
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Tokenise source and create an auto-starting BASIC TAP, without a machine.
|
|
528
|
+
*
|
|
529
|
+
* # Errors
|
|
530
|
+
* Returns a JavaScript error for unsupported or malformed listing input.
|
|
531
|
+
* @param {string} source
|
|
532
|
+
* @param {string} name
|
|
533
|
+
* @returns {Uint8Array}
|
|
534
|
+
*/
|
|
535
|
+
export function basicTape(source, name) {
|
|
536
|
+
const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
537
|
+
const len0 = WASM_VECTOR_LEN;
|
|
538
|
+
const ptr1 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
539
|
+
const len1 = WASM_VECTOR_LEN;
|
|
540
|
+
const ret = wasm.basicTape(ptr0, len0, ptr1, len1);
|
|
541
|
+
if (ret[3]) {
|
|
542
|
+
throw takeFromExternrefTable0(ret[2]);
|
|
543
|
+
}
|
|
544
|
+
var v3 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
|
|
545
|
+
wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
|
|
546
|
+
return v3;
|
|
547
|
+
}
|
|
261
548
|
function __wbg_get_imports() {
|
|
262
549
|
const import0 = {
|
|
263
550
|
__proto__: null,
|
|
@@ -372,7 +659,7 @@ function __wbg_get_imports() {
|
|
|
372
659
|
return ret;
|
|
373
660
|
},
|
|
374
661
|
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
|
375
|
-
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx:
|
|
662
|
+
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 30, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
|
376
663
|
const ret = makeMutClosure(arg0, arg1, wasm_bindgen_d6cb7d81ec28c0ae___convert__closures_____invoke___wasm_bindgen_d6cb7d81ec28c0ae___JsValue__core_f0fd674eaa06beef___result__Result_____wasm_bindgen_d6cb7d81ec28c0ae___JsError___true_);
|
|
377
664
|
return ret;
|
|
378
665
|
},
|
|
Binary file
|
package/package.json
CHANGED