@emu198x/zx-spectrum 0.3.0 → 0.4.1

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
@@ -73,7 +73,10 @@ The permission is Cliff Lawson's, for Amstrad plc, on comp.sys.sinclair,
73
73
  31 August 1999:
74
74
  <https://web.archive.org/web/20180828125931/http://www.worldofspectrum.org/permits/amstrad-roms.txt>
75
75
 
76
- The ROM image is included unmodified, and no charge is made for it. The
76
+ The ROM image is included unmodified, and no charge is made for it. Versions
77
+ 0.1.0 to 0.4.0 carried a 48K image with 272 bytes changed in the ROM's unused
78
+ area, from address `0x386E`; use 0.4.1 or later, which carries the unmodified
79
+ image (SHA1 `5ea7c2b824672e914525d1d5c419d71b84a426a2`). The
77
80
  permission covers the Sinclair 48K and 128K ROMs and Amstrad's +2/+2A/+3
78
81
  machines. It does not extend to the ZX80, ZX81, Interface 1 or 2, Timex
79
82
  machines, or Spectrum clones.
@@ -83,3 +86,78 @@ machines, or Spectrum clones.
83
86
  The emulator is licensed under the terms in the Emu198x repository. The ROM is
84
87
  copyright Amstrad plc and is redistributed under the permission above; it is
85
88
  not covered by that licence.
