@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/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,113 @@
|
|
|
1
|
+
type AssemblerProfile = 'rars' | 'gnu-compiler-v1';
|
|
2
|
+
/**
|
|
3
|
+
* GNU compiler assembly members and the index naming the member that defines each global. A Build
|
|
4
|
+
* pulls a member only to resolve a global the program uses and does not define, as ld pulls members
|
|
5
|
+
* out of an archive, and keeps pulling for the globals those members use in turn. Members are always
|
|
6
|
+
* assembled with the `gnu-compiler-v1` profile, whatever profile the program uses, and their paths
|
|
7
|
+
* never meet the program's Files.
|
|
8
|
+
*/
|
|
9
|
+
type RuntimeLibrary = {
|
|
10
|
+
/** Canonical member path to its source. */
|
|
11
|
+
members: Record<string, string>;
|
|
12
|
+
/** Global symbol to the path of the member defining it. */
|
|
13
|
+
index: Record<string, string>;
|
|
14
|
+
/**
|
|
15
|
+
* Globals the library supplies even to a weak reference, which otherwise pulls no member: GCC
|
|
16
|
+
* refers to `__cxa_pure_virtual` weakly from every abstract class's vtable.
|
|
17
|
+
*/
|
|
18
|
+
resolveWeak?: string[];
|
|
19
|
+
};
|
|
20
|
+
type AssemblyOptions = {
|
|
21
|
+
assemblerProfile?: AssemblerProfile;
|
|
22
|
+
/**
|
|
23
|
+
* The global execution starts at instead of `main`. The link must define it, and with a library
|
|
24
|
+
* it pulls its own member, as ld's entry symbol does: `_start` brings in a library's startup code.
|
|
25
|
+
*/
|
|
26
|
+
entrySymbol?: string;
|
|
27
|
+
/** Libraries searched in order: the first one whose index names a symbol supplies it. */
|
|
28
|
+
libraries?: RuntimeLibrary[];
|
|
29
|
+
};
|
|
30
|
+
/** The globals one GNU compiler unit defines and the ones it uses without defining. */
|
|
31
|
+
type GnuUnitSymbols = {
|
|
32
|
+
/** Globals the unit defines, strong or weak. */
|
|
33
|
+
defined: string[];
|
|
34
|
+
/** The defined globals that are weak. */
|
|
35
|
+
weak: string[];
|
|
36
|
+
/** Globals the unit uses without defining, other than weak references, which pull nothing. */
|
|
37
|
+
references: string[];
|
|
38
|
+
/** Why the unit could not be parsed; empty when it parsed. */
|
|
39
|
+
errors: string[];
|
|
40
|
+
};
|
|
1
41
|
type JsInstructionToken = {
|
|
2
42
|
/** One-based column in the processed source line. */
|
|
3
43
|
sourceColumn: number;
|
|
4
44
|
value: string;
|
|
5
45
|
type: string;
|
|
6
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
|
+
*/
|
|
7
52
|
declare enum StopReason {
|
|
8
|
-
|
|
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. */
|
|
9
56
|
BREAKPOINT = 0,
|
|
57
|
+
/** Never returned: a runtime failure rejects the run call with a `RuntimeError` instead. */
|
|
10
58
|
EXCEPTION = 1,
|
|
11
|
-
|
|
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. */
|
|
12
62
|
NORMAL_TERMINATION = 3,
|
|
13
|
-
|
|
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. */
|
|
14
66
|
PAUSE = 5,
|
|
67
|
+
/** Not reachable through this package. */
|
|
15
68
|
STOP = 6
|
|
16
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. */
|
|
17
109
|
declare enum DialogType {
|
|
110
|
+
PLAIN_MESSAGE = -1,
|
|
18
111
|
ERROR_MESSAGE = 0,
|
|
19
112
|
INFORMATION_MESSAGE = 1,
|
|
20
113
|
WARNING_MESSAGE = 2,
|
|
@@ -25,97 +118,114 @@ declare enum ConfirmResult {
|
|
|
25
118
|
NO = 1,
|
|
26
119
|
CANCEL = 2
|
|
27
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
|
+
*/
|
|
28
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
|
+
*/
|
|
29
133
|
openFile: {
|
|
30
134
|
in: [filename: string, flags: number, append: boolean];
|
|
31
135
|
out: number;
|
|
32
136
|
};
|
|
137
|
+
/** Syscall 57: close a file descriptor that `openFile` returned. */
|
|
33
138
|
closeFile: {
|
|
34
139
|
in: [fileDescriptor: number];
|
|
35
140
|
out: void;
|
|
36
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
|
+
*/
|
|
37
146
|
writeFile: {
|
|
38
147
|
in: [fileDescriptor: number, buffer: number[]];
|
|
39
|
-
out:
|
|
148
|
+
out: number;
|
|
40
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
|
+
*/
|
|
41
154
|
readFile: {
|
|
42
|
-
in: [fileDescriptor: number,
|
|
43
|
-
out: [
|
|
155
|
+
in: [fileDescriptor: number, length: number];
|
|
156
|
+
out: [count: number, buffer: number[]];
|
|
44
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
|
+
*/
|
|
45
162
|
confirm: {
|
|
46
163
|
in: [message: string];
|
|
47
164
|
out: ConfirmResult;
|
|
48
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
|
+
*/
|
|
49
171
|
inputDialog: {
|
|
50
172
|
in: [message: string];
|
|
51
|
-
out: string;
|
|
173
|
+
out: string | null;
|
|
52
174
|
};
|
|
175
|
+
/** Syscalls 55, 56 and 58 to 60: show a message. The program waits until the handler settles. */
|
|
53
176
|
outputDialog: {
|
|
54
177
|
in: [message: string, type: DialogType];
|
|
55
178
|
out: void;
|
|
56
179
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
in: [message: string];
|
|
63
|
-
out: number;
|
|
64
|
-
};
|
|
65
|
-
askInt: {
|
|
66
|
-
in: [message: string];
|
|
67
|
-
out: number;
|
|
68
|
-
};
|
|
69
|
-
askString: {
|
|
70
|
-
in: [message: string];
|
|
71
|
-
out: string;
|
|
72
|
-
};
|
|
73
|
-
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: {
|
|
74
185
|
in: [];
|
|
75
|
-
out:
|
|
186
|
+
out: string;
|
|
76
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
|
+
*/
|
|
77
192
|
readFloat: {
|
|
78
193
|
in: [];
|
|
79
|
-
out:
|
|
194
|
+
out: string;
|
|
80
195
|
};
|
|
81
|
-
|
|
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: {
|
|
82
201
|
in: [];
|
|
83
|
-
out:
|
|
202
|
+
out: string;
|
|
84
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
|
+
*/
|
|
85
209
|
readString: {
|
|
86
210
|
in: [];
|
|
87
211
|
out: string;
|
|
88
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
|
+
*/
|
|
89
218
|
readChar: {
|
|
90
219
|
in: [];
|
|
91
220
|
out: string;
|
|
92
221
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
in: [message: string];
|
|
99
|
-
out: void;
|
|
100
|
-
};
|
|
101
|
-
printChar: {
|
|
102
|
-
in: [c: string];
|
|
103
|
-
out: void;
|
|
104
|
-
};
|
|
105
|
-
printDouble: {
|
|
106
|
-
in: [d: number];
|
|
107
|
-
out: void;
|
|
108
|
-
};
|
|
109
|
-
printFloat: {
|
|
110
|
-
in: [f: number];
|
|
111
|
-
out: void;
|
|
112
|
-
};
|
|
113
|
-
printInt: {
|
|
114
|
-
in: [i: number];
|
|
115
|
-
out: void;
|
|
116
|
-
};
|
|
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
|
+
*/
|
|
117
227
|
printString: {
|
|
118
|
-
in: [
|
|
228
|
+
in: [text: string];
|
|
119
229
|
out: void;
|
|
120
230
|
};
|
|
121
231
|
/**
|
|
@@ -127,26 +237,56 @@ type HandlerMap = {
|
|
|
127
237
|
out: void;
|
|
128
238
|
};
|
|
129
239
|
/**
|
|
130
|
-
* Syscall 30
|
|
131
|
-
*
|
|
132
|
-
*
|
|
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.
|
|
133
244
|
*/
|
|
134
245
|
time: {
|
|
135
246
|
in: [];
|
|
136
247
|
out: number;
|
|
137
248
|
};
|
|
249
|
+
/**
|
|
250
|
+
* A read of standard input (descriptor 0): answer with the bytes left over from the current
|
|
251
|
+
* line or else one new line, at most `length` of them, as `[count, bytes]`. A count of 0 is end
|
|
252
|
+
* of input and -1 a failed read. Return a promise to wait for the user.
|
|
253
|
+
*/
|
|
138
254
|
stdIn: {
|
|
139
|
-
in: [
|
|
140
|
-
out:
|
|
255
|
+
in: [length: number];
|
|
256
|
+
out: [count: number, buffer: number[]];
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Syscall 62 (lseek): move an open file's position. `whence` is 0 from the start, 1 from the
|
|
260
|
+
* current position, 2 from the end. Answer with the new position, or -1 when the seek fails.
|
|
261
|
+
*/
|
|
262
|
+
seekFile: {
|
|
263
|
+
in: [fileDescriptor: number, offset: number, whence: number];
|
|
264
|
+
out: number;
|
|
141
265
|
};
|
|
266
|
+
/** Syscall 64 on descriptor 1: the bytes written to standard output. */
|
|
142
267
|
stdOut: {
|
|
143
268
|
in: [buffer: number[]];
|
|
144
269
|
out: void;
|
|
145
270
|
};
|
|
271
|
+
/** Syscall 64 on descriptor 2: the bytes written to standard error. */
|
|
146
272
|
stdErr: {
|
|
147
273
|
in: [buffer: number[]];
|
|
148
274
|
out: void;
|
|
149
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
|
+
};
|
|
150
290
|
};
|
|
151
291
|
/**
|
|
152
292
|
* Notified after a write anywhere in an observed address range.
|
|
@@ -211,8 +351,14 @@ type RISCVAssembleResult = {
|
|
|
211
351
|
hasWarnings: boolean;
|
|
212
352
|
};
|
|
213
353
|
declare class RISCV {
|
|
354
|
+
static readonly assemblerProfiles: readonly AssemblerProfile[];
|
|
214
355
|
static makeRiscVFromFiles: typeof makeRiscVFromFiles;
|
|
215
356
|
static initializeRISCV: typeof initializeRISCV;
|
|
357
|
+
/**
|
|
358
|
+
* The globals one `gnu-compiler-v1` unit defines and uses, without laying it out or touching
|
|
359
|
+
* memory, for building a library index. Uses the current width, so call `setIs64Bit` first.
|
|
360
|
+
*/
|
|
361
|
+
static analyzeGnuUnit(path: string, source: string): GnuUnitSymbols;
|
|
216
362
|
static getInstructionSet(): JsInstruction[];
|
|
217
363
|
static setIs64Bit(is64Bit: boolean): void;
|
|
218
364
|
static is64Bit(): boolean;
|
|
@@ -302,12 +448,26 @@ declare enum BackStepAction {
|
|
|
302
448
|
* A whole Poke: every value one `beginPoke`/`endPoke` transaction wrote, restored together.
|
|
303
449
|
* A Poke is a single back step, so it takes one slot of the undo size whatever it wrote.
|
|
304
450
|
*/
|
|
305
|
-
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
|
|
306
464
|
}
|
|
307
465
|
/**
|
|
308
466
|
* Represents a back step in the simulation undo stack.
|
|
309
467
|
*/
|
|
310
468
|
interface JsBackStep {
|
|
469
|
+
/** Opaque decimal identity, unique across Undo, initialize and reassembly in this module. */
|
|
470
|
+
readonly serial: string;
|
|
311
471
|
/**
|
|
312
472
|
* The action performed (e.g., register write, memory write).
|
|
313
473
|
*/
|
|
@@ -337,8 +497,10 @@ interface JsBackStep {
|
|
|
337
497
|
* was made, in the low order bits.
|
|
338
498
|
* - `PC_RESTORE`: the address the restore puts back - the same value as `param1`, which is
|
|
339
499
|
* where this action has always kept it, its `param2` being unused and 0.
|
|
500
|
+
* - `EXIT_RESTORE`: the exit code the exit replaced.
|
|
340
501
|
* - `'0'` for `CONTROL_AND_STATUS_COUNTERS_DECREMENT`, `DO_NOTHING` and `POKE`, which replace
|
|
341
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.
|
|
342
504
|
*/
|
|
343
505
|
readonly oldValue: string;
|
|
344
506
|
/**
|
|
@@ -348,9 +510,9 @@ interface JsBackStep {
|
|
|
348
510
|
*
|
|
349
511
|
* `CONTROL_AND_STATUS_REGISTER_BACKDOOR` is the simulator sampling the wall clock into `time`
|
|
350
512
|
* rather than a program writing a register, but it is a write all the same and reports the
|
|
351
|
-
* millisecond reading it stored. `'0'` for
|
|
352
|
-
* `DO_NOTHING`, which write no value of their own,
|
|
353
|
-
* 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`.
|
|
354
516
|
*/
|
|
355
517
|
readonly newValue: string;
|
|
356
518
|
/**
|
|
@@ -410,6 +572,8 @@ type JsUndoGroup = JsInstructionUndoGroup | JsPokeUndoGroup;
|
|
|
410
572
|
* overwrote, and the `cycle`/`instret` decrement every instruction pushes on top of them.
|
|
411
573
|
*/
|
|
412
574
|
type JsInstructionUndoGroup = {
|
|
575
|
+
/** Opaque decimal identity, unique across Undo, initialize and reassembly in this module. */
|
|
576
|
+
readonly serial: string;
|
|
413
577
|
readonly kind: 'instruction';
|
|
414
578
|
readonly pc: number;
|
|
415
579
|
/** The instruction's back steps, newest first. */
|
|
@@ -424,6 +588,8 @@ type JsInstructionUndoGroup = {
|
|
|
424
588
|
* alone.
|
|
425
589
|
*/
|
|
426
590
|
type JsPokeUndoGroup = {
|
|
591
|
+
/** Opaque decimal identity, unique across Undo, initialize and reassembly in this module. */
|
|
592
|
+
readonly serial: string;
|
|
427
593
|
readonly kind: 'poke';
|
|
428
594
|
readonly pc: -1;
|
|
429
595
|
/** The single back step the Poke is: one slot of the history, whatever the Poke wrote. */
|
|
@@ -487,8 +653,13 @@ type HandlerName = keyof HandlerMap;
|
|
|
487
653
|
* user, reading a file, awaiting a worker) without blocking the event loop. If the promise
|
|
488
654
|
* rejects, the pending `step`/`simulate*` call rejects too.
|
|
489
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';
|
|
490
659
|
type HandlerMapFns = {
|
|
491
|
-
[K in HandlerName]:
|
|
660
|
+
[K in Exclude<HandlerName, OptionalHandlerName>]: HandlerFn<K>;
|
|
661
|
+
} & {
|
|
662
|
+
[K in OptionalHandlerName]?: HandlerFn<K>;
|
|
492
663
|
};
|
|
493
664
|
declare function registerHandlers(riscv: JsRiscV, handlers: HandlerMapFns): void;
|
|
494
665
|
declare function unimplementedHandler(name: HandlerName): () => never;
|
|
@@ -501,23 +672,39 @@ interface JsRiscV {
|
|
|
501
672
|
*/
|
|
502
673
|
assemble(): RISCVAssembleResult;
|
|
503
674
|
/**
|
|
504
|
-
* 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.
|
|
505
678
|
* @param startAtMain If true, starts execution at the 'main' label. Otherwise, starts at the first instruction.
|
|
506
679
|
*/
|
|
507
680
|
initialize(startAtMain: boolean): void;
|
|
508
681
|
/**
|
|
509
|
-
*
|
|
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.
|
|
510
685
|
*/
|
|
511
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;
|
|
512
693
|
/**
|
|
513
694
|
* Executes a single instruction.
|
|
514
695
|
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
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.
|
|
517
702
|
*/
|
|
518
703
|
step(): Promise<StopReason>;
|
|
519
704
|
/**
|
|
520
|
-
* Sets the
|
|
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.
|
|
521
708
|
* @param size
|
|
522
709
|
*/
|
|
523
710
|
setUndoSize(size: number): void;
|
|
@@ -556,32 +743,47 @@ interface JsRiscV {
|
|
|
556
743
|
* @returns The label at the given address, or null if no label is found.
|
|
557
744
|
*/
|
|
558
745
|
getLabelAtAddress(address: number): string | null;
|
|
746
|
+
/** Address of a defined label/alias, or -1 if absent. Requires successful assembly. */
|
|
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;
|
|
559
757
|
/**
|
|
560
758
|
* Sets whether the undo feature is enabled.
|
|
561
759
|
* @param enabled True to enable the undo feature, false to disable it.
|
|
562
760
|
*/
|
|
563
761
|
setUndoEnabled(enabled: boolean): void;
|
|
564
762
|
/**
|
|
565
|
-
* Simulates until the
|
|
763
|
+
* Simulates until the program stops: an exit, running off the end, an `ebreak`, or a failure,
|
|
764
|
+
* which rejects with a `RuntimeError`.
|
|
566
765
|
*/
|
|
567
766
|
simulate(): Promise<StopReason>;
|
|
568
767
|
/**
|
|
569
768
|
* Simulates the program for a limited number of instructions.
|
|
570
769
|
* @param limit The maximum number of instructions to execute.
|
|
571
|
-
* @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.
|
|
572
772
|
*/
|
|
573
773
|
simulateWithLimit(limit: number): Promise<StopReason>;
|
|
574
774
|
/**
|
|
575
775
|
* Simulates the program until a breakpoint is reached.
|
|
576
776
|
* @param breakpoints An array of memory addresses where the simulation should pause.
|
|
577
|
-
* @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.
|
|
578
779
|
*/
|
|
579
780
|
simulateWithBreakpoints(breakpoints: number[]): Promise<StopReason>;
|
|
580
781
|
/**
|
|
581
782
|
* Simulates the program with both breakpoints and a limit.
|
|
582
783
|
* @param breakpoints An array of memory addresses where the simulation should pause.
|
|
583
784
|
* @param limit The maximum number of instructions to execute.
|
|
584
|
-
* @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.
|
|
585
787
|
*/
|
|
586
788
|
simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): Promise<StopReason>;
|
|
587
789
|
/**
|
|
@@ -597,11 +799,12 @@ interface JsRiscV {
|
|
|
597
799
|
*/
|
|
598
800
|
getRegisterValueLong(register: RegisterName): string;
|
|
599
801
|
/**
|
|
600
|
-
* 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`.
|
|
601
804
|
* @param name The name of the event or condition.
|
|
602
805
|
* @param handler The handler function to be called when the event occurs. The function signature depends on the event name.
|
|
603
806
|
*/
|
|
604
|
-
registerHandler<T extends HandlerName>(name: T, handler:
|
|
807
|
+
registerHandler<T extends HandlerName>(name: T, handler: HandlerFn<T> | (T extends OptionalHandlerName ? undefined : never)): void;
|
|
605
808
|
/**
|
|
606
809
|
* Gets the current value of the stack pointer.
|
|
607
810
|
* @returns The value of the stack pointer.
|
|
@@ -692,6 +895,24 @@ interface JsRiscV {
|
|
|
692
895
|
* carries what it changed, with the old and the new value of each write.
|
|
693
896
|
*/
|
|
694
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;
|
|
907
|
+
/**
|
|
908
|
+
* The newest `max` entries of `getUndoGroups`, read from the top of the history without
|
|
909
|
+
* grouping the rest of it: what a panel showing the latest steps of a large history wants.
|
|
910
|
+
*/
|
|
911
|
+
getUndoGroupsUpTo(max: number): JsUndoGroup[];
|
|
912
|
+
/** `max` entries of `getUndoGroups` after the newest `skip`, which are counted but never built. */
|
|
913
|
+
getUndoGroupsRange(skip: number, max: number): JsUndoGroup[];
|
|
914
|
+
/** How many entries `undo()` can still pop, counted the way `getUndoGroups` groups them. */
|
|
915
|
+
getUndoDepth(): number;
|
|
695
916
|
/**
|
|
696
917
|
* Opens a Poke: a register or memory value changed by the host between two instructions,
|
|
697
918
|
* recorded in this history as a step of its own.
|
|
@@ -790,9 +1011,10 @@ interface JsRiscV {
|
|
|
790
1011
|
countMemoryObservers(): number;
|
|
791
1012
|
/**
|
|
792
1013
|
* Gets the next statement to be executed.
|
|
793
|
-
* @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.
|
|
794
1016
|
*/
|
|
795
|
-
getNextStatement(): JsProgramStatement;
|
|
1017
|
+
getNextStatement(): JsProgramStatement | null;
|
|
796
1018
|
/**
|
|
797
1019
|
* Sets the value of a register. Outside a Poke the write is direct: it records no undo step.
|
|
798
1020
|
* Inside a Poke it joins the open transaction, except for `zero`, which holds no value and so
|
|
@@ -802,10 +1024,12 @@ interface JsRiscV {
|
|
|
802
1024
|
*/
|
|
803
1025
|
setRegisterValue(register: RegisterName, high: number, low: number): void;
|
|
804
1026
|
/**
|
|
805
|
-
*
|
|
806
|
-
*
|
|
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.
|
|
807
1031
|
*/
|
|
808
|
-
terminated: boolean;
|
|
1032
|
+
readonly terminated: boolean;
|
|
809
1033
|
}
|
|
810
1034
|
/**
|
|
811
1035
|
* Creates a RISC-V simulator from a virtual source tree and its entry file.
|
|
@@ -814,9 +1038,10 @@ interface JsRiscV {
|
|
|
814
1038
|
* snapshotted by this call; only the entry file and files reached through `.include` are assembled.
|
|
815
1039
|
* @param files Source text keyed by canonical source path.
|
|
816
1040
|
* @param entryFile Canonical path of the file from which include expansion starts.
|
|
1041
|
+
* @param options The assembler profile, the entry symbol and the libraries to link; see `AssemblyOptions`.
|
|
817
1042
|
* @returns A new `JsRiscV` object.
|
|
818
1043
|
*/
|
|
819
|
-
declare function makeRiscVFromFiles(files: RISCVSourceSet, entryFile: string): JsRiscV;
|
|
1044
|
+
declare function makeRiscVFromFiles(files: RISCVSourceSet, entryFile: string, options?: AssemblyOptions): JsRiscV;
|
|
820
1045
|
/**
|
|
821
1046
|
* Initializes the RISCV simulator.
|
|
822
1047
|
*/
|
|
@@ -829,4 +1054,4 @@ declare function bigintToHighLow(value: bigint): [high: number, low: number];
|
|
|
829
1054
|
*/
|
|
830
1055
|
declare function highLowToBigint(high: number, low: number): bigint;
|
|
831
1056
|
|
|
832
|
-
export { BackStepAction, ConfirmResult, type CsrRegisterName, DialogType, type FloatingPointRegisterName, 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, 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 };
|