@specy/risc-v 3.7.0 → 4.0.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/dist/index.d.mts CHANGED
@@ -44,17 +44,70 @@ type JsInstructionToken = {
44
44
  value: string;
45
45
  type: string;
46
46
  };
47
+ /**
48
+ * Why a run call (`step`, `simulate`, `simulateWithLimit`, `simulateWithBreakpoints`,
49
+ * `simulateWithBreakpointsAndLimit`) stopped. The same enum, with the same values, as
50
+ * `@specy/mips`'s.
51
+ */
47
52
  declare enum StopReason {
48
- NONE = -1,// no simulation has run yet
53
+ /** No run call has stopped since the program was initialized; only `getStopReason` reports it. */
54
+ NONE = -1,
55
+ /** A breakpoint address or an `ebreak` was reached. */
49
56
  BREAKPOINT = 0,
57
+ /** Never returned: a runtime failure rejects the run call with a `RuntimeError` instead. */
50
58
  EXCEPTION = 1,
51
- MAX_STEPS = 2,// includes step mode (where maxSteps is 1)
59
+ /** The instruction limit was reached, which is what a single `step()` ends on. */
60
+ MAX_STEPS = 2,
61
+ /** An exit service (10 or 93) ran, or the program had already exited and nothing ran. */
52
62
  NORMAL_TERMINATION = 3,
53
- CLIFF_TERMINATION = 4,// run off bottom of program
63
+ /** The program ran off the end of its code: there is no statement at the program counter. */
64
+ CLIFF_TERMINATION = 4,
65
+ /** Not reachable through this package. */
54
66
  PAUSE = 5,
67
+ /** Not reachable through this package. */
55
68
  STOP = 6
56
69
  }
