@specy/risc-v 1.1.0 → 2.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 +180 -151
- package/dist/index.d.mts +26 -13
- package/dist/index.d.ts +26 -13
- package/dist/index.js +37730 -33438
- package/dist/index.mjs +37730 -33438
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,152 +1,181 @@
|
|
|
1
|
-
# RiscV-js
|
|
2
|
-
This is a Typescript implementation of a RISC-V simulator made by compiling the [RARS RISC-V simulator](https://github.com/TheThirdOne/rars) to Javascript.
|
|
3
|
-
It is part of a family of javascript assembly interpreters/simulators:
|
|
4
|
-
|
|
5
|
-
- MIPS: [git repo](https://github.com/Specy/mars), [npm package](https://www.npmjs.com/package/@specy/mips)
|
|
6
|
-
- RISC-V: [git repo](https://github.com/Specy/rars), [npm package](https://www.npmjs.com/package/@specy/risc-v)
|
|
7
|
-
- X86: [git repo](https://github.com/Specy/x86-js), [npm package](https://www.npmjs.com/package/@specy/x86)
|
|
8
|
-
- M68K: [git repo](https://github.com/Specy/s68k), [npm package](https://www.npmjs.com/package/@specy/s68k)
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
|
|
12
|
-
First, create an instance of the simulator with the `RISCV.makeRiscvFromSource` function.
|
|
13
|
-
|
|
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
|
-
|
|
16
|
-
⚠️**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
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
import {RISCV, JsRiscV, RegisterName, BackStepAction} from '@specy/risc-v';
|
|
20
|
-
const sourceCode = `
|
|
21
|
-
li t0, 5 # Load immediate value 5 into register t0
|
|
22
|
-
li t1, 7 # Load immediate value 7 into register t1
|
|
23
|
-
add t2, t0, t1 # Add t0 and t1, store result in t2
|
|
24
|
-
`;
|
|
25
|
-
|
|
26
|
-
// RISCV.setIs64Bit(true); // Set to 64-bit mode if needed
|
|
27
|
-
const riscvSimulator: JsRiscV = RISCV.makeRiscVFromSource(sourceCode);
|
|
28
|
-
|
|
29
|
-
riscvSimulator.assemble();
|
|
30
|
-
riscvSimulator.initialize(true); // Start at 'main'
|
|
31
|
-
|
|
32
|
-
while (!riscvSimulator.terminated) {
|
|
33
|
-
riscvSimulator.step();
|
|
34
|
-
}
|
|
35
|
-
// or riscvSimulator.simulate()
|
|
36
|
-
|
|
37
|
-
const pc = riscvSimulator.programCounter;
|
|
38
|
-
const t2 = riscvSimulator.getRegisterValue('t2');
|
|
39
|
-
|
|
40
|
-
console.log(`Program Counter: ${pc}`);
|
|
41
|
-
console.log(`t2: ${t2}`);
|
|
42
|
-
|
|
43
|
-
// Accessing memory:
|
|
44
|
-
const data = riscvSimulator.readMemoryBytes(0xffff0000, 4); // Read 4 bytes from address 0xffff0000
|
|
45
|
-
riscvSimulator.setMemoryBytes(0xffff0000, [0x01, 0x02, 0x03, 0x04]); // Write 4 bytes to address 0xffff0000
|
|
46
|
-
|
|
47
|
-
// Registering Handlers (for syscalls and other events):
|
|
48
|
-
riscvSimulator.registerHandler("printInt", (value: number) => {
|
|
49
|
-
console.log("printInt syscall called with:", value);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
riscvSimulator.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
* `
|
|
112
|
-
|
|
113
|
-
* `
|
|
114
|
-
|
|
115
|
-
* `
|
|
116
|
-
|
|
117
|
-
* `
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
* `
|
|
127
|
-
* `
|
|
128
|
-
* `
|
|
129
|
-
* `
|
|
130
|
-
|
|
131
|
-
####
|
|
132
|
-
|
|
133
|
-
* `
|
|
134
|
-
* `
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
* `
|
|
139
|
-
* `
|
|
140
|
-
* `
|
|
141
|
-
* `
|
|
142
|
-
* `
|
|
143
|
-
* `
|
|
144
|
-
* `
|
|
145
|
-
* `
|
|
146
|
-
* `
|
|
147
|
-
* `
|
|
148
|
-
* `
|
|
149
|
-
* `
|
|
150
|
-
* `
|
|
151
|
-
* `
|
|
1
|
+
# RiscV-js
|
|
2
|
+
This is a Typescript implementation of a RISC-V simulator made by compiling the [RARS RISC-V simulator](https://github.com/TheThirdOne/rars) to Javascript.
|
|
3
|
+
It is part of a family of javascript assembly interpreters/simulators:
|
|
4
|
+
|
|
5
|
+
- MIPS: [git repo](https://github.com/Specy/mars), [npm package](https://www.npmjs.com/package/@specy/mips)
|
|
6
|
+
- RISC-V: [git repo](https://github.com/Specy/rars), [npm package](https://www.npmjs.com/package/@specy/risc-v)
|
|
7
|
+
- X86: [git repo](https://github.com/Specy/x86-js), [npm package](https://www.npmjs.com/package/@specy/x86)
|
|
8
|
+
- M68K: [git repo](https://github.com/Specy/s68k), [npm package](https://www.npmjs.com/package/@specy/s68k)
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
First, create an instance of the simulator with the `RISCV.makeRiscvFromSource` function.
|
|
13
|
+
|
|
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
|
+
|
|
16
|
+
⚠️**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
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import {RISCV, JsRiscV, RegisterName, BackStepAction} from '@specy/risc-v';
|
|
20
|
+
const sourceCode = `
|
|
21
|
+
li t0, 5 # Load immediate value 5 into register t0
|
|
22
|
+
li t1, 7 # Load immediate value 7 into register t1
|
|
23
|
+
add t2, t0, t1 # Add t0 and t1, store result in t2
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
// RISCV.setIs64Bit(true); // Set to 64-bit mode if needed
|
|
27
|
+
const riscvSimulator: JsRiscV = RISCV.makeRiscVFromSource(sourceCode);
|
|
28
|
+
|
|
29
|
+
riscvSimulator.assemble();
|
|
30
|
+
riscvSimulator.initialize(true); // Start at 'main'
|
|
31
|
+
|
|
32
|
+
while (!riscvSimulator.terminated) {
|
|
33
|
+
await riscvSimulator.step();
|
|
34
|
+
}
|
|
35
|
+
// or await riscvSimulator.simulate()
|
|
36
|
+
|
|
37
|
+
const pc = riscvSimulator.programCounter;
|
|
38
|
+
const t2 = riscvSimulator.getRegisterValue('t2');
|
|
39
|
+
|
|
40
|
+
console.log(`Program Counter: ${pc}`);
|
|
41
|
+
console.log(`t2: ${t2}`);
|
|
42
|
+
|
|
43
|
+
// Accessing memory:
|
|
44
|
+
const data = riscvSimulator.readMemoryBytes(0xffff0000, 4); // Read 4 bytes from address 0xffff0000
|
|
45
|
+
riscvSimulator.setMemoryBytes(0xffff0000, [0x01, 0x02, 0x03, 0x04]); // Write 4 bytes to address 0xffff0000
|
|
46
|
+
|
|
47
|
+
// Registering Handlers (for syscalls and other events):
|
|
48
|
+
riscvSimulator.registerHandler("printInt", (value: number) => {
|
|
49
|
+
console.log("printInt syscall called with:", value);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Handlers may also be async: returning a promise suspends the simulation until it settles,
|
|
53
|
+
// so IO can be backed by an async API without blocking the event loop.
|
|
54
|
+
riscvSimulator.registerHandler("readInt", async () => {
|
|
55
|
+
return await promptUserForANumber();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Accessing the undo stack:
|
|
59
|
+
const undoStack = riscvSimulator.getUndoStack();
|
|
60
|
+
undoStack.forEach(step => {
|
|
61
|
+
if (step.action === BackStepAction.REGISTER_RESTORE) {
|
|
62
|
+
console.log(`Register restored at PC ${step.pc}`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
// Simulating with breakpoints:
|
|
68
|
+
const breakpoints = [0x00400004, 0x00400008]; // Example breakpoint addresses
|
|
69
|
+
await riscvSimulator.simulateWithBreakpoints(breakpoints);
|
|
70
|
+
|
|
71
|
+
//Simulating with a limit
|
|
72
|
+
const limit = 100
|
|
73
|
+
await riscvSimulator.simulateWithLimit(limit);
|
|
74
|
+
|
|
75
|
+
//Simulating with breakpoints and a limit
|
|
76
|
+
await riscvSimulator.simulateWithBreakpointsAndLimit(breakpoints, limit);
|
|
77
|
+
|
|
78
|
+
//Setting Register Values:
|
|
79
|
+
riscvSimulator.setRegisterValue("t0", 42);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## IO handlers
|
|
83
|
+
|
|
84
|
+
Every syscall that needs to talk to the outside world goes through a handler you register. A
|
|
85
|
+
handler can return its result directly, or return a promise of it:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
riscvSimulator.registerHandler("readInt", () => 42); // synchronous
|
|
89
|
+
riscvSimulator.registerHandler("readString", () => fetchLine()); // asynchronous
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
When a handler returns a promise the simulation suspends at that instruction and resumes once the
|
|
93
|
+
promise settles, so nothing has to be shoehorned into a synchronous API. Because of that,
|
|
94
|
+
`step`, `simulate`, `simulateWithLimit`, `simulateWithBreakpoints` and
|
|
95
|
+
`simulateWithBreakpointsAndLimit` all return a promise. Everything else on `JsRiscV` (registers,
|
|
96
|
+
memory, statements, the undo stack) stays synchronous.
|
|
97
|
+
|
|
98
|
+
If a handler's promise rejects, the pending `step`/`simulate*` call rejects as well, with the
|
|
99
|
+
rejection reason in the message.
|
|
100
|
+
|
|
101
|
+
Synchronous handlers never yield to the event loop: the simulation runs straight through, and the
|
|
102
|
+
returned promise settles on a microtask. Overlapping calls are queued and run one after another,
|
|
103
|
+
never concurrently.
|
|
104
|
+
|
|
105
|
+
## API
|
|
106
|
+
|
|
107
|
+
### `RISCV` Static Class
|
|
108
|
+
|
|
109
|
+
Provides static methods to initialize and create simulator instances.
|
|
110
|
+
|
|
111
|
+
* `RISCV.makeRiscvFromSource(source: string): JsRiscv`
|
|
112
|
+
Creates a new `JsRiscv` instance from RISC-V assembly source code. This also initializes the underlying RISC-V simulation environment if it hasn't been already.
|
|
113
|
+
* `RISCV.initializeRISCV(): void`
|
|
114
|
+
Initializes the core RISC-V simulation environment. Called internally by `makeRiscvFromSource`, but can be called explicitly if needed.
|
|
115
|
+
* `RISCV.getInstructionSet(): JsInstruction[]`
|
|
116
|
+
Returns an array of objects, each describing a supported RISC-V instruction (name, example, description, tokens).
|
|
117
|
+
* `RISCV.setIs64Bit(is64Bit: boolean): void`
|
|
118
|
+
Sets the simulator to operate in 32-bit or 64-bit mode. This affects the instruction set.
|
|
119
|
+
|
|
120
|
+
### `JsRiscv` Interface
|
|
121
|
+
|
|
122
|
+
This interface provides methods to control and interact with a RISC-V simulator instance.
|
|
123
|
+
|
|
124
|
+
#### Properties
|
|
125
|
+
|
|
126
|
+
* `canUndo: boolean` (Read-only): True if an undo operation can be performed.
|
|
127
|
+
* `programCounter: number` (Read-only): The current value of the program counter (PC).
|
|
128
|
+
* `stackPointer: number` (Read-only): The current value of the stack pointer (`$sp`).
|
|
129
|
+
* `terminated: boolean` (Read-only): True if the simulation has terminated (e.g., via an exit syscall or error).
|
|
130
|
+
|
|
131
|
+
#### Methods
|
|
132
|
+
|
|
133
|
+
* `assemble(): RISCVAssembleResult`: Assembles the program. Returns an object containing a report, error list, and an `hasErrors` flag.
|
|
134
|
+
* `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.
|
|
135
|
+
* `step(): Promise<StopReason>`: Executes a single instruction. Resolves to the reason the simulation stopped.
|
|
136
|
+
* `undo(): void`: Undoes the last instruction executed, if `canUndo` is true and undo is enabled.
|
|
137
|
+
* `setUndoEnabled(enabled: boolean): void`: Enables or disables the undo feature.
|
|
138
|
+
* `setUndoSize(size: number): void`: Sets the maximum number of steps kept in the undo history. Must be called before assembling.
|
|
139
|
+
* `simulateWithLimit(limit: number): Promise<StopReason>`: Simulates the program for a maximum of `limit` instructions. Resolves to the reason the simulation stopped.
|
|
140
|
+
* `simulateWithBreakpoints(breakpoints: number[]): Promise<StopReason>`: Simulates the program until a breakpoint is reached or the program terminates. `breakpoints` is an array of memory addresses. Resolves to the reason the simulation stopped.
|
|
141
|
+
* `simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): Promise<StopReason>`: Simulates until a breakpoint, limit is reached, or termination. Resolves to the reason the simulation stopped.
|
|
142
|
+
* `getRegisterValue(register: RegisterName): number`: Returns the value of the specified register.
|
|
143
|
+
* `setRegisterValue(register: RegisterName, value: number): void`: Sets the value of the specified register.
|
|
144
|
+
* `getRegistersValues(): number[]`: Returns an array of all general-purpose register values. The order might be implementation-defined but usually corresponds to register numbers 0-31.
|
|
145
|
+
* `getConditionFlags(): number[]`: Gets the 8 condition flags (if applicable, typically related to floating-point or custom extensions).
|
|
146
|
+
* `registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out'] | Promise<HandlerMap[T]['out']>): void`: Registers a handler function for a specific event (e.g., syscalls). See `HandlerName`, `HandlerMap` and [IO handlers](#io-handlers) for details.
|
|
147
|
+
* `getUndoStack(): JsBackStep[]`: Returns the undo stack, an array of `JsBackStep` objects representing simulation history.
|
|
148
|
+
* `readMemoryBytes(address: number, length: number): number[]`: Reads `length` bytes from memory starting at `address`. Returns an array of byte values.
|
|
149
|
+
* `setMemoryBytes(address: number, bytes: number[]): void`: Writes an array of `bytes` to memory starting at `address`.
|
|
150
|
+
* `getCurrentStatementIndex(): number`: Returns the index of the current instruction in the list of assembled program statements.
|
|
151
|
+
* `getNextStatement(): JsProgramStatement | null`: Returns the next `JsProgramStatement` to be executed, or `null` if at the end.
|
|
152
|
+
* `getStatementAtAddress(address: number): JsProgramStatement | null`: Gets the program statement at the given memory `address`.
|
|
153
|
+
* `getStatementAtSourceLine(line: number): JsProgramStatement | null`: Gets the program statement corresponding to the original source `line` number.
|
|
154
|
+
* `getCompiledStatements(): JsProgramStatement[]`: Returns an array of all assembled program statements.
|
|
155
|
+
* `getParsedStatements(): JsInstructionToken[]`: Returns an array of tokens from the initial parsing stage.
|
|
156
|
+
* `getTokenizedLines(): RiscvTokenizedLine[]`: Returns an array of lines, each with its source string and corresponding tokens.
|
|
157
|
+
* `getCallStack(): JsRiscvStackFrame[]`: Returns the current call stack as an array of stack frame objects.
|
|
158
|
+
* `getLabelAtAddress(address: number): string | null`: Returns the label name at the given memory `address`, or `null` if no label exists there.
|
|
159
|
+
|
|
160
|
+
#### Helper Functions (for use with `JsRiscv`)
|
|
161
|
+
|
|
162
|
+
* `registerHandlers(riscv: JsRiscv, handlers: Partial<HandlerMapFns>): void`: A utility to register multiple handlers at once.
|
|
163
|
+
* `unimplementedHandler(name: HandlerName): (...args: any[]) => any`: Returns a function that throws an error indicating the handler `name` is not implemented. Useful for stubbing handlers.
|
|
164
|
+
|
|
165
|
+
#### Types
|
|
166
|
+
|
|
167
|
+
* `RegisterName`: Union type for RISC-V register names (e.g., `'ra'`, `'sp'`, `'a0'`, `'t0'`, etc.).
|
|
168
|
+
* `HandlerName`: Union type of all possible handler names (keys of `HandlerMap`).
|
|
169
|
+
* `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.
|
|
170
|
+
* `HandlerMapFns`: An object type where keys are `HandlerName`s and values are the corresponding handler functions.
|
|
171
|
+
* `DialogType`: Enum for message dialog types (`ERROR_MESSAGE`, `INFORMATION_MESSAGE`, `WARNING_MESSAGE`, `QUESTION_MESSAGE`).
|
|
172
|
+
* `ConfirmResult`: Enum for confirm dialog results (`YES`, `NO`, `CANCEL`).
|
|
173
|
+
* `BackStepAction`: Enum representing types of actions that can be undone (e.g., `MEMORY_RESTORE_WORD`, `REGISTER_RESTORE`).
|
|
174
|
+
* `JsBackStep`: Interface representing an entry in the undo stack, detailing the action, parameters, and PC value.
|
|
175
|
+
* `JsProgramStatement`: Interface representing an assembled instruction, including source line, address, binary/machine/assembly representations.
|
|
176
|
+
* `JsInstructionToken`: Interface representing a token from the assembly source (value, type, source position).
|
|
177
|
+
* `JsInstruction`: Interface describing a RISC-V instruction (name, example, description, token patterns).
|
|
178
|
+
* `RiscvTokenizedLine`: An object containing the original line string and its parsed `JsInstructionToken`s.
|
|
179
|
+
* `RISCVAssembleError`: Interface describing an assembly error (message, line/column, warning status, etc.).
|
|
180
|
+
* `RISCVAssembleResult`: Interface for the result of `assemble()`, containing a report string, list of `RISCVAssembleError`s, and a `hasErrors` boolean.
|
|
152
181
|
* `JsRiscvStackFrame`: Interface describing a frame on the call stack (PC, target address, SP, FP, register snapshot).
|
package/dist/index.d.mts
CHANGED
|
@@ -6,6 +6,7 @@ type JsInstructionToken = {
|
|
|
6
6
|
type: string;
|
|
7
7
|
};
|
|
8
8
|
declare enum StopReason {
|
|
9
|
+
NONE = -1,// no simulation has run yet
|
|
9
10
|
BREAKPOINT = 0,
|
|
10
11
|
EXCEPTION = 1,
|
|
11
12
|
MAX_STEPS = 2,// includes step mode (where maxSteps is 1)
|
|
@@ -40,7 +41,7 @@ type HandlerMap = {
|
|
|
40
41
|
};
|
|
41
42
|
readFile: {
|
|
42
43
|
in: [fileDescriptor: number, destination: number[], length: number];
|
|
43
|
-
out: number;
|
|
44
|
+
out: [readOrEof: number, buffer: number[]];
|
|
44
45
|
};
|
|
45
46
|
confirm: {
|
|
46
47
|
in: [message: string];
|
|
@@ -130,6 +131,10 @@ type HandlerMap = {
|
|
|
130
131
|
in: [buffer: number[]];
|
|
131
132
|
out: void;
|
|
132
133
|
};
|
|
134
|
+
stdErr: {
|
|
135
|
+
in: [buffer: number[]];
|
|
136
|
+
out: void;
|
|
137
|
+
};
|
|
133
138
|
};
|
|
134
139
|
type JsInstruction = {
|
|
135
140
|
name: string;
|
|
@@ -160,7 +165,7 @@ declare class RISCV {
|
|
|
160
165
|
static initializeRISCV: typeof initializeRISCV;
|
|
161
166
|
static getInstructionSet(): JsInstruction[];
|
|
162
167
|
static setIs64Bit(is64Bit: boolean): void;
|
|
163
|
-
static is64Bit():
|
|
168
|
+
static is64Bit(): boolean;
|
|
164
169
|
}
|
|
165
170
|
type JsRiscVStackFrame = {
|
|
166
171
|
/**
|
|
@@ -287,8 +292,14 @@ declare enum RISCVRegisters {
|
|
|
287
292
|
type RegisterName = keyof typeof RISCVRegisters;
|
|
288
293
|
declare const RISCV_REGISTERS: RegisterName[];
|
|
289
294
|
type HandlerName = keyof HandlerMap;
|
|
295
|
+
/**
|
|
296
|
+
* A handler may return its result directly, or a promise of it. When a handler returns a promise
|
|
297
|
+
* the simulation suspends until it settles, so IO can be backed by an async API (prompting the
|
|
298
|
+
* user, reading a file, awaiting a worker) without blocking the event loop. If the promise
|
|
299
|
+
* rejects, the pending `step`/`simulate*` call rejects too.
|
|
300
|
+
*/
|
|
290
301
|
type HandlerMapFns = {
|
|
291
|
-
[K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out']
|
|
302
|
+
[K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'] | Promise<HandlerMap[K]['out']>;
|
|
292
303
|
};
|
|
293
304
|
declare function registerHandlers(riscv: JsRiscV, handlers: HandlerMapFns): void;
|
|
294
305
|
declare function unimplementedHandler(name: HandlerName): () => never;
|
|
@@ -311,9 +322,11 @@ interface JsRiscV {
|
|
|
311
322
|
getStopReason(): StopReason;
|
|
312
323
|
/**
|
|
313
324
|
* Executes a single instruction.
|
|
314
|
-
*
|
|
325
|
+
*
|
|
326
|
+
* The promise settles on a microtask unless an IO handler returned a promise, in which case
|
|
327
|
+
* it settles once that handler and the rest of the instruction have finished.
|
|
315
328
|
*/
|
|
316
|
-
step(): StopReason
|
|
329
|
+
step(): Promise<StopReason>;
|
|
317
330
|
/**
|
|
318
331
|
* Gets the 8 condition flags.
|
|
319
332
|
*/
|
|
@@ -368,26 +381,26 @@ interface JsRiscV {
|
|
|
368
381
|
/**
|
|
369
382
|
* Simulates until the stop condition is met. it might be a breakpoint, exception, etc...
|
|
370
383
|
*/
|
|
371
|
-
simulate(): StopReason
|
|
384
|
+
simulate(): Promise<StopReason>;
|
|
372
385
|
/**
|
|
373
386
|
* Simulates the program for a limited number of instructions.
|
|
374
387
|
* @param limit The maximum number of instructions to execute.
|
|
375
|
-
* @returns
|
|
388
|
+
* @returns A promise resolving to the reason the simulation stopped.
|
|
376
389
|
*/
|
|
377
|
-
simulateWithLimit(limit: number): StopReason
|
|
390
|
+
simulateWithLimit(limit: number): Promise<StopReason>;
|
|
378
391
|
/**
|
|
379
392
|
* Simulates the program until a breakpoint is reached.
|
|
380
393
|
* @param breakpoints An array of memory addresses where the simulation should pause.
|
|
381
|
-
* @returns
|
|
394
|
+
* @returns A promise resolving to the reason the simulation stopped.
|
|
382
395
|
*/
|
|
383
|
-
simulateWithBreakpoints(breakpoints: number[]): StopReason
|
|
396
|
+
simulateWithBreakpoints(breakpoints: number[]): Promise<StopReason>;
|
|
384
397
|
/**
|
|
385
398
|
* Simulates the program with both breakpoints and a limit.
|
|
386
399
|
* @param breakpoints An array of memory addresses where the simulation should pause.
|
|
387
400
|
* @param limit The maximum number of instructions to execute.
|
|
388
|
-
* @returns
|
|
401
|
+
* @returns A promise resolving to the reason the simulation stopped.
|
|
389
402
|
*/
|
|
390
|
-
simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): StopReason
|
|
403
|
+
simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): Promise<StopReason>;
|
|
391
404
|
/**
|
|
392
405
|
* Gets the value of a register.
|
|
393
406
|
* @param register The name of the register.
|
|
@@ -405,7 +418,7 @@ interface JsRiscV {
|
|
|
405
418
|
* @param name The name of the event or condition.
|
|
406
419
|
* @param handler The handler function to be called when the event occurs. The function signature depends on the event name.
|
|
407
420
|
*/
|
|
408
|
-
registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out']): void;
|
|
421
|
+
registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out'] | Promise<HandlerMap[T]['out']>): void;
|
|
409
422
|
/**
|
|
410
423
|
* Gets the current value of the stack pointer.
|
|
411
424
|
* @returns The value of the stack pointer.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ type JsInstructionToken = {
|
|
|
6
6
|
type: string;
|
|
7
7
|
};
|
|
8
8
|
declare enum StopReason {
|
|
9
|
+
NONE = -1,// no simulation has run yet
|
|
9
10
|
BREAKPOINT = 0,
|
|
10
11
|
EXCEPTION = 1,
|
|
11
12
|
MAX_STEPS = 2,// includes step mode (where maxSteps is 1)
|
|
@@ -40,7 +41,7 @@ type HandlerMap = {
|
|
|
40
41
|
};
|
|
41
42
|
readFile: {
|
|
42
43
|
in: [fileDescriptor: number, destination: number[], length: number];
|
|
43
|
-
out: number;
|
|
44
|
+
out: [readOrEof: number, buffer: number[]];
|
|
44
45
|
};
|
|
45
46
|
confirm: {
|
|
46
47
|
in: [message: string];
|
|
@@ -130,6 +131,10 @@ type HandlerMap = {
|
|
|
130
131
|
in: [buffer: number[]];
|
|
131
132
|
out: void;
|
|
132
133
|
};
|
|
134
|
+
stdErr: {
|
|
135
|
+
in: [buffer: number[]];
|
|
136
|
+
out: void;
|
|
137
|
+
};
|
|
133
138
|
};
|
|
134
139
|
type JsInstruction = {
|
|
135
140
|
name: string;
|
|
@@ -160,7 +165,7 @@ declare class RISCV {
|
|
|
160
165
|
static initializeRISCV: typeof initializeRISCV;
|
|
161
166
|
static getInstructionSet(): JsInstruction[];
|
|
162
167
|
static setIs64Bit(is64Bit: boolean): void;
|
|
163
|
-
static is64Bit():
|
|
168
|
+
static is64Bit(): boolean;
|
|
164
169
|
}
|
|
165
170
|
type JsRiscVStackFrame = {
|
|
166
171
|
/**
|
|
@@ -287,8 +292,14 @@ declare enum RISCVRegisters {
|
|
|
287
292
|
type RegisterName = keyof typeof RISCVRegisters;
|
|
288
293
|
declare const RISCV_REGISTERS: RegisterName[];
|
|
289
294
|
type HandlerName = keyof HandlerMap;
|
|
295
|
+
/**
|
|
296
|
+
* A handler may return its result directly, or a promise of it. When a handler returns a promise
|
|
297
|
+
* the simulation suspends until it settles, so IO can be backed by an async API (prompting the
|
|
298
|
+
* user, reading a file, awaiting a worker) without blocking the event loop. If the promise
|
|
299
|
+
* rejects, the pending `step`/`simulate*` call rejects too.
|
|
300
|
+
*/
|
|
290
301
|
type HandlerMapFns = {
|
|
291
|
-
[K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out']
|
|
302
|
+
[K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'] | Promise<HandlerMap[K]['out']>;
|
|
292
303
|
};
|
|
293
304
|
declare function registerHandlers(riscv: JsRiscV, handlers: HandlerMapFns): void;
|
|
294
305
|
declare function unimplementedHandler(name: HandlerName): () => never;
|
|
@@ -311,9 +322,11 @@ interface JsRiscV {
|
|
|
311
322
|
getStopReason(): StopReason;
|
|
312
323
|
/**
|
|
313
324
|
* Executes a single instruction.
|
|
314
|
-
*
|
|
325
|
+
*
|
|
326
|
+
* The promise settles on a microtask unless an IO handler returned a promise, in which case
|
|
327
|
+
* it settles once that handler and the rest of the instruction have finished.
|
|
315
328
|
*/
|
|
316
|
-
step(): StopReason
|
|
329
|
+
step(): Promise<StopReason>;
|
|
317
330
|
/**
|
|
318
331
|
* Gets the 8 condition flags.
|
|
319
332
|
*/
|
|
@@ -368,26 +381,26 @@ interface JsRiscV {
|
|
|
368
381
|
/**
|
|
369
382
|
* Simulates until the stop condition is met. it might be a breakpoint, exception, etc...
|
|
370
383
|
*/
|
|
371
|
-
simulate(): StopReason
|
|
384
|
+
simulate(): Promise<StopReason>;
|
|
372
385
|
/**
|
|
373
386
|
* Simulates the program for a limited number of instructions.
|
|
374
387
|
* @param limit The maximum number of instructions to execute.
|
|
375
|
-
* @returns
|
|
388
|
+
* @returns A promise resolving to the reason the simulation stopped.
|
|
376
389
|
*/
|
|
377
|
-
simulateWithLimit(limit: number): StopReason
|
|
390
|
+
simulateWithLimit(limit: number): Promise<StopReason>;
|
|
378
391
|
/**
|
|
379
392
|
* Simulates the program until a breakpoint is reached.
|
|
380
393
|
* @param breakpoints An array of memory addresses where the simulation should pause.
|
|
381
|
-
* @returns
|
|
394
|
+
* @returns A promise resolving to the reason the simulation stopped.
|
|
382
395
|
*/
|
|
383
|
-
simulateWithBreakpoints(breakpoints: number[]): StopReason
|
|
396
|
+
simulateWithBreakpoints(breakpoints: number[]): Promise<StopReason>;
|
|
384
397
|
/**
|
|
385
398
|
* Simulates the program with both breakpoints and a limit.
|
|
386
399
|
* @param breakpoints An array of memory addresses where the simulation should pause.
|
|
387
400
|
* @param limit The maximum number of instructions to execute.
|
|
388
|
-
* @returns
|
|
401
|
+
* @returns A promise resolving to the reason the simulation stopped.
|
|
389
402
|
*/
|
|
390
|
-
simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): StopReason
|
|
403
|
+
simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): Promise<StopReason>;
|
|
391
404
|
/**
|
|
392
405
|
* Gets the value of a register.
|
|
393
406
|
* @param register The name of the register.
|
|
@@ -405,7 +418,7 @@ interface JsRiscV {
|
|
|
405
418
|
* @param name The name of the event or condition.
|
|
406
419
|
* @param handler The handler function to be called when the event occurs. The function signature depends on the event name.
|
|
407
420
|
*/
|
|
408
|
-
registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out']): void;
|
|
421
|
+
registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out'] | Promise<HandlerMap[T]['out']>): void;
|
|
409
422
|
/**
|
|
410
423
|
* Gets the current value of the stack pointer.
|
|
411
424
|
* @returns The value of the stack pointer.
|