@specy/risc-v 2.1.0 → 3.1.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
@@ -9,22 +9,33 @@ It is part of a family of javascript assembly interpreters/simulators:
9
9
 
10
10
  ## Usage
11
11
 
12
- First, create an instance of the simulator with the `RISCV.makeRiscvFromSource` function.
12
+ First, create an instance of the simulator with `makeRiscVFromFiles`. Supply a virtual source tree and the path of its entry file. Relative `.include` paths resolve from the file containing the directive, while paths beginning with `/` resolve from the virtual root.
13
13
 
14
14
  Before running the simulator, you must assemble and initialize it. You can then step through the program, simulate with breakpoints, or simulate with a limit.
15
15
 
16
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
17
 
18
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
- `;
19
+ import {RISCV, makeRiscVFromFiles, JsRiscV, RegisterName, BackStepAction} from '@specy/risc-v';
20
+
21
+ const files = {
22
+ 'src/main.asm': `
23
+ .include "lib/math.asm"
24
+ .text
25
+ .globl main
26
+ main:
27
+ add_values(t2, 5, 7)
28
+ `,
29
+ 'src/lib/math.asm': `
30
+ .macro add_values(%result, %left, %right)
31
+ li %result, %left
32
+ addi %result, %result, %right
33
+ .end_macro
34
+ `,
35
+ } as const;
25
36
 
26
37
  // RISCV.setIs64Bit(true); // Set to 64-bit mode if needed
27
- const riscvSimulator: JsRiscV = RISCV.makeRiscVFromSource(sourceCode);
38
+ const riscvSimulator: JsRiscV = makeRiscVFromFiles(files, 'src/main.asm');
28
39
 
29
40
  riscvSimulator.assemble();
30
41
  riscvSimulator.initialize(true); // Start at 'main'
@@ -154,10 +165,10 @@ riscvSimulator.removeMemoryObservers()
154
165
 
155
166
  Provides static methods to initialize and create simulator instances.
156
167
 
157
- * `RISCV.makeRiscvFromSource(source: string): JsRiscv`
158
- 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.
168
+ * `makeRiscVFromFiles(files: RISCVSourceSet, entryFile: string): JsRiscV`
169
+ Creates a simulator from a snapshot of a virtual source tree and its entry file. Source paths are canonical, root-relative POSIX paths. The factory is also available as `RISCV.makeRiscVFromFiles` and initializes the simulation environment when needed.
159
170
  * `RISCV.initializeRISCV(): void`
160
- Initializes the core RISC-V simulation environment. Called internally by `makeRiscvFromSource`, but can be called explicitly if needed.
171
+ Initializes the core RISC-V simulation environment. Called internally by `makeRiscVFromFiles`, but can be called explicitly if needed.
161
172
  * `RISCV.getInstructionSet(): JsInstruction[]`
162
173
  Returns an array of objects, each describing a supported RISC-V instruction (name, example, description, tokens).
163
174
  * `RISCV.setIs64Bit(is64Bit: boolean): void`
@@ -199,13 +210,12 @@ This interface provides methods to control and interact with a RISC-V simulator
199
210
  * `removeMemoryObserver(handle: number): void`: Removes one registration.
200
211
  * `removeMemoryObservers(): void`: Removes every registration.
201
212
  * `countMemoryObservers(): number`: The number of live registrations.
202
- * `getCurrentStatementIndex(): number`: Returns the index of the current instruction in the list of assembled program statements.
203
213
  * `getNextStatement(): JsProgramStatement | null`: Returns the next `JsProgramStatement` to be executed, or `null` if at the end.
204
214
  * `getStatementAtAddress(address: number): JsProgramStatement | null`: Gets the program statement at the given memory `address`.
205
- * `getStatementAtSourceLine(line: number): JsProgramStatement | null`: Gets the program statement corresponding to the original source `line` number.
206
- * `getCompiledStatements(): JsProgramStatement[]`: Returns an array of all assembled program statements.
207
- * `getParsedStatements(): JsInstructionToken[]`: Returns an array of tokens from the initial parsing stage.
208
- * `getTokenizedLines(): RiscvTokenizedLine[]`: Returns an array of lines, each with its source string and corresponding tokens.
215
+ * `getStatementsAtSourceLocation(sourcePath: string, sourceLine: number): JsProgramStatement[]`: Returns every machine statement generated from an original one-based source location, in address order. Pseudo-instructions and macro calls can produce multiple results.
216
+ * `getCompiledStatements(): JsProgramStatement[]`: Returns all machine statements after a successful assembly.
217
+ * `getParsedStatements(): JsProgramStatement[]`: Returns the parsed statements before pseudo-instruction expansion.
218
+ * `getTokenizedLines(): RiscvTokenizedLine[]`: Returns the expanded source stream with each line's path, one-based line number, original source, processed source, and tokens. It is available after tokenization succeeds, including when a later assembly stage fails.
209
219
  * `getCallStack(): JsRiscvStackFrame[]`: Returns the current call stack as an array of stack frame objects.
210
220
  * `getLabelAtAddress(address: number): string | null`: Returns the label name at the given memory `address`, or `null` if no label exists there.
211
221
 
@@ -217,6 +227,8 @@ This interface provides methods to control and interact with a RISC-V simulator
217
227
  #### Types
218
228
 
219
229
  * `RegisterName`: Union type for RISC-V register names (e.g., `'ra'`, `'sp'`, `'a0'`, `'t0'`, etc.).
230
+ * `RISCVSourceSet`: A read-only mapping from canonical virtual source paths to source strings.
231
+ * `RISCVSourceLocation`: A source path and one-based source line.
220
232
  * `HandlerName`: Union type of all possible handler names (keys of `HandlerMap`).
221
233
  * `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.