70
+ /**
71
+ * What a `RuntimeError` was:
72
+ * - `exception`: a RISC-V exception the program had no trap handler for: a misaligned or faulting
73
+ * access, an illegal instruction, an instruction fetch outside the program.
74
+ * - `syscall`: an ecall refused its service number, arguments or input, such as "invalid integer
75
+ * input (syscall 5)" or "invalid or unimplemented syscall service: 99".
76
+ * - `handler`: a host handler or memory observer threw, rejected or broke its contract; its `cause`
77
+ * is what it threw or rejected with.
78
+ * - `internal`: the simulator itself failed.
79
+ */
80
+ type RuntimeErrorKind = 'exception' | 'syscall' | 'handler' | 'internal';
81
+ /**
82
+ * The error a run call rejects with when the program fails at runtime: an `Error` named
83
+ * `RuntimeError` with typed fields, so that a host never parses its text. Any other rejection,
84
+ * such as a run call on a program that did not assemble, is the plain error it is.
85
+ */
86
+ interface RuntimeError extends Error {
87
+ readonly name: 'RuntimeError';
88
+ readonly kind: RuntimeErrorKind;
89
+ /**
90
+ * The address of the instruction that failed, or of the program counter that could not be
91
+ * fetched, as a signed 32 bit int like `JsProgramStatement.address`.
92
+ */
93
+ readonly address: number;
94
+ /** The failing statement's source file, or null when there is no statement. */
95
+ readonly sourcePath: string | null;
96
+ /** The failing statement's one-based line in `sourcePath`, or null. */
97
+ readonly line: number | null;
98
+ /**
99
+ * RARS's own words, without the location: "Runtime exception at 0x00400004: invalid or
100
+ * unimplemented syscall service: 99", "Handler readInt did not return a string".
101
+ */
102
+ readonly message: string;
103
+ /** For a `handler` failure, what the handler threw or rejected with. */
104
+ readonly cause?: unknown;
105
+ }
106
+ /** Whether a run call's rejection is a typed `RuntimeError`. */
107
+ declare function isRuntimeError(error: unknown): error is RuntimeError;
108
+ /** The kind of message dialog syscall 55 asks for; any other value of a1 asks for a plain message. */
57
109
  declare enum DialogType {
110
+ PLAIN_MESSAGE = -1,
58
111
  ERROR_MESSAGE = 0,
59
112
  INFORMATION_MESSAGE = 1,
60
113
  WARNING_MESSAGE = 2,
@@ -65,97 +118,114 @@ declare enum ConfirmResult {
65
118
  NO = 1,
66
119
  CANCEL = 2
67
120
  }
121
+ /**
122
+ * The handlers a program's syscalls reach the outside world through, each as its arguments and its
123
+ * answer. The syscalls own each service's semantics, as RARS defines them: they format what they
124
+ * print and parse what they read, so a handler only moves text and bytes. Bytes cross as plain
125
+ * arrays of numbers from 0 to 255.
126
+ */
68
127
  type HandlerMap = {
128
+ /**
129
+ * Syscall 1024: open a file, with `flags` 0 to read and 1 to write, `append` set for flags 9. The
130
+ * path is the program's string decoded from UTF-8. Answer with the new file descriptor, or -1
131
+ * when the file cannot be opened.
132
+ */
69
133
  openFile: {
70
134
  in: [filename: string, flags: number, append: boolean];
71
135
  out: number;
72
136
  };
137
+ /** Syscall 57: close a file descriptor that `openFile` returned. */
73
138
  closeFile: {
74
139
  in: [fileDescriptor: number];
75
140
  out: void;
76
141
  };
142
+ /**
143
+ * Syscall 64 on a file descriptor that `openFile` returned: write the bytes. Answer with the
144
+ * number of bytes written, or -1 when the write failed; the program receives it in a0.
145
+ */
77
146
  writeFile: {
78
147
  in: [fileDescriptor: number, buffer: number[]];
79
- out: void;
148
+ out: number;
80
149
  };
150
+ /**
151
+ * Syscall 63 on a file descriptor that `openFile` returned: read at most `length` bytes. Answer
152
+ * with `[count, bytes]`, where a count of 0 is the end of the file and -1 a failed read.
153
+ */
81
154
  readFile: {
82
- in: [fileDescriptor: number, destination: number[], length: number];
83
- out: [readOrEof: number, buffer: number[]];
155
+ in: [fileDescriptor: number, length: number];
156
+ out: [count: number, buffer: number[]];
84
157
  };
158
+ /**
159
+ * Syscall 50: a Yes, No and Cancel question. Cancel reaches the program as 2. Every dialog's
160
+ * message is the program's string at a0, decoded from UTF-8.
161
+ */
85
162
  confirm: {
86
163
  in: [message: string];
87
164
  out: ConfirmResult;
88
165
  };
166
+ /**
167
+ * Syscalls 51 to 54: ask for a line of text. Answer with the text, or null when the user cancels,
168
+ * which the program receives as status -2. The syscall parses the text itself, as RARS does;
169
+ * 54 stores it as UTF-8, measuring the buffer in bytes.
170
+ */
89
171
  inputDialog: {
90
172
  in: [message: string];
91
- out: string;
173
+ out: string | null;
92
174
  };
175
+ /** Syscalls 55, 56 and 58 to 60: show a message. The program waits until the handler settles. */
93
176
  outputDialog: {
94
177
  in: [message: string, type: DialogType];
95
178
  out: void;
96
179
  };
97
- askDouble: {
98
- in: [message: string];
99
- out: number;
100
- };
101
- askFloat: {
102
- in: [message: string];
103
- out: number;
104
- };
105
- askInt: {
106
- in: [message: string];
107
- out: number;
108
- };
109
- askString: {
110
- in: [message: string];
111
- out: string;
112
- };
113
- readDouble: {
180
+ /**
181
+ * Syscall 5: the line typed. The syscall trims it and parses it as Java's `Integer.parseInt`
182
+ * does; anything else stops the program with "invalid integer input (syscall 5)".
183
+ */
184
+ readInt: {
114
185
  in: [];
115
- out: number;
186
+ out: string;
116
187
  };
188
+ /**
189
+ * Syscall 6: the line typed. The syscall trims it and parses it as Java's `Float.parseFloat`
190
+ * does; anything else stops the program with "invalid float input (syscall 6)".
191
+ */
117
192
  readFloat: {
118
193
  in: [];
119
- out: number;
194
+ out: string;
120
195
  };
121
- readInt: {
196
+ /**
197
+ * Syscall 7: the line typed. The syscall trims it and parses it as Java's `Double.parseDouble`
198
+ * does; anything else stops the program with "invalid double input (syscall 7)".
199
+ */
200
+ readDouble: {
122
201
  in: [];
123
- out: number;
202
+ out: string;
124
203
  };
204
+ /**
205
+ * Syscall 8: the line typed. The syscall stores it as UTF-8, as RARS does: at most the buffer's
206
+ * length less one characters, then at most that many bytes of them, so a character can be cut
207
+ * where the buffer ends.
208
+ */
125
209
  readString: {
126
210
  in: [];
127
211
  out: string;
128
212
  };
213
+ /**
214
+ * Syscall 12: the character typed, Enter as `"\n"`. The syscall takes the first UTF-16 unit of the
215
+ * answer, as RARS does (`é` reads as 233, an emoji as its high surrogate); an empty answer stops
216
+ * the program with "invalid char input (syscall 12)".
217
+ */
129
218
  readChar: {
130
219
  in: [];
131
220
  out: string;
132
221
  };
133
- logLine: {
134
- in: [message: string];
135
- out: void;
136
- };
137
- log: {
138
- in: [message: string];
139
- out: void;
140
- };
141
- printChar: {
142
- in: [c: string];
143
- out: void;
144
- };
145
- printDouble: {
146
- in: [d: number];
147
- out: void;
148
- };
149
- printFloat: {
150
- in: [f: number];
151
- out: void;
152
- };
153
- printInt: {
154
- in: [i: number];
155
- out: void;
156
- };
222
+ /**
223
+ * Program output: the text every print syscall (1 to 4, 11 and 34 to 36) formats, exactly as
224
+ * RARS prints it on Java 21, so a float prints `1.0` and `1.0E-5`. Print string (4) decodes the
225
+ * string from UTF-8; print char (11) prints the character numbered by the low byte of a0.
226
+ */
157
227
  printString: {
158
- in: [l: string];
228
+ in: [text: string];
159
229
  out: void;
160
230
  };
161
231
  /**
@@ -167,9 +237,10 @@ type HandlerMap = {
167
237
  out: void;
168
238
  };
169
239
  /**
170
- * Syscall 30: the program time in milliseconds, split by the syscall into a0 (low word) and
171
- * a1 (high word). Answer with `Date.now()` for a live run, or with a virtual clock for a
172
- * scripted one, so that elapsed-time output stays reproducible.
240
+ * Syscall 30, and every instruction that reads the `time` CSR (or `timeh`, its upper half on
241
+ * RV32): the program time in milliseconds. The syscall splits it into a0 (low word) and a1 (high
242
+ * word). Answer with `Date.now()` for a live run, or with a virtual clock for a scripted one, so
243
+ * that elapsed-time output stays reproducible.
173
244
  */
174
245
  time: {
175
246
  in: [];
@@ -181,7 +252,7 @@ type HandlerMap = {
181
252
  * of input and -1 a failed read. Return a promise to wait for the user.
182
253
  */
183
254
  stdIn: {
184
- in: [buffer: number[], length: number];
255
+ in: [length: number];
185
256
  out: [count: number, buffer: number[]];
186
257
  };
187
258
  /**
@@ -192,14 +263,30 @@ type HandlerMap = {
192
263
  in: [fileDescriptor: number, offset: number, whence: number];
193
264
  out: number;
194
265
  };
266
+ /** Syscall 64 on descriptor 1: the bytes written to standard output. */
195
267
  stdOut: {
196
268
  in: [buffer: number[]];
197
269
  out: void;
198
270
  };
271
+ /** Syscall 64 on descriptor 2: the bytes written to standard error. */
199
272
  stdErr: {
200
273
  in: [buffer: number[]];
201
274
  out: void;
202
275
  };
276
+ /**
277
+ * Syscalls 41 to 44, on the first use of a generator the program has not seeded with service
278
+ * 40: the seed it starts from, as `new java.util.Random(seed)` takes it, a whole number from 0
279
+ * to 2^48 - 1 (every seed a Java long can hold scrambles to the state one of these does).
280
+ * `index` is the generator's number from a0. Answer with host randomness for a live run, or
281
+ * with a fixed seed for a scripted one, so that a Testcase gets the same numbers every time.
282
+ *
283
+ * Optional: without it a generator starts from host randomness, as in RARS. The generators
284
+ * implement `java.util.Random` exactly, so a seeded program prints the numbers RARS prints.
285
+ */
286
+ randomSeed: {
287
+ in: [index: number];
288
+ out: number;
289
+ };
203
290
  };
204
291
  /**
205
292
  * Notified after a write anywhere in an observed address range.
@@ -361,12 +448,26 @@ declare enum BackStepAction {
361
448
  * A whole Poke: every value one `beginPoke`/`endPoke` transaction wrote, restored together.
362
449
  * A Poke is a single back step, so it takes one slot of the undo size whatever it wrote.
363
450
  */
364
- POKE = 13
451
+ POKE = 13,
452
+ /**
453
+ * An exit service (10 or 93): undoing it leaves the program running on the ecall again.
454
+ * `param1` is 1 if the program had already exited (never, as nothing runs after an exit),
455
+ * `oldValue` the exit code it replaced and `newValue` the code the exit set.
456
+ */
457
+ EXIT_RESTORE = 14,
458
+ /**
459
+ * A random service (40 to 44) advanced or reseeded generator `param1`: undoing it puts the
460
+ * generator back, so that stepping the service again draws the same number. The generator's
461
+ * state is the simulator's own, so `param2`, `oldValue` and `newValue` are 0.
462
+ */
463
+ RANDOM_STREAM_RESTORE = 15
365
464
  }
366
465
  /**
367
466
  * Represents a back step in the simulation undo stack.
368
467
  */
369
468
  interface JsBackStep {
469
+ /** Opaque decimal identity, unique across Undo, initialize and reassembly in this module. */
470
+ readonly serial: string;
370
471
  /**
371
472
  * The action performed (e.g., register write, memory write).
372
473
  */
@@ -396,8 +497,10 @@ interface JsBackStep {
396
497
  * was made, in the low order bits.
397
498
  * - `PC_RESTORE`: the address the restore puts back - the same value as `param1`, which is
398
499
  * where this action has always kept it, its `param2` being unused and 0.
500
+ * - `EXIT_RESTORE`: the exit code the exit replaced.
399
501
  * - `'0'` for `CONTROL_AND_STATUS_COUNTERS_DECREMENT`, `DO_NOTHING` and `POKE`, which replace
400
502
  * no single value; a Poke reports both sides of everything it wrote through its own `writes`.
503
+ * Also for `RANDOM_STREAM_RESTORE`, whose generator state is the simulator's own.
401
504
  */
402
505
  readonly oldValue: string;
403
506
  /**
@@ -407,9 +510,9 @@ interface JsBackStep {
407
510
  *
408
511
  * `CONTROL_AND_STATUS_REGISTER_BACKDOOR` is the simulator sampling the wall clock into `time`
409
512
  * rather than a program writing a register, but it is a write all the same and reports the
410
- * millisecond reading it stored. `'0'` for `CONTROL_AND_STATUS_COUNTERS_DECREMENT` and
411
- * `DO_NOTHING`, which write no value of their own, and for `POKE`, whose `writes` carry both
412
- * sides already.
513
+ * millisecond reading it stored. `EXIT_RESTORE` reports the exit code the exit set. `'0'` for
514
+ * `CONTROL_AND_STATUS_COUNTERS_DECREMENT` and `DO_NOTHING`, which write no value of their own,
515
+ * for `POKE`, whose `writes` carry both sides already, and for `RANDOM_STREAM_RESTORE`.
413
516
  */
414
517
  readonly newValue: string;
415
518
  /**
@@ -469,6 +572,8 @@ type JsUndoGroup = JsInstructionUndoGroup | JsPokeUndoGroup;
469
572
  * overwrote, and the `cycle`/`instret` decrement every instruction pushes on top of them.
470
573
  */
471
574
  type JsInstructionUndoGroup = {
575
+ /** Opaque decimal identity, unique across Undo, initialize and reassembly in this module. */
576
+ readonly serial: string;
472
577
  readonly kind: 'instruction';
473
578
  readonly pc: number;
474
579
  /** The instruction's back steps, newest first. */
@@ -483,6 +588,8 @@ type JsInstructionUndoGroup = {
483
588
  * alone.
484
589
  */
485
590
  type JsPokeUndoGroup = {
591
+ /** Opaque decimal identity, unique across Undo, initialize and reassembly in this module. */
592
+ readonly serial: string;
486
593
  readonly kind: 'poke';
487
594
  readonly pc: -1;
488
595
  /** The single back step the Poke is: one slot of the history, whatever the Poke wrote. */
@@ -546,8 +653,13 @@ type HandlerName = keyof HandlerMap;
546
653
  * user, reading a file, awaiting a worker) without blocking the event loop. If the promise
547
654
  * rejects, the pending `step`/`simulate*` call rejects too.
548
655
  */
656
+ type HandlerFn<K extends HandlerName> = (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'] | Promise<HandlerMap[K]['out']>;
657
+ /** The handlers a host may leave out: the simulator then does what RARS does on its own. */
658
+ type OptionalHandlerName = 'randomSeed';
549
659
  type HandlerMapFns = {
550
- [K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'] | Promise<HandlerMap[K]['out']>;
660
+ [K in Exclude<HandlerName, OptionalHandlerName>]: HandlerFn<K>;
661
+ } & {
662
+ [K in OptionalHandlerName]?: HandlerFn<K>;
551
663
  };
552
664
  declare function registerHandlers(riscv: JsRiscV, handlers: HandlerMapFns): void;
553
665
  declare function unimplementedHandler(name: HandlerName): () => never;
@@ -560,23 +672,39 @@ interface JsRiscV {
560
672
  */
561
673
  assemble(): RISCVAssembleResult;
562
674
  /**
563
- * Initializes the simulator.
675
+ * Initializes the simulator for a new run: the registers, the program counter, and the state
676
+ * of the run - not exited, exit code 0, no stop reason, and the random generators forgotten, so
677
+ * that each starts from a new seed on its first use. Assembling leaves the run state alone.
564
678
  * @param startAtMain If true, starts execution at the 'main' label. Otherwise, starts at the first instruction.
565
679
  */
566
680
  initialize(startAtMain: boolean): void;
567
681
  /**
568
- * Gets the current stop reason.
682
+ * Why the last run call stopped: `NONE` when none has since the program was initialized, and
683
+ * `EXCEPTION` after one rejected with a `RuntimeError`. Undo does not change it; read
684
+ * `terminated` for whether the program can run.
569
685
  */
570
686
  getStopReason(): StopReason;
687
+ /**
688
+ * The program's exit code: exit2's (93) operand once it has run, 0 otherwise - after exit (10),
689
+ * after running off the end, while the program runs. `initialize` resets it to 0, and undoing
690
+ * the exit puts back the code before it.
691
+ */
692
+ readonly exitCode: number;
571
693
  /**
572
694
  * Executes a single instruction.
573
695
  *
574
- * The promise settles on a microtask unless an IO handler returned a promise, in which case
575
- * it settles once that handler and the rest of the instruction have finished.
696
+ * Resolves to why it stopped: `MAX_STEPS` normally, `NORMAL_TERMINATION` for an exit,
697
+ * `CLIFF_TERMINATION` when the instruction was the program's last (or there was none to run),
698
+ * `BREAKPOINT` for an `ebreak`. A program that has exited runs nothing more and resolves to
699
+ * `NORMAL_TERMINATION` again. Rejects with a `RuntimeError` when the program fails. The
700
+ * promise settles on a microtask unless an IO handler returned a promise, in which case it
701
+ * settles once that handler and the rest of the instruction have finished.
576
702
  */
577
703
  step(): Promise<StopReason>;
578
704
  /**
579
- * Sets the size of the undo stack, must be called before assembling the program.
705
+ * Sets the maximum raw restore slots, before assembly. A Poke uses one slot; instructions
706
+ * use one slot per restore. Oldest instructions are evicted whole. An instruction larger
707
+ * than the capacity is discarded whole. Zero retains no history; negative sizes throw.
580
708
  * @param size
581
709
  */
582
710
  setUndoSize(size: number): void;
@@ -617,32 +745,45 @@ interface JsRiscV {
617
745
  getLabelAtAddress(address: number): string | null;
618
746
  /** Address of a defined label/alias, or -1 if absent. Requires successful assembly. */
619
747
  getAddressOfLabel(label: string): number;
748
+ /**
749
+ * Where the program's heap, and so the first block sbrk (9) hands out, starts. RARS's heap base,
750
+ * 0x10040000, unless the program is a `gnu-compiler-v1` Build whose static data (`.data`,
751
+ * `.rodata`, `.bss`, common symbols) reaches past it: then the heap starts at the first 4 KiB page
752
+ * after static data, where ld and a kernel put the break. A RARS-dialect program keeps the heap
753
+ * base whatever its size, as RARS does. Requires successful assembly; `initialize` empties the
754
+ * heap again.
755
+ */
756
+ getHeapStart(): number;
620
757
  /**
621
758
  * Sets whether the undo feature is enabled.
622
759
  * @param enabled True to enable the undo feature, false to disable it.
623
760
  */
624
761
  setUndoEnabled(enabled: boolean): void;
625
762
  /**
626
- * Simulates until the stop condition is met. it might be a breakpoint, exception, etc...
763
+ * Simulates until the program stops: an exit, running off the end, an `ebreak`, or a failure,
764
+ * which rejects with a `RuntimeError`.
627
765
  */
628
766
  simulate(): Promise<StopReason>;
629
767
  /**
630
768
  * Simulates the program for a limited number of instructions.
631
769
  * @param limit The maximum number of instructions to execute.
632
- * @returns A promise resolving to the reason the simulation stopped.
770
+ * @returns A promise resolving to the reason the simulation stopped (`MAX_STEPS` when the
771
+ * limit did), rejecting with a `RuntimeError` when the program fails.
633
772
  */
634
773
  simulateWithLimit(limit: number): Promise<StopReason>;
635
774
  /**
636
775
  * Simulates the program until a breakpoint is reached.
637
776
  * @param breakpoints An array of memory addresses where the simulation should pause.
638
- * @returns A promise resolving to the reason the simulation stopped.
777
+ * @returns A promise resolving to the reason the simulation stopped, rejecting with a
778
+ * `RuntimeError` when the program fails.
639
779
  */
640
780
  simulateWithBreakpoints(breakpoints: number[]): Promise<StopReason>;
641
781
  /**
642
782
  * Simulates the program with both breakpoints and a limit.
643
783
  * @param breakpoints An array of memory addresses where the simulation should pause.
644
784
  * @param limit The maximum number of instructions to execute.
645
- * @returns A promise resolving to the reason the simulation stopped.
785
+ * @returns A promise resolving to the reason the simulation stopped, rejecting with a
786
+ * `RuntimeError` when the program fails.
646
787
  */
647
788
  simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): Promise<StopReason>;
648
789
  /**
@@ -658,11 +799,12 @@ interface JsRiscV {
658
799
  */
659
800
  getRegisterValueLong(register: RegisterName): string;
660
801
  /**
661
- * Registers a handler function for a specific event or condition.
802
+ * Registers a handler function for a specific event or condition. Handlers are shared by every
803
+ * `JsRiscV` instance. An optional handler (`randomSeed`) is removed by registering `undefined`.
662
804
  * @param name The name of the event or condition.
663
805
  * @param handler The handler function to be called when the event occurs. The function signature depends on the event name.
664
806
  */
665
- registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out'] | Promise<HandlerMap[T]['out']>): void;
807
+ registerHandler<T extends HandlerName>(name: T, handler: HandlerFn<T> | (T extends OptionalHandlerName ? undefined : never)): void;
666
808
  /**
667
809
  * Gets the current value of the stack pointer.
668
810
  * @returns The value of the stack pointer.
@@ -753,6 +895,15 @@ interface JsRiscV {
753
895
  * carries what it changed, with the old and the new value of each write.
754
896
  */
755
897
  getUndoGroups(): JsUndoGroup[];
898
+ /**
899
+ * The current dynamic instruction's serial, or null outside execution. Read it inside a host
900
+ * handler to journal an effect under the same identity its eventual Undo group carries. It
901
+ * remains stable while an async handler waits. Failures and exits have identities too.
902
+ * Serials are allocated even with Undo disabled; such runs retain no groups. Treat the decimal
903
+ * string as opaque (or compare with BigInt), never convert it to a JS number. Serials never
904
+ * rewind or alias after Undo, initialize or reassembly; initialize clears retained history.
905
+ */
906
+ getCurrentInstructionSerial(): string | null;
756
907
  /**
757
908
  * The newest `max` entries of `getUndoGroups`, read from the top of the history without
758
909
  * grouping the rest of it: what a panel showing the latest steps of a large history wants.
@@ -860,9 +1011,10 @@ interface JsRiscV {
860
1011
  countMemoryObservers(): number;
861
1012
  /**
862
1013
  * Gets the next statement to be executed.
863
- * @returns The next `JsProgramStatement`.
1014
+ * @returns The next `JsProgramStatement`, or null once the program has ended (`terminated`):
1015
+ * after an exit the statement that follows the ecall is not one the program will run.
864
1016
  */
865
- getNextStatement(): JsProgramStatement;
1017
+ getNextStatement(): JsProgramStatement | null;
866
1018
  /**
867
1019
  * Sets the value of a register. Outside a Poke the write is direct: it records no undo step.
868
1020
  * Inside a Poke it joins the open transaction, except for `zero`, which holds no value and so
@@ -872,10 +1024,12 @@ interface JsRiscV {
872
1024
  */
873
1025
  setRegisterValue(register: RegisterName, high: number, low: number): void;
874
1026
  /**
875
- * Checks if the simulation has terminated.
876
- * @returns True if the simulation has terminated, false otherwise.
1027
+ * Whether the program has ended, read from its state rather than from the last run call, so
1028
+ * that it is right after `undo()` too: an exit service has run, or there is no statement at
1029
+ * the program counter because execution ran off the end. It is true as soon as the last
1030
+ * instruction has run. A runtime failure does not set it: the run call's rejection reports it.
877
1031
  */
878
- terminated: boolean;
1032
+ readonly terminated: boolean;
879
1033
  }
880
1034
  /**
881
1035
  * Creates a RISC-V simulator from a virtual source tree and its entry file.
@@ -900,4 +1054,4 @@ declare function bigintToHighLow(value: bigint): [high: number, low: number];
900
1054
  */
901
1055
  declare function highLowToBigint(high: number, low: number): bigint;
902
1056
 
903
- export { type AssemblerProfile, type AssemblyOptions, BackStepAction, ConfirmResult, type CsrRegisterName, DialogType, type FloatingPointRegisterName, type GnuUnitSymbols, type HandlerMap, type HandlerMapFns, type JsBackStep, type JsInstruction, type JsInstructionToken, type JsInstructionUndoGroup, type JsPokeMemoryWrite, type JsPokeRegisterWrite, type JsPokeUndoGroup, type JsPokeWrite, type JsProgramStatement, type JsRiscV, type JsRiscVStackFrame, type JsUndoGroup, type MemoryAccessObserver, type MemoryObserverHandle, type MemoryWriteObserver, RISCV, type RISCVAssembleError, type RISCVAssembleResult, RISCVRegisters, type RISCVSourceLocation, type RISCVSourceSet, RISCV_CSR_REGISTERS, RISCV_FLOATING_POINT_REGISTERS, RISCV_REGISTERS, type RegisterName, type RiscvTokenizedLine, type RuntimeLibrary, StopReason, bigintToHighLow, highLowToBigint, makeRiscVFromFiles, registerHandlers, unimplementedHandler };
1057
+ export { type AssemblerProfile, type AssemblyOptions, BackStepAction, ConfirmResult, type CsrRegisterName, DialogType, type FloatingPointRegisterName, type GnuUnitSymbols, type HandlerMap, type HandlerMapFns, type JsBackStep, type JsInstruction, type JsInstructionToken, type JsInstructionUndoGroup, type JsPokeMemoryWrite, type JsPokeRegisterWrite, type JsPokeUndoGroup, type JsPokeWrite, type JsProgramStatement, type JsRiscV, type JsRiscVStackFrame, type JsUndoGroup, type MemoryAccessObserver, type MemoryObserverHandle, type MemoryWriteObserver, type OptionalHandlerName, RISCV, type RISCVAssembleError, type RISCVAssembleResult, RISCVRegisters, type RISCVSourceLocation, type RISCVSourceSet, RISCV_CSR_REGISTERS, RISCV_FLOATING_POINT_REGISTERS, RISCV_REGISTERS, type RegisterName, type RiscvTokenizedLine, type RuntimeError, type RuntimeErrorKind, type RuntimeLibrary, StopReason, bigintToHighLow, highLowToBigint, isRuntimeError, makeRiscVFromFiles, registerHandlers, unimplementedHandler };