89
+
90
+ ### Running inside a Web Worker
91
+
92
+ `Spectrum.createHeadless(rom)` and, in a firmware-bundled build,
93
+ `Spectrum.createHeadlessBundled()` create the same 48K machine without a DOM
94
+ canvas. Run `autoload()` and `tick(elapsedMs)` in the worker, then transfer the
95
+ `frameRgba()` buffer and `frameSize()` dimensions to the page for presentation.
96
+ The existing canvas-based constructors remain available.
97
+
98
+ Boot and turbo tape loading are synchronous operations. Keeping them in a worker
99
+ prevents those operations from blocking editing and other page interactions; it
100
+ does not remove their emulation cost. Keep at most one tick request in flight,
101
+ terminate the previous worker when starting a new run, and pause requests when
102
+ the page is hidden. The caller owns worker lifecycle, frame transfer and audio
103
+ presentation. No snapshot injection or tape-loading shortcut is implied.
104
+
105
+ ## Numbered BASIC listings
106
+
107
+ The additive `basicTape(source, name)` export converts a numbered ASCII listing
108
+ into a self-starting BASIC TAP. It uses the format crate's `tokenise_listing`
109
+ path and the shared TAP writer, preserving expression text rather than passing
110
+ it through the analysis AST. Line numbers are sorted; duplicate line numbers,
111
+ empty programs, unsupported characters, unterminated strings and out-of-range
112
+ numbers return errors. This is tokenisation, not a BASIC grammar validator:
113
+ the ROM still reports execution and syntax errors when the tape runs.
114
+
115
+ This route is currently a bounded Code198x browser trial (Meet BASIC greeting
116
+ and Sonar). DEF FN parameter markers, embedded graphics/control codes and broader
117
+ source-notation compatibility remain outside it. The listing and headless worker APIs are available from npm version 0.4.0.
118
+
119
+ ### Direct program execution
120
+
121
+ On a fresh 48K instance, `runBasic(source)` installs the tokenised listing in
122
+ RAM through the shared native BASIC loader, updates its system variables, and
123
+ types RUN through the ROM. It mounts no tape. `basicTape(source, name)` remains
124
+ the independent download/export path.
125
+
126
+ `runCode(bytes, origin, entry)` boots the ROM, installs the machine code and a
127
+ small BASIC launcher, then executes CLEAR followed by RANDOMIZE USR. This
128
+ preserves ROM services and a return stack. Code must start at or above 24576,
129
+ fit below 65536, and contain its entry point. Use a fresh instance for each run.
130
+ Both APIs are synchronous: worker hosts keep boot work off the page thread.
131
+ These APIs are available from npm version 0.4.0.
132
+
133
+ `readMemory(address, length)` reads the current visible address space for lesson
134
+ inspectors. It is read-only and rejects ranges outside the 64 KiB address space.
135
+ This API is available from npm version 0.4.0.
136
+
137
+ For lesson replay, call `enableScreenWriteTrace(address, length)` on a fresh machine before
138
+ `runCode()`, then read `screenWriteTrace()`. Choose a narrow bitmap range to avoid filling the capture with ROM clearing.
139
+ It returns captured writes within that range
140
+ (including ROM writes) and a `full` flag when the shared 8192-record cap is
141
+ reached. The caller can filter by program PC range. A replay is a recording,
142
+ not live stepping; a full capture must not be presented as complete. These
143
+ APIs are available from npm version 0.4.0.
144
+
145
+ For the bounded routine lesson, call `enableRoutineTrace(stop)` before
146
+ `runCode()`, then `routineTrace()`. The JSON recording contains actual unconditional
147
+ CALL/RET transitions and bitmap writes in $4000–$47FF, with before/after register
148
+ snapshots. Existing debugger stepping stops at the supplied PC, on leaving the
149
+ program, or after 4096 steps. Check `complete`: only reaching the supplied stop
150
+ address makes it true. The intended stop is the lesson's `hold` label. This is
151
+ bounded teaching instrumentation, not a general instruction trace. It is available
152
+ from npm version 0.4.0.
153
+
154
+ ### Debugger controls
155
+
156
+ `debugState()` serialises shared debugger CPU state and disassembly/bytes at PC.
157
+ `debugStep()` invokes the existing bounded native step; `debugRunTo(address)`
158
+ runs to an instruction boundary with a fixed 14-million-half-cycle budget and
159
+ returns whether it reached the address. Invalid addresses are rejected. Both
160
+ execution methods deliver queued input first. Suspend normal frame ticks before
161
+ using them; no implicit rendering or real-time playback runs while paused.
162
+ A failed run-to is not a breakpoint hit, and a step at HALT can remain waiting.
163
+ 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.
@@ -82,6 +137,10 @@ export class Spectrum {
82
137
  * Width and height of the machine's picture, as `[width, height]`.
83
138
  */
84
139
  frameSize(): Uint32Array;
140
+ /**
141
+ * Frame period derived from the machine profile.
142
+ */
143
+ frame_ms(): number;
85
144
  /**
86
145
  * Presses a key, from a DOM `KeyboardEvent.code`.
87
146
  *
@@ -144,10 +203,63 @@ export class Spectrum {
144
203
  * Returns a JavaScript error if the machine does not know the path.
145
204
  */
146
205
  query(path: string): string;
206
+ /**
207
+ * Reads visible memory for a lesson's live inspection panel.
208
+ *
209
+ * # Errors
210
+ * Rejects ranges extending beyond the 64 KiB address space.
211
+ */
212
+ readMemory(address: number, length: number): Uint8Array;
213
+ /**
214
+ * Restores compatible machine state, clearing pending host input/audio.
215
+ */
216
+ restore_state(bytes: Uint8Array): void;
217
+ /**
218
+ * Returns the executed routine events and whether the stop was reached.
219
+ *
220
+ * # Errors
221
+ * Returns a JSON serialisation error if the recording cannot be encoded.
222
+ */
223
+ routineTrace(): string;
224
+ /**
225
+ * Installs a numbered BASIC listing directly into RAM and asks the ROM
226
+ * to RUN it. Call on a fresh 48K; no tape is mounted or played.
227
+ *
228
+ * # Errors
229
+ * Returns conversion, boot or editor-prompt errors.
230
+ */
231
+ runBasic(source: string): void;
232
+ /**
233
+ * Installs machine code in a fresh 48K and calls it through the ROM's
234
+ * RANDOMIZE USR, with CLEAR reserving its RAM and a valid return stack.
235
+ *
236
+ * # Errors
237
+ * Rejects empty code, overlap with BASIC/system RAM, overflow, entry
238
+ * outside the code, and ROM boot or prompt failures.
239
+ */
240
+ runCode(bytes: Uint8Array, origin: number, entry: number): void;
241
+ /**
242
+ * Exports the current machine state.
243
+ */
244
+ save_state(): Uint8Array;
245
+ /**
246
+ * Returns captured writes (including ROM writes) and saturation status.
247
+ * Consumers can select the program's PC range without inventing a trace.
248
+ *
249
+ * # Errors
250
+ * Returns a serialisation error if the capture cannot be encoded.
251
+ */
252
+ screenWriteTrace(): string;
147
253
  /**
148
254
  * Starts or stops machine audio.
149
255
  */
150
256
  setAudioEnabled(enabled: boolean): void;
257
+ /**
258
+ * Advances exactly one frame without drawing to a canvas.
259
+ * # Errors
260
+ * Returns the runtime's execution error.
261
+ */
262
+ step(): void;
151
263
  /**
152
264
  * Runs the machine for `elapsed_ms` of real time and draws the result.
153
265
  *
@@ -168,25 +280,50 @@ export class Spectrum {
168
280
  tick(elapsed_ms: number): number;
169
281
  }
170
282
 
283
+ /**
284
+ * Tokenise source and create an auto-starting BASIC TAP, without a machine.
285
+ *
286
+ * # Errors
287
+ * Returns a JavaScript error for unsupported or malformed listing input.
288
+ */
289
+ export function basicTape(source: string, name: string): Uint8Array;
290
+
171
291
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
172
292
 
173
293
  export interface InitOutput {
174
294
  readonly memory: WebAssembly.Memory;
175
295
  readonly __wbg_spectrum_free: (a: number, b: number) => void;
296
+ readonly basicTape: (a: number, b: number, c: number, d: number) => [number, number, number, number];
176
297
  readonly spectrum_audioDrain: (a: number) => [number, number];
177
298
  readonly spectrum_autoload: (a: number, b: number) => [number, number, number];
178
299
  readonly spectrum_configureAudio: (a: number, b: number, c: number, d: number) => void;
179
300
  readonly spectrum_create: (a: any, b: number, c: number) => any;
180
301
  readonly spectrum_createBundled: (a: any) => any;
302
+ readonly spectrum_createHeadless: (a: number, b: number) => [number, number, number];
303
+ readonly spectrum_createHeadlessBundled: () => [number, number, number];
304
+ readonly spectrum_debugRunTo: (a: number, b: number) => [number, number, number];
305
+ readonly spectrum_debugState: (a: number) => [number, number, number, number];
306
+ readonly spectrum_debugStep: (a: number) => [bigint, number, number];
307
+ readonly spectrum_enableRoutineTrace: (a: number, b: number) => void;
308
+ readonly spectrum_enableScreenWriteTrace: (a: number, b: number, c: number) => [number, number];
181
309
  readonly spectrum_frameRgba: (a: number) => [number, number];
182
310
  readonly spectrum_frameSize: (a: number) => [number, number];
311
+ readonly spectrum_frame_ms: (a: number) => number;
183
312
  readonly spectrum_keyDown: (a: number, b: number, c: number) => number;
184
313
  readonly spectrum_keyUp: (a: number, b: number, c: number) => number;
185
314
  readonly spectrum_load: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number];
186
315
  readonly spectrum_loadSnapshot: (a: number, b: number, c: number, d: number, e: number) => [number, number];
187
316
  readonly spectrum_mediaSlots: (a: number) => [number, number];
188
317
  readonly spectrum_query: (a: number, b: number, c: number) => [number, number, number, number];
318
+ readonly spectrum_readMemory: (a: number, b: number, c: number) => [number, number, number, number];
319
+ readonly spectrum_restore_state: (a: number, b: number, c: number) => [number, number];
320
+ readonly spectrum_routineTrace: (a: number) => [number, number, number, number];
321
+ readonly spectrum_runBasic: (a: number, b: number, c: number) => [number, number];
322
+ readonly spectrum_runCode: (a: number, b: number, c: number, d: number, e: number) => [number, number];
323
+ readonly spectrum_save_state: (a: number) => [number, number, number, number];
324
+ readonly spectrum_screenWriteTrace: (a: number) => [number, number, number, number];
189
325
  readonly spectrum_setAudioEnabled: (a: number, b: number) => void;
326
+ readonly spectrum_step: (a: number) => [number, number];
190
327
  readonly spectrum_tick: (a: number, b: number) => [number, number, number];
191
328
  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];
