@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 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 attached to a canvas.
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
- mediaSlots(): string[];
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
  }
@@ -1,7 +1,7 @@
1
1
  /* @ts-self-types="./emu198x_spectrum_web.d.ts" */
2
2
 
3
3
  /**
4
- * A ZX Spectrum attached to a canvas.
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: 21, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
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
@@ -2,7 +2,7 @@
2
2
  "name": "@emu198x/zx-spectrum",
3
3
  "type": "module",
4
4
  "description": "ZX Spectrum in the browser, published to npm as @emu198x/zx-spectrum",
5
- "version": "0.2.0",
5
+ "version": "0.4.0",
6
6
  "license": "SEE LICENSE IN README.md",
7
7
  "repository": {
8
8
  "type": "git",