This project is a fork of the RARS RISC-V simulator for the RISC-V instruction set.
It has been designed to decouple the UI from the core simulator, providing a Typescript library that compiles the simulator to JavaScript and offers a simple interface to interact with it.
If you are looking for the original RISC-V RARS, you can find it here.
This is a Typescript implementation of a RISC-V simulator made by compiling the RARS RISC-V simulator to Javascript. It is part of a family of javascript assembly interpreters/simulators:
- MIPS: git repo, npm package
- RISC-V: git repo, npm package
- X86: git repo, npm package
- M68K: git repo, npm package
First, create an instance of the simulator with the RISCV.makeRiscvFromSource function.
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.
import {RISCV, JsRiscV, RegisterName, BackStepAction} from '@specy/risc-v';
const sourceCode = `
li t0, 5 # Load immediate value 5 into register t0
li t1, 7 # Load immediate value 7 into register t1
add t2, t0, t1 # Add t0 and t1, store result in t2
`;
// RISCV.setIs64Bit(true); // Set to 64-bit mode if needed
const riscvSimulator: JsRiscV = RISCV.makeRiscVFromSource(sourceCode);
riscvSimulator.assemble();
riscvSimulator.initialize(true); // Start at 'main'
while (!riscvSimulator.terminated) {
riscvSimulator.step();
}
// or riscvSimulator.simulate()
const pc = riscvSimulator.programCounter;
const t2 = riscvSimulator.getRegisterValue('t2');
console.log(`Program Counter: ${pc}`);
console.log(`t2: ${t2}`);
// Accessing memory:
const data = riscvSimulator.readMemoryBytes(0xffff0000, 4); // Read 4 bytes from address 0xffff0000
riscvSimulator.setMemoryBytes(0xffff0000, [0x01, 0x02, 0x03, 0x04]); // Write 4 bytes to address 0xffff0000
// Registering Handlers (for syscalls and other events):
riscvSimulator.registerHandler("printInt", (value: number) => {
console.log("printInt syscall called with:", value);
});
// Accessing the undo stack:
const undoStack = riscvSimulator.getUndoStack();
undoStack.forEach(step => {
if (step.action === BackStepAction.REGISTER_RESTORE) {
console.log(`Register restored at PC ${step.pc}`);
}
});
// Simulating with breakpoints:
const breakpoints = [0x00400004, 0x00400008]; // Example breakpoint addresses
riscvSimulator.simulateWithBreakpoints(breakpoints);
//Simulating with a limit
const limit = 100
riscvSimulator.simulateWithLimit(limit);
//Simulating with breakpoints and a limit
riscvSimulator.simulateWithBreakpointsAndLimit(breakpoints, limit);
//Setting Register Values:
riscvSimulator.setRegisterValue("t0", 42);Provides static methods to initialize and create simulator instances.
RISCV.makeRiscvFromSource(source: string): JsRiscvCreates a newJsRiscvinstance from RISC-V assembly source code. This also initializes the underlying RISC-V simulation environment if it hasn't been already.RISCV.initializeRISCV(): voidInitializes the core RISC-V simulation environment. Called internally bymakeRiscvFromSource, but can be called explicitly if needed.RISCV.getInstructionSet(): JsInstruction[]Returns an array of objects, each describing a supported RISC-V instruction (name, example, description, tokens).RISCV.setIs64Bit(is64Bit: boolean): voidSets the simulator to operate in 32-bit or 64-bit mode. This affects the instruction set.
This interface provides methods to control and interact with a RISC-V simulator instance.
canUndo: boolean(Read-only): True if an undo operation can be performed.programCounter: number(Read-only): The current value of the program counter (PC).stackPointer: number(Read-only): The current value of the stack pointer ($sp).terminated: boolean(Read-only): True if the simulation has terminated (e.g., via an exit syscall or error).
assemble(): RISCVAssembleResult: Assembles the program. Returns an object containing a report, error list, and anhasErrorsflag.initialize(startAtMain: boolean): void: Initializes the simulator state for execution. IfstartAtMainis true, execution begins at themainlabel; otherwise, it starts at the first instruction.step(): boolean: Executes a single instruction. Returnstrueif the execution is complete (program terminated),falseotherwise.undo(): void: Undoes the last instruction executed, ifcanUndois true and undo is enabled.setUndoEnabled(enabled: boolean): void: Enables or disables the undo feature.setUndoSize(size: number): void: Sets the maximum number of steps kept in the undo history. Must be called before assembling.simulateWithLimit(limit: number): boolean: Simulates the program for a maximum oflimitinstructions. Returnstrueif execution completes/terminates before the limit,falseotherwise.simulateWithBreakpoints(breakpoints: number[]): boolean: Simulates the program until a breakpoint is reached or the program terminates.breakpointsis an array of memory addresses. Returnstrueif execution completes/terminates,falseif a breakpoint is hit.simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): boolean: Simulates until a breakpoint, limit is reached, or termination. Returnstrueif execution completes/terminates,falseotherwise.getRegisterValue(register: RegisterName): number: Returns the value of the specified register.setRegisterValue(register: RegisterName, value: number): void: Sets the value of the specified register.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.getConditionFlags(): number[]: Gets the 8 condition flags (if applicable, typically related to floating-point or custom extensions).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). SeeHandlerNameandHandlerMapfor details.getUndoStack(): JsBackStep[]: Returns the undo stack, an array ofJsBackStepobjects representing simulation history.readMemoryBytes(address: number, length: number): number[]: Readslengthbytes from memory starting ataddress. Returns an array of byte values.setMemoryBytes(address: number, bytes: number[]): void: Writes an array ofbytesto memory starting ataddress.getCurrentStatementIndex(): number: Returns the index of the current instruction in the list of assembled program statements.getNextStatement(): JsProgramStatement | null: Returns the nextJsProgramStatementto be executed, ornullif at the end.getStatementAtAddress(address: number): JsProgramStatement | null: Gets the program statement at the given memoryaddress.getStatementAtSourceLine(line: number): JsProgramStatement | null: Gets the program statement corresponding to the original sourcelinenumber.getCompiledStatements(): JsProgramStatement[]: Returns an array of all assembled program statements.getParsedStatements(): JsInstructionToken[]: Returns an array of tokens from the initial parsing stage.getTokenizedLines(): RiscvTokenizedLine[]: Returns an array of lines, each with its source string and corresponding tokens.getCallStack(): JsRiscvStackFrame[]: Returns the current call stack as an array of stack frame objects.getLabelAtAddress(address: number): string | null: Returns the label name at the given memoryaddress, ornullif no label exists there.
registerHandlers(riscv: JsRiscv, handlers: Partial<HandlerMapFns>): void: A utility to register multiple handlers at once.unimplementedHandler(name: HandlerName): (...args: any[]) => any: Returns a function that throws an error indicating the handlernameis not implemented. Useful for stubbing handlers.
RegisterName: Union type for RISC-V register names (e.g.,'ra','sp','a0','t0', etc.).HandlerName: Union type of all possible handler names (keys ofHandlerMap).HandlerMap: An object type mappingHandlerNames to their expected input argument types (in) and return type (out). This defines the signature for syscall/event handlers.HandlerMapFns: An object type where keys areHandlerNames and values are the corresponding handler functions.DialogType: Enum for message dialog types (ERROR_MESSAGE,INFORMATION_MESSAGE,WARNING_MESSAGE,QUESTION_MESSAGE).ConfirmResult: Enum for confirm dialog results (YES,NO,CANCEL).BackStepAction: Enum representing types of actions that can be undone (e.g.,MEMORY_RESTORE_WORD,REGISTER_RESTORE).JsBackStep: Interface representing an entry in the undo stack, detailing the action, parameters, and PC value.JsProgramStatement: Interface representing an assembled instruction, including source line, address, binary/machine/assembly representations.JsInstructionToken: Interface representing a token from the assembly source (value, type, source position).JsInstruction: Interface describing a RISC-V instruction (name, example, description, token patterns).RiscvTokenizedLine: An object containing the original line string and its parsedJsInstructionTokens.RISCVAssembleError: Interface describing an assembly error (message, line/column, warning status, etc.).RISCVAssembleResult: Interface for the result ofassemble(), containing a report string, list ofRISCVAssembleErrors, and ahasErrorsboolean.JsRiscvStackFrame: Interface describing a frame on the call stack (PC, target address, SP, FP, register snapshot).