222
234
  * `HandlerMapFns`: An object type where keys are `HandlerName`s and values are the corresponding handler functions.
@@ -224,10 +236,10 @@ This interface provides methods to control and interact with a RISC-V simulator
224
236
  * `ConfirmResult`: Enum for confirm dialog results (`YES`, `NO`, `CANCEL`).
225
237
  * `BackStepAction`: Enum representing types of actions that can be undone (e.g., `MEMORY_RESTORE_WORD`, `REGISTER_RESTORE`).
226
238
  * `JsBackStep`: Interface representing an entry in the undo stack, detailing the action, parameters, and PC value.
227
- * `JsProgramStatement`: Interface representing an assembled instruction, including source line, address, binary/machine/assembly representations.
228
- * `JsInstructionToken`: Interface representing a token from the assembly source (value, type, source position).
239
+ * `JsProgramStatement`: Interface representing an assembled instruction, including its source path, source line, address, binary/machine/assembly representations, and full original source line.
240
+ * `JsInstructionToken`: Interface representing a token from the assembly source (value, type, and one-based source column).
229
241
  * `JsInstruction`: Interface describing a RISC-V instruction (name, example, description, token patterns).
230
- * `RiscvTokenizedLine`: An object containing the original line string and its parsed `JsInstructionToken`s.
231
- * `RISCVAssembleError`: Interface describing an assembly error (message, line/column, warning status, etc.).
242
+ * `RiscvTokenizedLine`: An object containing source identity, original and processed line strings, and parsed `JsInstructionToken`s.
243
+ * `RISCVAssembleError`: Interface describing an assembly diagnostic with its source path, one-based line and column, warning status, and structured macro expansion trace.
232
244
  * `RISCVAssembleResult`: Interface for the result of `assemble()`, containing a report string, list of `RISCVAssembleError`s, and a `hasErrors` boolean.