192
329
  readonly wasm_bindgen_d6cb7d81ec28c0ae___convert__closures_____invoke___js_sys_e5668971ffde892f___Function_fn_wasm_bindgen_d6cb7d81ec28c0ae___JsValue_____wasm_bindgen_d6cb7d81ec28c0ae___sys__Undefined___js_sys_e5668971ffde892f___Function_fn_wasm_bindgen_d6cb7d81ec28c0ae___JsValue_____wasm_bindgen_d6cb7d81ec28c0ae___sys__Undefined_______true_: (a: number, b: number, c: any, d: any) => void;
@@ -194,10 +331,10 @@ export interface InitOutput {
194
331
  readonly __externref_table_alloc: () => number;
195
332
  readonly __wbindgen_externrefs: WebAssembly.Table;
196
333
  readonly __wbindgen_destroy_closure: (a: number, b: number) => void;
197
- readonly __wbindgen_free: (a: number, b: number, c: number) => void;
198
- readonly __externref_table_dealloc: (a: number) => void;
199
334
  readonly __wbindgen_malloc: (a: number, b: number) => number;
200
335
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
336
+ readonly __externref_table_dealloc: (a: number) => void;
337
+ readonly __wbindgen_free: (a: number, b: number, c: number) => void;
201
338
  readonly __externref_drop_slice: (a: number, b: number) => void;
202
339
  readonly __wbindgen_start: () => void;
203
340
  }
