@specy/risc-v 1.1.0 → 2.0.1

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;
@@ -153,14 +158,17 @@ type RISCVAssembleError = {
153
158
  type RISCVAssembleResult = {
154
159
  report: string;
155
160
  errors: RISCVAssembleError[];
161
+ /** True only when at least one diagnostic is a real error; warnings alone leave the assembled program runnable. */
156
162
  hasErrors: boolean;
163
+ /** True when at least one diagnostic is a warning. */
164
+ hasWarnings: boolean;
157
165
  };
158
166
  declare class RISCV {
159
167
  static makeRiscVFromSource: typeof makeRiscVfromSource;
160
168
  static initializeRISCV: typeof initializeRISCV;
161
169
  static getInstructionSet(): JsInstruction[];
162
170
  static setIs64Bit(is64Bit: boolean): void;
163
- static is64Bit(): any;
171
+ static is64Bit(): boolean;
164
172
  }
165
173
  type JsRiscVStackFrame = {
166
174
  /**
@@ -287,8 +295,14 @@ declare enum RISCVRegisters {
287
295
  type RegisterName = keyof typeof RISCVRegisters;
288
296
  declare const RISCV_REGISTERS: RegisterName[];
289
297
  type HandlerName = keyof HandlerMap;
298
+ /**
299
+ * A handler may return its result directly, or a promise of it. When a handler returns a promise
300
+ * the simulation suspends until it settles, so IO can be backed by an async API (prompting the
301
+ * user, reading a file, awaiting a worker) without blocking the event loop. If the promise
302
+ * rejects, the pending `step`/`simulate*` call rejects too.
303
+ */
290
304
  type HandlerMapFns = {
291
- [K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'];
305
+ [K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'] | Promise<HandlerMap[K]['out']>;
292
306
  };
293
307
  declare function registerHandlers(riscv: JsRiscV, handlers: HandlerMapFns): void;
294
308
  declare function unimplementedHandler(name: HandlerName): () => never;
@@ -311,9 +325,11 @@ interface JsRiscV {
311
325
  getStopReason(): StopReason;
312
326
  /**
313
327
  * Executes a single instruction.
314
- * @returns True if the execution is complete, false otherwise.
328
+ *
329
+ * The promise settles on a microtask unless an IO handler returned a promise, in which case
330
+ * it settles once that handler and the rest of the instruction have finished.
315
331
  */
316
- step(): StopReason;
332
+ step(): Promise<StopReason>;
317
333
  /**
318
334
  * Gets the 8 condition flags.
319
335
  */
@@ -368,26 +384,26 @@ interface JsRiscV {
368
384
  /**
369
385
  * Simulates until the stop condition is met. it might be a breakpoint, exception, etc...
370
386
  */
371
- simulate(): StopReason;
387
+ simulate(): Promise<StopReason>;
372
388
  /**
373
389
  * Simulates the program for a limited number of instructions.
374
390
  * @param limit The maximum number of instructions to execute.
375
- * @returns True if the execution is complete, false otherwise.
391
+ * @returns A promise resolving to the reason the simulation stopped.
376
392
  */
377
- simulateWithLimit(limit: number): StopReason;
393
+ simulateWithLimit(limit: number): Promise<StopReason>;
378
394
  /**
379
395
  * Simulates the program until a breakpoint is reached.
380
396
  * @param breakpoints An array of memory addresses where the simulation should pause.
381
- * @returns True if the execution is complete, false otherwise.
397
+ * @returns A promise resolving to the reason the simulation stopped.
382
398
  */
383
- simulateWithBreakpoints(breakpoints: number[]): StopReason;
399
+ simulateWithBreakpoints(breakpoints: number[]): Promise<StopReason>;
384
400
  /**
385
401
  * Simulates the program with both breakpoints and a limit.
386
402
  * @param breakpoints An array of memory addresses where the simulation should pause.
387
403
  * @param limit The maximum number of instructions to execute.
388
- * @returns True if the execution is complete, false otherwise.
404
+ * @returns A promise resolving to the reason the simulation stopped.
389
405
  */
390
- simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): StopReason;
406
+ simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): Promise<StopReason>;
391
407
  /**
392
408
  * Gets the value of a register.
393
409
  * @param register The name of the register.
@@ -405,7 +421,7 @@ interface JsRiscV {
405
421
  * @param name The name of the event or condition.
406
422
  * @param handler The handler function to be called when the event occurs. The function signature depends on the event name.
407
423
  */
408
- registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out']): void;
424
+ registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out'] | Promise<HandlerMap[T]['out']>): void;
409
425
  /**
410
426
  * Gets the current value of the stack pointer.
411
427
  * @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;
@@ -153,14 +158,17 @@ type RISCVAssembleError = {
153
158
  type RISCVAssembleResult = {
154
159
  report: string;
155
160
  errors: RISCVAssembleError[];
161
+ /** True only when at least one diagnostic is a real error; warnings alone leave the assembled program runnable. */
156
162
  hasErrors: boolean;
163
+ /** True when at least one diagnostic is a warning. */
164
+ hasWarnings: boolean;
157
165
  };
158
166
  declare class RISCV {
159
167
  static makeRiscVFromSource: typeof makeRiscVfromSource;
160
168
  static initializeRISCV: typeof initializeRISCV;
161
169
  static getInstructionSet(): JsInstruction[];
162
170
  static setIs64Bit(is64Bit: boolean): void;
163
- static is64Bit(): any;
171
+ static is64Bit(): boolean;
164
172
  }
165
173
  type JsRiscVStackFrame = {
166
174
  /**
@@ -287,8 +295,14 @@ declare enum RISCVRegisters {
287
295
  type RegisterName = keyof typeof RISCVRegisters;
288
296
  declare const RISCV_REGISTERS: RegisterName[];
289
297
  type HandlerName = keyof HandlerMap;
298
+ /**
299
+ * A handler may return its result directly, or a promise of it. When a handler returns a promise
300
+ * the simulation suspends until it settles, so IO can be backed by an async API (prompting the
301
+ * user, reading a file, awaiting a worker) without blocking the event loop. If the promise
302
+ * rejects, the pending `step`/`simulate*` call rejects too.
303
+ */
290
304
  type HandlerMapFns = {
291
- [K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'];
305
+ [K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'] | Promise<HandlerMap[K]['out']>;
292
306
  };
293
307
  declare function registerHandlers(riscv: JsRiscV, handlers: HandlerMapFns): void;
294
308
  declare function unimplementedHandler(name: HandlerName): () => never;
@@ -311,9 +325,11 @@ interface JsRiscV {
311
325
  getStopReason(): StopReason;
312
326
  /**
313
327
  * Executes a single instruction.
314
- * @returns True if the execution is complete, false otherwise.
328
+ *
329
+ * The promise settles on a microtask unless an IO handler returned a promise, in which case
330
+ * it settles once that handler and the rest of the instruction have finished.
315
331
  */
316
- step(): StopReason;
332
+ step(): Promise<StopReason>;
317
333
  /**
318
334
  * Gets the 8 condition flags.
319
335
  */
@@ -368,26 +384,26 @@ interface JsRiscV {
368
384
  /**
369
385
  * Simulates until the stop condition is met. it might be a breakpoint, exception, etc...
370
386
  */
371
- simulate(): StopReason;
387
+ simulate(): Promise<StopReason>;
372
388
  /**
373
389
  * Simulates the program for a limited number of instructions.
374
390
  * @param limit The maximum number of instructions to execute.
375
- * @returns True if the execution is complete, false otherwise.
391
+ * @returns A promise resolving to the reason the simulation stopped.
376
392
  */
377
- simulateWithLimit(limit: number): StopReason;
393
+ simulateWithLimit(limit: number): Promise<StopReason>;
378
394
  /**
379
395
  * Simulates the program until a breakpoint is reached.
380
396
  * @param breakpoints An array of memory addresses where the simulation should pause.
381
- * @returns True if the execution is complete, false otherwise.
397
+ * @returns A promise resolving to the reason the simulation stopped.
382
398
  */
383
- simulateWithBreakpoints(breakpoints: number[]): StopReason;
399
+ simulateWithBreakpoints(breakpoints: number[]): Promise<StopReason>;
384
400
  /**
385
401
  * Simulates the program with both breakpoints and a limit.
386
402
  * @param breakpoints An array of memory addresses where the simulation should pause.
387
403
  * @param limit The maximum number of instructions to execute.
388
- * @returns True if the execution is complete, false otherwise.
404
+ * @returns A promise resolving to the reason the simulation stopped.
389
405
  */
390
- simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): StopReason;
406
+ simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): Promise<StopReason>;
391
407
  /**
392
408
  * Gets the value of a register.
393
409
  * @param register The name of the register.
@@ -405,7 +421,7 @@ interface JsRiscV {
405
421
  * @param name The name of the event or condition.
406
422
  * @param handler The handler function to be called when the event occurs. The function signature depends on the event name.
407
423
  */
408
- registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out']): void;
424
+ registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out'] | Promise<HandlerMap[T]['out']>): void;
409
425
  /**
410
426
  * Gets the current value of the stack pointer.
411
427
  * @returns The value of the stack pointer.