233
- * `JsRiscvStackFrame`: Interface describing a frame on the call stack (PC, target address, SP, FP, register snapshot).
245
+ * `JsRiscvStackFrame`: Interface describing a frame on the call stack (PC, target address, SP, FP, register snapshot).
package/dist/index.d.mts CHANGED
@@ -1,7 +1,6 @@
1
1
  type JsInstructionToken = {
2
- sourceLine: number;
2
+ /** One-based column in the processed source line. */
3
3
  sourceColumn: number;
4
- originalSourceLine: number;
5
4
  value: string;
6
5
  type: string;
7
6
  };
@@ -178,16 +177,30 @@ type JsInstruction = {
178
177
  getIsRv64Only: () => boolean;
179
178
  };
180
179
  type RiscvTokenizedLine = {
181
- line: string;
180
+ sourcePath: string;
181
+ /** One-based line in `sourcePath`. */
182
+ sourceLine: number;
183
+ /** Exact line supplied in the source set. */
184
+ source: string;
185
+ /** Line after assembler substitutions such as `.eqv`. */
186
+ processedSource: string;
182
187
  tokens: JsInstructionToken[];
183
188
  };
189
+ type RISCVSourceLocation = {
190
+ sourcePath: string;
191
+ /** One-based line in `sourcePath`. */
192
+ sourceLine: number;
193
+ };
194
+ type RISCVSourceSet = Readonly<Record<string, string>>;
184
195
  type RISCVAssembleError = {
185
196
  isWarning: boolean;
186
197
  message: string;
187
- macroExpansionHistory: string;
188
- filename: string;
189
- lineNumber: number;
190
- columnNumber: number;
198
+ macroExpansionTrace: RISCVSourceLocation[];
199
+ sourcePath: string;
200
+ /** One-based line in `sourcePath`. */
201
+ sourceLine: number;
202
+ /** One-based column in `sourcePath`. Zero only for diagnostics without a source location. */
203
+ sourceColumn: number;
191
204
  };
