@specy/risc-v 3.6.2 → 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/README.md +218 -15
- package/dist/index.d.mts +306 -81
- package/dist/index.d.ts +306 -81
- package/dist/index.js +47247 -36414
- package/dist/index.mjs +47246 -36414
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,6 +13,20 @@ First, create an instance of the simulator with `makeRiscVFromFiles`. Supply a v
|
|
|
13
13
|
|
|
14
14
|
Before running the simulator, you must assemble and initialize it. You can then step through the program, simulate with breakpoints, or simulate with a limit.
|
|
15
15
|
|
|
16
|
+
The optional third factory argument selects the assembly profile:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
const core = makeRiscVFromFiles(files, 'main.s', { assemblerProfile: 'gnu-compiler-v1' });
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Omission selects `rars`. `RISCV.assemblerProfiles` lists supported profiles; every present invalid profile throws. GNU compiler v1 provides independent named sections, explicit data alignment, exact eight-byte data, byte-valued string escapes, bounded expressions/aliases, numeric labels, and absolute/PC-relative address fixups. It uses fixed instruction expansions and rejects unsupported directives, ISA attributes, relocations and unresolved runtime helpers. Units link with ld semantics: global and weak symbols (an undefined weak reference is zero), COMDAT groups (the first copy is kept), and `.init_array`/`.fini_array` with ld-provided `__init_array_start`/`__init_array_end` bounds. Global labels enter the global symbol table, so the start label finds them. It does not load or link ELF, compressed/vector instructions, TLS or a standard library. RARS retains its existing educational syntax and macros.
|
|
23
|
+
|
|
24
|
+
`getAddressOfLabel(name)` returns a defined label/alias address after successful assembly, or `-1` when absent. Profile state is per program; width, memory and execution state retain the existing single-instance constraint.
|
|
25
|
+
|
|
26
|
+
Floating point literals are read as Java 21 reads them, correctly rounded: `.double` stores
|
|
27
|
+
`Double.parseDouble` of its token and `.float` that double rounded to a float, as RARS does, while
|
|
28
|
+
the `gnu-compiler-v1` profile reads `.float` straight to single precision, as GNU as does.
|
|
29
|
+
|
|
16
30
|
⚠️**WARNING**⚠️ You must have only one instance of the simulator at a time. Memory, registers, and other state may be shared or behave unpredictably with multiple instances.
|
|
17
31
|
|
|
18
32
|
```typescript
|
|
@@ -55,15 +69,15 @@ console.log(`t2: ${t2}`);
|
|
|
55
69
|
const data = riscvSimulator.readMemoryBytes(0xffff0000, 4); // Read 4 bytes from address 0xffff0000
|
|
56
70
|
riscvSimulator.setMemoryBytes(0xffff0000, [0x01, 0x02, 0x03, 0x04]); // Write 4 bytes to address 0xffff0000
|
|
57
71
|
|
|
58
|
-
// Registering Handlers (for syscalls and other events):
|
|
59
|
-
riscvSimulator.registerHandler("
|
|
60
|
-
|
|
72
|
+
// Registering Handlers (for syscalls and other events): every print syscall writes its text here.
|
|
73
|
+
riscvSimulator.registerHandler("printString", (text: string) => {
|
|
74
|
+
process.stdout.write(text);
|
|
61
75
|
});
|
|
62
76
|
|
|
63
77
|
// Handlers may also be async: returning a promise suspends the simulation until it settles,
|
|
64
78
|
// so IO can be backed by an async API without blocking the event loop.
|
|
65
79
|
riscvSimulator.registerHandler("readInt", async () => {
|
|
66
|
-
return await
|
|
80
|
+
return await promptUserForALine(); // the syscall parses the line itself
|
|
67
81
|
});
|
|
68
82
|
|
|
69
83
|
// Accessing the undo stack:
|
|
@@ -96,7 +110,7 @@ Every syscall that needs to talk to the outside world goes through a handler you
|
|
|
96
110
|
handler can return its result directly, or return a promise of it:
|
|
97
111
|
|
|
98
112
|
```typescript
|
|
99
|
-
riscvSimulator.registerHandler("readInt", () => 42);
|
|
113
|
+
riscvSimulator.registerHandler("readInt", () => "42"); // synchronous
|
|
100
114
|
riscvSimulator.registerHandler("readString", () => fetchLine()); // asynchronous
|
|
101
115
|
```
|
|
102
116
|
|
|
@@ -106,17 +120,95 @@ promise settles, so nothing has to be shoehorned into a synchronous API. Because
|
|
|
106
120
|
`simulateWithBreakpointsAndLimit` all return a promise. Everything else on `JsRiscV` (registers,
|
|
107
121
|
memory, statements, the undo stack) stays synchronous.
|
|
108
122
|
|
|
109
|
-
If a handler
|
|
110
|
-
|
|
123
|
+
If a handler throws, its promise rejects or it answers what its type does not allow, the pending
|
|
124
|
+
`step`/`simulate*` call rejects with a `RuntimeError` of kind `handler`, whose `cause` is what the
|
|
125
|
+
handler threw or rejected with (see [Running and stopping](#running-and-stopping)).
|
|
111
126
|
|
|
112
127
|
Synchronous handlers never yield to the event loop: the simulation runs straight through, and the
|
|
113
128
|
returned promise settles on a microtask. Overlapping calls are queued and run one after another,
|
|
114
129
|
never concurrently.
|
|
115
130
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
The syscalls behave as RARS 1.6 does on Java 21, so handlers only move text and bytes:
|
|
132
|
+
|
|
133
|
+
* Every print syscall formats its value and writes the text through `printString`. Print float
|
|
134
|
+
and print double write what `Float.toString` and `Double.toString` write: `1.0`, `0.001`,
|
|
135
|
+
`1.0E-4`, `1.0E7`, `-0.0`, `NaN`, `Infinity`.
|
|
136
|
+
* `readInt`, `readFloat` and `readDouble` answer with the line typed. The syscall trims it and
|
|
137
|
+
parses it as `Integer.parseInt`, `Float.parseFloat` and `Double.parseDouble` do, hexadecimal
|
|
138
|
+
floats and an `f` or `d` suffix included, and stops the program on anything else:
|
|
139
|
+
`Runtime exception at 0x00400004: invalid integer input (syscall 5)`.
|
|
140
|
+
* `readChar` answers with the character typed, Enter as `"\n"`. The syscall takes the first
|
|
141
|
+
UTF-16 unit, and an empty answer stops the program with `invalid char input (syscall 12)`.
|
|
142
|
+
* `inputDialog` answers `null` when the user cancels, which dialogs 51 to 54 report to the
|
|
143
|
+
program as status -2; `confirm` answers 2 for Cancel. The program waits for `outputDialog` to
|
|
144
|
+
settle.
|
|
145
|
+
* Bytes cross as plain arrays of numbers from 0 to 255. `writeFile` answers with the number of
|
|
146
|
+
bytes written, or -1, and the program receives that count; `readFile` and `stdIn` answer
|
|
147
|
+
`[count, bytes]`, with a count of 0 at the end of the input and -1 for a failed read.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
riscvSimulator.registerHandler("inputDialog", (message) => window.prompt(message)); // null on Cancel
|
|
151
|
+
riscvSimulator.registerHandler("writeFile", (descriptor, bytes) => files.write(descriptor, Uint8Array.from(bytes)));
|
|
152
|
+
riscvSimulator.registerHandler("stdIn", async (length) => {
|
|
153
|
+
const bytes = await terminal.readBytes(length);
|
|
154
|
+
return [bytes.length, Array.from(bytes)];
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Handler | Syscalls | Receives | Answers |
|
|
159
|
+
| --- | --- | --- | --- |
|
|
160
|
+
| `printString` | 1 to 4, 11, 34 to 36 | the text to print | nothing |
|
|
161
|
+
| `readInt`, `readFloat`, `readDouble`, `readString` | 5 to 8 | nothing | the line typed |
|
|
162
|
+
| `readChar` | 12 | nothing | the character typed |
|
|
163
|
+
| `openFile` | 1024 | path, flags, whether to append | the file descriptor, or -1 |
|
|
164
|
+
| `readFile` | 63 | file descriptor, most bytes to read | `[count, bytes]` |
|
|
165
|
+
| `writeFile` | 64 | file descriptor, bytes | bytes written, or -1 |
|
|
166
|
+
| `closeFile` | 57 | file descriptor | nothing |
|
|
167
|
+
| `seekFile` | 62 | file descriptor, offset, whence | the new position, or -1 |
|
|
168
|
+
| `stdIn` | 63 on descriptor 0 | most bytes to read | `[count, bytes]` |
|
|
169
|
+
| `stdOut`, `stdErr` | 64 on descriptors 1 and 2 | bytes | nothing |
|
|
170
|
+
| `confirm` | 50 | message | `ConfirmResult` |
|
|
171
|
+
| `inputDialog` | 51 to 54 | message | the text entered, or `null` |
|
|
172
|
+
| `outputDialog` | 55, 56, 58 to 60 | message, `DialogType` | nothing |
|
|
173
|
+
| `sleep` | 32 | milliseconds | nothing |
|
|
174
|
+
| `time` | 30, and an instruction reading the `time` CSR | nothing | milliseconds |
|
|
175
|
+
| `randomSeed` (optional) | 41 to 44, a generator's first use | the generator's number | a seed from 0 to 2^48 - 1 |
|
|
176
|
+
|
|
177
|
+
Program time is a handler too, so a run can be given a clock of its own: `sleep` answers syscall 32,
|
|
178
|
+
and `time` answers syscall 30 and every read of the `time` CSR. A live run resolves `sleep` on a
|
|
179
|
+
timer and returns `Date.now()` from `time`; a scripted run can settle `sleep` immediately, advance a
|
|
180
|
+
virtual clock by the requested milliseconds and return that clock instead, which keeps elapsed-time
|
|
181
|
+
output reproducible.
|
|
182
|
+
|
|
183
|
+
## Text
|
|
184
|
+
|
|
185
|
+
Text is UTF-8 throughout, as in RARS, so a string's text is the same in a literal, through print
|
|
186
|
+
string (4) and through write (64), and the same as the UTF-8 a C compiler emits:
|
|
187
|
+
|
|
188
|
+
* `.ascii`, `.asciz` and `.string` store a literal by code point, `\u` escapes included: four
|
|
189
|
+
hexadecimal digits naming one UTF-16 unit, and two units that make a surrogate pair name one
|
|
190
|
+
character. RARS encodes a literal one UTF-16 unit at a time, which stores a character outside the
|
|
191
|
+
Basic Multilingual Plane as `??`; here `"😀"` and `"\ud83d\ude00"` both store its four bytes.
|
|
192
|
+
* Print string (4), the path of open (1024) and the messages of the dialogs (50 to 60) are read
|
|
193
|
+
as UTF-8, the way Java 21 decodes it: a malformed sequence prints as U+FFFD. Message dialog double
|
|
194
|
+
(58), which RARS reads one byte per character, reads UTF-8 like the others.
|
|
195
|
+
* Read string (8) and input dialog string (54) store the text as UTF-8, measuring the buffer in
|
|
196
|
+
bytes: a buffer of n bytes holds at most n - 1 bytes of text, so a character can be
|
|
197
|
+
cut where the buffer ends. An unpaired surrogate is stored as `?`, as Java encodes it.
|
|
198
|
+
* Print char (11) prints the character numbered by the low byte of `a0`, and read char (12)
|
|
199
|
+
answers the first UTF-16 unit typed: 233 for `é`, 8364 for `€` and 0xd83d, a high surrogate,
|
|
200
|
+
for `😀`.
|
|
201
|
+
|
|
202
|
+
## Corrections to RARS
|
|
203
|
+
|
|
204
|
+
* GetCWD (17) writes `/`, the root that relative and absolute file paths both resolve against.
|
|
205
|
+
RARS writes the JVM's working directory, which a browser does not have.
|
|
206
|
+
* Input dialog double (53) reads its message from `a0`, as every dialog does; RARS reads `x4`.
|
|
207
|
+
* The `time` CSR, and `timeh`, its upper half on RV32, read the program time in milliseconds
|
|
208
|
+
from the `time` handler when an instruction reads them, as the time service (30) does, so a
|
|
209
|
+
scripted run's virtual clock reaches them. RARS writes the host clock into the counter after
|
|
210
|
+
every instruction. The counter reads 0 until the program reads it, and Undo puts back the reading
|
|
211
|
+
before. `cycle` and `instret` count instructions as in RARS.
|
|
120
212
|
|
|
121
213
|
## Memory observers
|
|
122
214
|
|
|
@@ -159,6 +251,89 @@ riscvSimulator.removeMemoryObservers()
|
|
|
159
251
|
* `undo()` restores memory through the same stores, so an observed range reports the restored
|
|
160
252
|
values as ordinary writes and a device that follows notifications alone stays in step.
|
|
161
253
|
|
|
254
|
+
## Random numbers
|
|
255
|
+
|
|
256
|
+
The random services draw from `java.util.Random`, implemented exactly, so a program that seeds a
|
|
257
|
+
generator with ecall 40 prints the numbers RARS prints: `nextInt` (41), `nextInt(bound)` with
|
|
258
|
+
Java's rejection of over-represented values (42), `nextFloat` (43) and `nextDouble` (44). Each
|
|
259
|
+
number in `a0` names a generator of its own.
|
|
260
|
+
|
|
261
|
+
A generator the program has not seeded starts, on its first use, from the seed the optional
|
|
262
|
+
`randomSeed` handler answers: a whole number from 0 to 2^48 - 1, as `new Random(seed)` takes it.
|
|
263
|
+
Without the handler it starts from host randomness, as in RARS; a scripted run answers with a fixed
|
|
264
|
+
seed instead and gets the same numbers every time. Handlers are shared by every `JsRiscV` instance,
|
|
265
|
+
and registering `undefined` removes one.
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
riscvSimulator.registerHandler("randomSeed", () => 42); // the same numbers on every run
|
|
269
|
+
riscvSimulator.registerHandler("randomSeed", undefined); // host randomness again
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The generators belong to the run: `initialize` forgets them, so each starts from a new seed. Undo
|
|
273
|
+
puts a generator back as it was before the service ran, so Undo then Step draws the same number.
|
|
274
|
+
|
|
275
|
+
## Running and stopping
|
|
276
|
+
|
|
277
|
+
Every run call (`step`, `simulate`, `simulateWithLimit`, `simulateWithBreakpoints`,
|
|
278
|
+
`simulateWithBreakpointsAndLimit`) resolves to a `StopReason`, the same enum `@specy/mips` exports:
|
|
279
|
+
|
|
280
|
+
| `StopReason` | When |
|
|
281
|
+
| --- | --- |
|
|
282
|
+
| `MAX_STEPS` | the instruction limit was reached; a `step` ends on it |
|
|
283
|
+
| `BREAKPOINT` | the program counter reached a breakpoint address, whose instruction has not run, or an `ebreak` ran |
|
|
284
|
+
| `NORMAL_TERMINATION` | exit (10) or exit2 (93) ran |
|
|
285
|
+
| `CLIFF_TERMINATION` | the program ran off the end of its code; the call that runs the last instruction says so |
|
|
286
|
+
|
|
287
|
+
`terminated` is read from the program's state, so it is right after `undo()` too: it is true once
|
|
288
|
+
an exit has run or there is no statement at the program counter, and `getNextStatement()` then
|
|
289
|
+
returns `null`. A program that has exited runs nothing more: every run call resolves to
|
|
290
|
+
`NORMAL_TERMINATION` again until `undo()` or `initialize()`. `exitCode` is exit2's `a0` once it has
|
|
291
|
+
run and 0 otherwise, after exit and after running off the end too; `initialize` resets it and
|
|
292
|
+
undoing the exit puts back the code before it. `getStopReason()` reports the last run call's
|
|
293
|
+
reason, `NONE` after `initialize`.
|
|
294
|
+
|
|
295
|
+
A runtime failure rejects the run call with a `RuntimeError`, an `Error` with typed fields:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
try {
|
|
299
|
+
await riscvSimulator.simulate();
|
|
300
|
+
} catch (error) {
|
|
301
|
+
if (isRuntimeError(error)) {
|
|
302
|
+
// error.kind: 'exception' | 'syscall' | 'handler' | 'internal'
|
|
303
|
+
// error.address, error.sourcePath, error.line (one-based, null without a statement)
|
|
304
|
+
console.log(`${error.sourcePath}:${error.line}: ${error.message}`);
|
|
305
|
+
// main.asm:7: Runtime exception at 0x00400018: invalid or unimplemented syscall service: 99
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`exception` is a RISC-V exception the program had no trap handler for (a misaligned or faulting
|
|
311
|
+
access, an illegal instruction, an instruction fetch outside the program), `syscall` a service that
|
|
312
|
+
refused its number, arguments or input, `handler` a host handler or memory observer that threw,
|
|
313
|
+
rejected or broke its contract (its `cause` is what it threw), and `internal` the simulator
|
|
314
|
+
failing. The message is RARS's own. A failure leaves `terminated` false and `getStopReason()` at
|
|
315
|
+
`EXCEPTION`; any other rejection, such as a run call on a program that did not assemble, is the
|
|
316
|
+
plain error it is.
|
|
317
|
+
|
|
318
|
+
## Memory layout
|
|
319
|
+
|
|
320
|
+
Programs run in RARS's memory layout: text from 0x00400000, static data from 0x10010000, the heap
|
|
321
|
+
from 0x10040000, the stack down from 0x7fffeffc and memory-mapped IO from 0xffff0000. The data
|
|
322
|
+
segment, which static data and the heap share, ends at 0x10400000.
|
|
323
|
+
|
|
324
|
+
* A RARS-dialect program keeps this layout whatever its size, as in RARS: data past
|
|
325
|
+
0x10040000 shares its addresses with the heap.
|
|
326
|
+
* A `gnu-compiler-v1` program's heap follows its static data (`.data`, `.rodata`, `.bss` and
|
|
327
|
+
common symbols): once they reach past 0x10040000, the heap starts at the first 4 KiB page after
|
|
328
|
+
them, where ld and a kernel put the break, and sbrk (9) hands out that address first. Static data
|
|
329
|
+
must fit in the data segment, 4,128,768 bytes from 0x10010000; a program with more fails to
|
|
330
|
+
assemble with `Static data ends at 0x10410000, past the end of the data segment at 0x10400000: ...`.
|
|
331
|
+
* Library members linked after a RARS-dialect program must end below 0x10040000, or the program
|
|
332
|
+
fails to assemble with `Static data reaches the heap at 0x10040000`.
|
|
333
|
+
|
|
334
|
+
`getHeapStart()` reports where the heap starts, and `initialize` empties it again. An allocator
|
|
335
|
+
built on sbrk follows the heap wherever it starts.
|
|
336
|
+
|
|
162
337
|
## API
|
|
163
338
|
|
|
164
339
|
### `RISCV` Static Class
|
|
@@ -183,13 +358,18 @@ This interface provides methods to control and interact with a RISC-V simulator
|
|
|
183
358
|
* `canUndo: boolean` (Read-only): True if an undo operation can be performed.
|
|
184
359
|
* `programCounter: number` (Read-only): The current value of the program counter (PC).
|
|
185
360
|
* `stackPointer: number` (Read-only): The current value of the stack pointer (`$sp`).
|
|
186
|
-
* `terminated: boolean` (Read-only):
|
|
361
|
+
* `terminated: boolean` (Read-only): Whether the program has ended, by an exit or by running off the end, read from its state so that it is right after `undo()`.
|
|
362
|
+
* `exitCode: number` (Read-only): exit2's code once it has run, 0 otherwise.
|
|
187
363
|
|
|
188
364
|
#### Methods
|
|
189
365
|
|
|
190
366
|
* `assemble(): RISCVAssembleResult`: Assembles the program. Returns an object containing a report, error list, and an `hasErrors` flag.
|
|
191
367
|
* `initialize(startAtMain: boolean): void`: Initializes the simulator state for execution. If `startAtMain` is true, execution begins at the `main` label; otherwise, it starts at the first instruction.
|
|
192
|
-
* `step(): Promise<StopReason>`: Executes a single instruction.
|
|
368
|
+
* `step(): Promise<StopReason>`: Executes a single instruction. See [Running and stopping](#running-and-stopping) for every run call's `StopReason` and `RuntimeError`.
|
|
369
|
+
* `getStopReason(): StopReason`: Why the last run call stopped, `NONE` after `initialize`.
|
|
370
|
+
* `getHeapStart(): number`: Where the heap, and so sbrk's first block, starts: 0x10040000, or the
|
|
371
|
+
first page after static data in a `gnu-compiler-v1` program whose static data reaches past it.
|
|
372
|
+
See [Memory layout](#memory-layout).
|
|
193
373
|
* `undo(): void`: Undoes the last instruction executed, if `canUndo` is true and undo is enabled.
|
|
194
374
|
* `setUndoEnabled(enabled: boolean): void`: Enables or disables the undo feature.
|
|
195
375
|
* `setUndoSize(size: number): void`: Sets the maximum number of steps kept in the undo history. Must be called before assembling.
|
|
@@ -210,7 +390,7 @@ This interface provides methods to control and interact with a RISC-V simulator
|
|
|
210
390
|
* `removeMemoryObserver(handle: number): void`: Removes one registration.
|
|
211
391
|
* `removeMemoryObservers(): void`: Removes every registration.
|
|
212
392
|
* `countMemoryObservers(): number`: The number of live registrations.
|
|
213
|
-
* `getNextStatement(): JsProgramStatement | null`: Returns the next `JsProgramStatement` to be executed, or `null`
|
|
393
|
+
* `getNextStatement(): JsProgramStatement | null`: Returns the next `JsProgramStatement` to be executed, or `null` once the program has terminated.
|
|
214
394
|
* `getStatementAtAddress(address: number): JsProgramStatement | null`: Gets the program statement at the given memory `address`.
|
|
215
395
|
* `getStatementsAtSourceLocation(sourcePath: string, sourceLine: number): JsProgramStatement[]`: Returns every machine statement generated from an original one-based source location, in address order. Pseudo-instructions and macro calls can produce multiple results.
|
|
216
396
|
* `getCompiledStatements(): JsProgramStatement[]`: Returns all machine statements after a successful assembly.
|
|
@@ -230,9 +410,11 @@ This interface provides methods to control and interact with a RISC-V simulator
|
|
|
230
410
|
* `RISCVSourceSet`: A read-only mapping from canonical virtual source paths to source strings.
|
|
231
411
|
* `RISCVSourceLocation`: A source path and one-based source line.
|
|
232
412
|
* `HandlerName`: Union type of all possible handler names (keys of `HandlerMap`).
|
|
413
|
+
* `StopReason`: Enum of the reasons a run call stops.
|
|
414
|
+
* `RuntimeError`: The typed error a run call rejects with when the program fails; `isRuntimeError(error)` tells it apart.
|
|
233
415
|
* `HandlerMap`: An object type mapping `HandlerName`s to their expected input argument types (`in`) and return type (`out`). This defines the signature for syscall/event handlers.
|
|
234
416
|
* `HandlerMapFns`: An object type where keys are `HandlerName`s and values are the corresponding handler functions.
|
|
235
|
-
* `DialogType`: Enum for message dialog types (`ERROR_MESSAGE`, `INFORMATION_MESSAGE`, `WARNING_MESSAGE`, `QUESTION_MESSAGE`).
|
|
417
|
+
* `DialogType`: Enum for message dialog types (`PLAIN_MESSAGE`, `ERROR_MESSAGE`, `INFORMATION_MESSAGE`, `WARNING_MESSAGE`, `QUESTION_MESSAGE`).
|
|
236
418
|
* `ConfirmResult`: Enum for confirm dialog results (`YES`, `NO`, `CANCEL`).
|
|
237
419
|
* `BackStepAction`: Enum representing types of actions that can be undone (e.g., `MEMORY_RESTORE_WORD`, `REGISTER_RESTORE`).
|
|
238
420
|
* `JsBackStep`: Interface representing an entry in the undo stack, detailing the action, parameters, and PC value.
|
|
@@ -243,3 +425,24 @@ This interface provides methods to control and interact with a RISC-V simulator
|
|
|
243
425
|
* `RISCVAssembleError`: Interface describing an assembly diagnostic with its source path, one-based line and column, warning status, and structured macro expansion trace.
|
|
244
426
|
* `RISCVAssembleResult`: Interface for the result of `assemble()`, containing a report string, list of `RISCVAssembleError`s, and a `hasErrors` boolean.
|
|
245
427
|
* `JsRiscvStackFrame`: Interface describing a frame on the call stack (PC, target address, SP, FP, register snapshot).
|
|
428
|
+
|
|
429
|
+
### Dynamic instruction identities
|
|
430
|
+
|
|
431
|
+
`getCurrentInstructionSerial(): string | null` names the instruction currently executing. Read it
|
|
432
|
+
inside a host handler (including an awaited handler) to journal host effects. The eventual
|
|
433
|
+
`JsInstructionUndoGroup.serial` and each `JsBackStep.serial` carry exactly that identity. Core-only
|
|
434
|
+
services, exits, write-free instructions and failed instructions also receive their own identity.
|
|
435
|
+
Pokes have a distinct `JsPokeUndoGroup.serial`; the current-instruction getter remains null in a Poke.
|
|
436
|
+
Serials are opaque positive decimal strings, allocated monotonically for the lifetime of this module
|
|
437
|
+
across instances, Undo, initialize and reassembly. Do not convert them to JavaScript numbers. At
|
|
438
|
+
serial-space exhaustion execution fails rather than reusing an identity.
|
|
439
|
+
|
|
440
|
+
History keeps only the bounded restore stack. The Undo size still counts raw restore slots, with a
|
|
441
|
+
Poke taking one slot; oldest instructions are evicted whole. An instruction larger than the capacity
|
|
442
|
+
is discarded whole, so a retained group always represents a complete rollback. `initialize` clears
|
|
443
|
+
retained history and Poke journals. Executing an instruction or opening a Poke with recording
|
|
444
|
+
disabled clears older retained history, preventing Undo across an unrecorded gap. No-history
|
|
445
|
+
instructions still expose a serial inside handlers. Match and retain host frames by serial against
|
|
446
|
+
`getUndoGroups`/`getUndoGroupsRange`, never by PC; drop frames whose groups are no longer retained.
|
|
447
|
+
|
|
448
|
+
`initialize` is rejected while an instruction or Poke is active.
|