@@ -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.
@@ -138,6 +253,14 @@ export class Spectrum {
138
253
  wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
139
254
  return v1;
140
255
  }
256
+ /**
257
+ * Frame period derived from the machine profile.
258
+ * @returns {number}
259
+ */
260
+ frame_ms() {
261
+ const ret = wasm.spectrum_frame_ms(this.__wbg_ptr);
262
+ return ret;
263
+ }
141
264
  /**
142
265
  * Presses a key, from a DOM `KeyboardEvent.code`.
143
266
  *
@@ -268,6 +391,135 @@ export class Spectrum {
268
391
  wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
269
392
  }
270
393
  }
394
+ /**
395
+ * Reads visible memory for a lesson's live inspection panel.
396
+ *
397
+ * # Errors
398
+ * Rejects ranges extending beyond the 64 KiB address space.
399
+ * @param {number} address
400
+ * @param {number} length
401
+ * @returns {Uint8Array}
402
+ */
403
+ readMemory(address, length) {
404
+ const ret = wasm.spectrum_readMemory(this.__wbg_ptr, address, length);
405
+ if (ret[3]) {
406
+ throw takeFromExternrefTable0(ret[2]);
407
+ }
408
+ var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
409
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
410
+ return v1;
411
+ }
412
+ /**
413
+ * Restores compatible machine state, clearing pending host input/audio.
414
+ * @param {Uint8Array} bytes
415
+ */
416
+ restore_state(bytes) {
417
+ const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc);
418
+ const len0 = WASM_VECTOR_LEN;
419
+ const ret = wasm.spectrum_restore_state(this.__wbg_ptr, ptr0, len0);
420
+ if (ret[1]) {
421
+ throw takeFromExternrefTable0(ret[0]);
422
+ }
423
+ }
424
+ /**
425
+ * Returns the executed routine events and whether the stop was reached.
426
+ *
427
+ * # Errors
428
+ * Returns a JSON serialisation error if the recording cannot be encoded.
429
+ * @returns {string}
430
+ */
431
+ routineTrace() {
432
+ let deferred2_0;
433
+ let deferred2_1;
434
+ try {
435
+ const ret = wasm.spectrum_routineTrace(this.__wbg_ptr);
436
+ var ptr1 = ret[0];
437
+ var len1 = ret[1];
438
+ if (ret[3]) {
439
+ ptr1 = 0; len1 = 0;
440
+ throw takeFromExternrefTable0(ret[2]);
441
+ }
442
+ deferred2_0 = ptr1;
443
+ deferred2_1 = len1;
444
+ return getStringFromWasm0(ptr1, len1);
445
+ } finally {
446
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
447
+ }
448
+ }
449
+ /**
450
+ * Installs a numbered BASIC listing directly into RAM and asks the ROM
451
+ * to RUN it. Call on a fresh 48K; no tape is mounted or played.
452
+ *
453
+ * # Errors
454
+ * Returns conversion, boot or editor-prompt errors.
455
+ * @param {string} source
456
+ */
457
+ runBasic(source) {
458
+ const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
459
+ const len0 = WASM_VECTOR_LEN;
460
+ const ret = wasm.spectrum_runBasic(this.__wbg_ptr, ptr0, len0);
461
+ if (ret[1]) {
462
+ throw takeFromExternrefTable0(ret[0]);
463
+ }
464
+ }
465
+ /**
466
+ * Installs machine code in a fresh 48K and calls it through the ROM's
467
+ * RANDOMIZE USR, with CLEAR reserving its RAM and a valid return stack.
468
+ *
469
+ * # Errors
470
+ * Rejects empty code, overlap with BASIC/system RAM, overflow, entry
471
+ * outside the code, and ROM boot or prompt failures.
472
+ * @param {Uint8Array} bytes
473
+ * @param {number} origin
474
+ * @param {number} entry
475
+ */
476
+ runCode(bytes, origin, entry) {
477
+ const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_malloc);
478
+ const len0 = WASM_VECTOR_LEN;
479
+ const ret = wasm.spectrum_runCode(this.__wbg_ptr, ptr0, len0, origin, entry);
480
+ if (ret[1]) {
481
+ throw takeFromExternrefTable0(ret[0]);
482
+ }
483
+ }
484
+ /**
485
+ * Exports the current machine state.
486
+ * @returns {Uint8Array}
487
+ */
488
+ save_state() {
489
+ const ret = wasm.spectrum_save_state(this.__wbg_ptr);
490
+ if (ret[3]) {
491
+ throw takeFromExternrefTable0(ret[2]);
492
+ }
493
+ var v1 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
494
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
495
+ return v1;
496
+ }
497
+ /**
498
+ * Returns captured writes (including ROM writes) and saturation status.
499
+ * Consumers can select the program's PC range without inventing a trace.
500
+ *
501
+ * # Errors
502
+ * Returns a serialisation error if the capture cannot be encoded.
503
+ * @returns {string}
504
+ */
505
+ screenWriteTrace() {
506
+ let deferred2_0;
507
+ let deferred2_1;
508
+ try {
509
+ const ret = wasm.spectrum_screenWriteTrace(this.__wbg_ptr);
510
+ var ptr1 = ret[0];
511
+ var len1 = ret[1];
512
+ if (ret[3]) {
513
+ ptr1 = 0; len1 = 0;
514
+ throw takeFromExternrefTable0(ret[2]);
515
+ }
516
+ deferred2_0 = ptr1;
517
+ deferred2_1 = len1;
518
+ return getStringFromWasm0(ptr1, len1);
519
+ } finally {
520
+ wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
521
+ }
522
+ }
271
523
  /**
272
524
  * Starts or stops machine audio.
273
525
  * @param {boolean} enabled
@@ -275,6 +527,17 @@ export class Spectrum {
275
527
  setAudioEnabled(enabled) {
276
528
  wasm.spectrum_setAudioEnabled(this.__wbg_ptr, enabled);
277
529
  }
530
+ /**
531
+ * Advances exactly one frame without drawing to a canvas.
532
+ * # Errors
533
+ * Returns the runtime's execution error.
534
+ */
535
+ step() {
536
+ const ret = wasm.spectrum_step(this.__wbg_ptr);
537
+ if (ret[1]) {
538
+ throw takeFromExternrefTable0(ret[0]);
539
+ }
540
+ }
278
541
  /**
279
542
  * Runs the machine for `elapsed_ms` of real time and draws the result.
280
543
  *
@@ -303,6 +566,29 @@ export class Spectrum {
303
566
  }
304
567
  }
305
568
  if (Symbol.dispose) Spectrum.prototype[Symbol.dispose] = Spectrum.prototype.free;
569
+
570
+ /**
571
+ * Tokenise source and create an auto-starting BASIC TAP, without a machine.
572
+ *
573
+ * # Errors
574
+ * Returns a JavaScript error for unsupported or malformed listing input.
575
+ * @param {string} source
576
+ * @param {string} name
577
+ * @returns {Uint8Array}
578
+ */
579
+ export function basicTape(source, name) {
580
+ const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
581
+ const len0 = WASM_VECTOR_LEN;
582
+ const ptr1 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
583
+ const len1 = WASM_VECTOR_LEN;
584
+ const ret = wasm.basicTape(ptr0, len0, ptr1, len1);
585
+ if (ret[3]) {
586
+ throw takeFromExternrefTable0(ret[2]);
587
+ }
588
+ var v3 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
589
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
590
+ return v3;
591
+ }
306
592
  function __wbg_get_imports() {
307
593
  const import0 = {
308
594
  __proto__: null,
@@ -417,7 +703,7 @@ function __wbg_get_imports() {
417
703
  return ret;
418
704
  },
419
705
  __wbindgen_cast_0000000000000001: function(arg0, arg1) {
420
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 23, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
706
+ // 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`.
421
707
  const ret = makeMutClosure(arg0, arg1, wasm_bindgen_d6cb7d81ec28c0ae___convert__closures_____invoke___wasm_bindgen_d6cb7d81ec28c0ae___JsValue__core_f0fd674eaa06beef___result__Result_____wasm_bindgen_d6cb7d81ec28c0ae___JsError___true_);
422
708
  return ret;
423
709
  },
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.3.0",
5
+ "version": "0.4.1",
6
6
  "license": "SEE LICENSE IN README.md",
7
7
  "repository": {
8
8
  "type": "git",