192
205
  type RISCVAssembleResult = {
193
206
  report: string;
@@ -198,7 +211,7 @@ type RISCVAssembleResult = {
198
211
  hasWarnings: boolean;
199
212
  };
200
213
  declare class RISCV {
201
- static makeRiscVFromSource: typeof makeRiscVfromSource;
214
+ static makeRiscVFromFiles: typeof makeRiscVFromFiles;
202
215
  static initializeRISCV: typeof initializeRISCV;
203
216
  static getInstructionSet(): JsInstruction[];
204
217
  static setIs64Bit(is64Bit: boolean): void;
@@ -230,9 +243,9 @@ type JsRiscVStackFrame = {
230
243
  * Represents a statement in the assembled program.
231
244
  */
232
245
  interface JsProgramStatement {
233
- /**
234
- * The line number in the original source code.
235
- */
246
+ /** Canonical path of the original source file. */
247
+ readonly sourcePath: string;
248
+ /** The one-based line number in the original source file. */
236
249
  readonly sourceLine: number;
237
250
  /**
238
251
  * The memory address of the instruction.
@@ -269,7 +282,16 @@ declare enum BackStepAction {
269
282
  CONTROL_AND_STATUS_REGISTER_RESTORE = 7,
270
283
  CONTROL_AND_STATUS_REGISTER_BACKDOOR = 8,
271
284
  FLOATING_POINT_REGISTER_RESTORE = 9,
272
- DO_NOTHING = 10
285
+ DO_NOTHING = 10,
286
+ /**
287
+ * Undoes one instruction's worth of the `cycle` and `instret` counters. The simulator adds one
288
+ * to each of them per instruction and nothing else writes them, so a single entry without
289
+ * parameters covers both.
290
+ *
291
+ * Members mirror the core's `BackStepper.Action` by position, because the core reports an
292
+ * action as that enum's ordinal. Append here; never insert.
293
+ */
294
+ CONTROL_AND_STATUS_COUNTERS_DECREMENT = 11
273
295
  }
274
296
  /**
275
297
  * Represents a back step in the simulation undo stack.
@@ -382,11 +404,8 @@ interface JsRiscV {
382
404
  * @param address
383
405
  */
384
406
  getStatementAtAddress(address: number): JsProgramStatement;
385
- /**
386
- * Gets the statement at the given source line.
387
- * @param line
388
- */
389
- getStatementAtSourceLine(line: number): JsProgramStatement;
407
+ /** Gets every machine statement generated from an original source location. */
408
+ getStatementsAtSourceLocation(sourcePath: string, sourceLine: number): JsProgramStatement[];
390
409
  getTokenizedLines(): RiscvTokenizedLine[];
391
410
  /**
392
411
  * Checks if the simulation can be undone.
@@ -403,7 +422,7 @@ interface JsRiscV {
403
422
  * @returns An array of `JsProgramStatement` objects representing the compiled program.
404
423
  */
405
424
  getCompiledStatements(): JsProgramStatement[];
406
- getParsedStatements(): JsInstructionToken[];
425
+ getParsedStatements(): JsProgramStatement[];
407
426
  /**
408
427
  * Gets the label at the given address.
409
428
  * @param address The memory address.
@@ -560,11 +579,6 @@ interface JsRiscV {
560
579
  * The number of live registrations, across every `JsRiscV` instance.
561
580
  */
562
581
  countMemoryObservers(): number;
563
- /**
564
- * Gets the index of the current statement in the assembled program.
565
- * @returns The index of the current statement.
566
- */
567
- getCurrentStatementIndex(): number;
568
582
  /**
569
583
  * Gets the next statement to be executed.
570
584
  * @returns The next `JsProgramStatement`.
@@ -583,15 +597,19 @@ interface JsRiscV {
583
597
  terminated: boolean;
584
598
  }
585
599
  /**
586
- * Creates a new RISCV simulator from the given source code.
587
- * @param source The source code to assemble.
600
+ * Creates a RISC-V simulator from a virtual source tree and its entry file.
601
+ *
602
+ * Source paths are canonical, root-relative, case-sensitive POSIX paths. The source set is
603
+ * snapshotted by this call; only the entry file and files reached through `.include` are assembled.
604
+ * @param files Source text keyed by canonical source path.
605
+ * @param entryFile Canonical path of the file from which include expansion starts.
588
606
  * @returns A new `JsRiscV` object.
589
607
  */
590
- declare function makeRiscVfromSource(source: string): JsRiscV;
608
+ declare function makeRiscVFromFiles(files: RISCVSourceSet, entryFile: string): JsRiscV;
591
609
  /**
592
610
  * Initializes the RISCV simulator.
593
611
  */
594
612
  declare function initializeRISCV(): void;
595
613
  declare function bigintToHighLow(value: bigint): [high: number, low: number];
596
614
 
597
- export { BackStepAction, ConfirmResult, DialogType, type HandlerMap, type HandlerMapFns, type JsBackStep, type JsInstruction, type JsInstructionToken, type JsProgramStatement, type JsRiscV, type JsRiscVStackFrame, type MemoryAccessObserver, type MemoryObserverHandle, type MemoryWriteObserver, RISCV, type RISCVAssembleError, type RISCVAssembleResult, RISCVRegisters, RISCV_REGISTERS, type RegisterName, type RiscvTokenizedLine, StopReason, bigintToHighLow, registerHandlers, unimplementedHandler };
615
+ export { BackStepAction, ConfirmResult, DialogType, type HandlerMap, type HandlerMapFns, type JsBackStep, type JsInstruction, type JsInstructionToken, type JsProgramStatement, type JsRiscV, type JsRiscVStackFrame, type MemoryAccessObserver, type MemoryObserverHandle, type MemoryWriteObserver, RISCV, type RISCVAssembleError, type RISCVAssembleResult, RISCVRegisters, type RISCVSourceLocation, type RISCVSourceSet, RISCV_REGISTERS, type RegisterName, type RiscvTokenizedLine, StopReason, bigintToHighLow, makeRiscVFromFiles, registerHandlers, unimplementedHandler };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  type JsInstructionToken = {
2
- sourceLine: number;
2
+ /** One-based column in the processed source line. */
3
3
  sourceColumn: number;
4
- originalSourceLine: number;
5
4
  value: string;
6
5
  type: string;
7
6
  };
@@ -178,16 +177,30 @@ type JsInstruction = {
178
177
  getIsRv64Only: () => boolean;
179
178
  };
180
179
  type RiscvTokenizedLine = {
181
- line: string;
180
+ sourcePath: string;
181
+ /** One-based line in `sourcePath`. */
182
+ sourceLine: number;
183
+ /** Exact line supplied in the source set. */
184
+ source: string;
185
+ /** Line after assembler substitutions such as `.eqv`. */
186
+ processedSource: string;
182
187
  tokens: JsInstructionToken[];
183
188
  };
189
+ type RISCVSourceLocation = {
190
+ sourcePath: string;
191
+ /** One-based line in `sourcePath`. */
192
+ sourceLine: number;
193
+ };
194
+ type RISCVSourceSet = Readonly<Record<string, string>>;
184
195
  type RISCVAssembleError = {
185
196
  isWarning: boolean;
186
197
  message: string;
187
- macroExpansionHistory: string;
188
- filename: string;
189
- lineNumber: number;
190
- columnNumber: number;
198
+ macroExpansionTrace: RISCVSourceLocation[];
199
+ sourcePath: string;
200
+ /** One-based line in `sourcePath`. */
201
+ sourceLine: number;
202
+ /** One-based column in `sourcePath`. Zero only for diagnostics without a source location. */
203
+ sourceColumn: number;
191
204
  };
192
205
  type RISCVAssembleResult = {
193
206
  report: string;
@@ -198,7 +211,7 @@ type RISCVAssembleResult = {
198
211
  hasWarnings: boolean;
199
212
  };
200
213
  declare class RISCV {
201
- static makeRiscVFromSource: typeof makeRiscVfromSource;
214
+ static makeRiscVFromFiles: typeof makeRiscVFromFiles;
202
215
  static initializeRISCV: typeof initializeRISCV;
203
216
  static getInstructionSet(): JsInstruction[];
204
217
  static setIs64Bit(is64Bit: boolean): void;
@@ -230,9 +243,9 @@ type JsRiscVStackFrame = {
230
243
  * Represents a statement in the assembled program.
231
244
  */
232
245
  interface JsProgramStatement {
233
- /**
234
- * The line number in the original source code.
235
- */
246
+ /** Canonical path of the original source file. */
247
+ readonly sourcePath: string;
248
+ /** The one-based line number in the original source file. */
236
249
  readonly sourceLine: number;
237
250
  /**
238
251
  * The memory address of the instruction.
@@ -269,7 +282,16 @@ declare enum BackStepAction {
269
282
  CONTROL_AND_STATUS_REGISTER_RESTORE = 7,
270
283
  CONTROL_AND_STATUS_REGISTER_BACKDOOR = 8,
271
284
  FLOATING_POINT_REGISTER_RESTORE = 9,
272
- DO_NOTHING = 10
285
+ DO_NOTHING = 10,
286
+ /**
287
+ * Undoes one instruction's worth of the `cycle` and `instret` counters. The simulator adds one
288
+ * to each of them per instruction and nothing else writes them, so a single entry without
289
+ * parameters covers both.
290
+ *
291
+ * Members mirror the core's `BackStepper.Action` by position, because the core reports an
292
+ * action as that enum's ordinal. Append here; never insert.
293
+ */
294
+ CONTROL_AND_STATUS_COUNTERS_DECREMENT = 11
273
295
  }
274
296
  /**
275
297
  * Represents a back step in the simulation undo stack.
@@ -382,11 +404,8 @@ interface JsRiscV {
382
404
  * @param address
383
405
  */
384
406
  getStatementAtAddress(address: number): JsProgramStatement;
385
- /**
386
- * Gets the statement at the given source line.
387
- * @param line
388
- */
389
- getStatementAtSourceLine(line: number): JsProgramStatement;
407
+ /** Gets every machine statement generated from an original source location. */
408
+ getStatementsAtSourceLocation(sourcePath: string, sourceLine: number): JsProgramStatement[];
390
409
  getTokenizedLines(): RiscvTokenizedLine[];
391
410
  /**
392
411
  * Checks if the simulation can be undone.
@@ -403,7 +422,7 @@ interface JsRiscV {
403
422
  * @returns An array of `JsProgramStatement` objects representing the compiled program.
404
423
  */
405
424
  getCompiledStatements(): JsProgramStatement[];
406
- getParsedStatements(): JsInstructionToken[];
425
+ getParsedStatements(): JsProgramStatement[];
407
426
  /**
408
427
  * Gets the label at the given address.
409
428
  * @param address The memory address.
@@ -560,11 +579,6 @@ interface JsRiscV {
560
579
  * The number of live registrations, across every `JsRiscV` instance.
561
580
  */
562
581
  countMemoryObservers(): number;
563
- /**
564
- * Gets the index of the current statement in the assembled program.
565
- * @returns The index of the current statement.
566
- */
567
- getCurrentStatementIndex(): number;
568
582
  /**
569
583
  * Gets the next statement to be executed.
570
584
  * @returns The next `JsProgramStatement`.
@@ -583,15 +597,19 @@ interface JsRiscV {
583
597
  terminated: boolean;
584
598
  }
585
599
  /**
586
- * Creates a new RISCV simulator from the given source code.
587
- * @param source The source code to assemble.
600
+ * Creates a RISC-V simulator from a virtual source tree and its entry file.
601
+ *
602
+ * Source paths are canonical, root-relative, case-sensitive POSIX paths. The source set is
603
+ * snapshotted by this call; only the entry file and files reached through `.include` are assembled.
604
+ * @param files Source text keyed by canonical source path.
605
+ * @param entryFile Canonical path of the file from which include expansion starts.
588
606
  * @returns A new `JsRiscV` object.
589
607
  */
590
- declare function makeRiscVfromSource(source: string): JsRiscV;
608
+ declare function makeRiscVFromFiles(files: RISCVSourceSet, entryFile: string): JsRiscV;
591
609
  /**
592
610
  * Initializes the RISCV simulator.
593
611
  */
594
612
  declare function initializeRISCV(): void;
595
613
  declare function bigintToHighLow(value: bigint): [high: number, low: number];
596
614
 
597
- export { BackStepAction, ConfirmResult, DialogType, type HandlerMap, type HandlerMapFns, type JsBackStep, type JsInstruction, type JsInstructionToken, type JsProgramStatement, type JsRiscV, type JsRiscVStackFrame, type MemoryAccessObserver, type MemoryObserverHandle, type MemoryWriteObserver, RISCV, type RISCVAssembleError, type RISCVAssembleResult, RISCVRegisters, RISCV_REGISTERS, type RegisterName, type RiscvTokenizedLine, StopReason, bigintToHighLow, registerHandlers, unimplementedHandler };
615
+ export { BackStepAction, ConfirmResult, DialogType, type HandlerMap, type HandlerMapFns, type JsBackStep, type JsInstruction, type JsInstructionToken, type JsProgramStatement, type JsRiscV, type JsRiscVStackFrame, type MemoryAccessObserver, type MemoryObserverHandle, type MemoryWriteObserver, RISCV, type RISCVAssembleError, type RISCVAssembleResult, RISCVRegisters, type RISCVSourceLocation, type RISCVSourceSet, RISCV_REGISTERS, type RegisterName, type RiscvTokenizedLine, StopReason, bigintToHighLow, makeRiscVFromFiles, registerHandlers, unimplementedHandler };