@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 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
- // Accessing the undo stack:
53
- const undoStack = riscvSimulator.getUndoStack();
54
- undoStack.forEach(step => {
55
- if (step.action === BackStepAction.REGISTER_RESTORE) {
56
- console.log(`Register restored at PC ${step.pc}`);
57
- }
58
- });
59
-
60
-
61
- // Simulating with breakpoints:
62
- const breakpoints = [0x00400004, 0x00400008]; // Example breakpoint addresses
63
- riscvSimulator.simulateWithBreakpoints(breakpoints);
64
-
65
- //Simulating with a limit
66
- const limit = 100
67
- riscvSimulator.simulateWithLimit(limit);
68
-
69
- //Simulating with breakpoints and a limit
70
- riscvSimulator.simulateWithBreakpointsAndLimit(breakpoints, limit);
71
-
72
- //Setting Register Values:
73
- riscvSimulator.setRegisterValue("t0", 42);
74
- ```
75
-
76
- ## API
77
-
78
- ### `RISCV` Static Class
79
-
80
- Provides static methods to initialize and create simulator instances.
81
-
82
- * `RISCV.makeRiscvFromSource(source: string): JsRiscv`
83
- 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.
84
- * `RISCV.initializeRISCV(): void`
85
- Initializes the core RISC-V simulation environment. Called internally by `makeRiscvFromSource`, but can be called explicitly if needed.
86
- * `RISCV.getInstructionSet(): JsInstruction[]`
87
- Returns an array of objects, each describing a supported RISC-V instruction (name, example, description, tokens).
88
- * `RISCV.setIs64Bit(is64Bit: boolean): void`
89
- Sets the simulator to operate in 32-bit or 64-bit mode. This affects the instruction set.
90
-
91
- ### `JsRiscv` Interface
92
-
93
- This interface provides methods to control and interact with a RISC-V simulator instance.
94
-
95
- #### Properties
96
-
97
- * `canUndo: boolean` (Read-only): True if an undo operation can be performed.
98
- * `programCounter: number` (Read-only): The current value of the program counter (PC).
99
- * `stackPointer: number` (Read-only): The current value of the stack pointer (`$sp`).
100
- * `terminated: boolean` (Read-only): True if the simulation has terminated (e.g., via an exit syscall or error).
101
-
102
- #### Methods
103
-
104
- * `assemble(): RISCVAssembleResult`: Assembles the program. Returns an object containing a report, error list, and an `hasErrors` flag.
105
- * `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.
106
- * `step(): boolean`: Executes a single instruction. Returns `true` if the execution is complete (program terminated), `false` otherwise.
107
- * `undo(): void`: Undoes the last instruction executed, if `canUndo` is true and undo is enabled.
108
- * `setUndoEnabled(enabled: boolean): void`: Enables or disables the undo feature.
109
- * `setUndoSize(size: number): void`: Sets the maximum number of steps kept in the undo history. Must be called before assembling.
110
- * `simulateWithLimit(limit: number): boolean`: Simulates the program for a maximum of `limit` instructions. Returns `true` if execution completes/terminates before the limit, `false` otherwise.
111
- * `simulateWithBreakpoints(breakpoints: number[]): boolean`: Simulates the program until a breakpoint is reached or the program terminates. `breakpoints` is an array of memory addresses. Returns `true` if execution completes/terminates, `false` if a breakpoint is hit.
112
- * `simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): boolean`: Simulates until a breakpoint, limit is reached, or termination. Returns `true` if execution completes/terminates, `false` otherwise.
113
- * `getRegisterValue(register: RegisterName): number`: Returns the value of the specified register.
114
- * `setRegisterValue(register: RegisterName, value: number): void`: Sets the value of the specified register.
115
- * `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.
116
- * `getConditionFlags(): number[]`: Gets the 8 condition flags (if applicable, typically related to floating-point or custom extensions).
117
- * `registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out']): void`: Registers a handler function for a specific event (e.g., syscalls). See `HandlerName` and `HandlerMap` for details.
118
- * `getUndoStack(): JsBackStep[]`: Returns the undo stack, an array of `JsBackStep` objects representing simulation history.
119
- * `readMemoryBytes(address: number, length: number): number[]`: Reads `length` bytes from memory starting at `address`. Returns an array of byte values.
120
- * `setMemoryBytes(address: number, bytes: number[]): void`: Writes an array of `bytes` to memory starting at `address`.
121
- * `getCurrentStatementIndex(): number`: Returns the index of the current instruction in the list of assembled program statements.
122
- * `getNextStatement(): JsProgramStatement | null`: Returns the next `JsProgramStatement` to be executed, or `null` if at the end.
123
- * `getStatementAtAddress(address: number): JsProgramStatement | null`: Gets the program statement at the given memory `address`.
124
- * `getStatementAtSourceLine(line: number): JsProgramStatement | null`: Gets the program statement corresponding to the original source `line` number.
125
- * `getCompiledStatements(): JsProgramStatement[]`: Returns an array of all assembled program statements.
126
- * `getParsedStatements(): JsInstructionToken[]`: Returns an array of tokens from the initial parsing stage.
127
- * `getTokenizedLines(): RiscvTokenizedLine[]`: Returns an array of lines, each with its source string and corresponding tokens.
128
- * `getCallStack(): JsRiscvStackFrame[]`: Returns the current call stack as an array of stack frame objects.
129
- * `getLabelAtAddress(address: number): string | null`: Returns the label name at the given memory `address`, or `null` if no label exists there.
130
-
131
- #### Helper Functions (for use with `JsRiscv`)
132
-
133
- * `registerHandlers(riscv: JsRiscv, handlers: Partial<HandlerMapFns>): void`: A utility to register multiple handlers at once.
134
- * `unimplementedHandler(name: HandlerName): (...args: any[]) => any`: Returns a function that throws an error indicating the handler `name` is not implemented. Useful for stubbing handlers.
135
-
136
- #### Types
137
-
138
- * `RegisterName`: Union type for RISC-V register names (e.g., `'ra'`, `'sp'`, `'a0'`, `'t0'`, etc.).
139
- * `HandlerName`: Union type of all possible handler names (keys of `HandlerMap`).
140
- * `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.
141
- * `HandlerMapFns`: An object type where keys are `HandlerName`s and values are the corresponding handler functions.
142
- * `DialogType`: Enum for message dialog types (`ERROR_MESSAGE`, `INFORMATION_MESSAGE`, `WARNING_MESSAGE`, `QUESTION_MESSAGE`).
143
- * `ConfirmResult`: Enum for confirm dialog results (`YES`, `NO`, `CANCEL`).
144
- * `BackStepAction`: Enum representing types of actions that can be undone (e.g., `MEMORY_RESTORE_WORD`, `REGISTER_RESTORE`).
145
- * `JsBackStep`: Interface representing an entry in the undo stack, detailing the action, parameters, and PC value.
146
- * `JsProgramStatement`: Interface representing an assembled instruction, including source line, address, binary/machine/assembly representations.
147
- * `JsInstructionToken`: Interface representing a token from the assembly source (value, type, source position).
148
- * `JsInstruction`: Interface describing a RISC-V instruction (name, example, description, token patterns).
149
- * `RiscvTokenizedLine`: An object containing the original line string and its parsed `JsInstructionToken`s.
150
- * `RISCVAssembleError`: Interface describing an assembly error (message, line/column, warning status, etc.).
151
- * `RISCVAssembleResult`: Interface for the result of `assemble()`, containing a report string, list of `RISCVAssembleError`s, and a `hasErrors` boolean.
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(): any;
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
- * @returns True if the execution is complete, false otherwise.
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 True if the execution is complete, false otherwise.
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 True if the execution is complete, false otherwise.
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 True if the execution is complete, false otherwise.
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(): any;
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
- * @returns True if the execution is complete, false otherwise.
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 True if the execution is complete, false otherwise.
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 True if the execution is complete, false otherwise.
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 True if the execution is complete, false otherwise.
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.