@specy/risc-v
Version:
The RARS risc-v interpreter in JS
491 lines (489 loc) • 13.8 kB
text/typescript
type JsInstructionToken = {
sourceLine: number;
sourceColumn: number;
originalSourceLine: number;
value: string;
type: string;
};
declare enum StopReason {
BREAKPOINT = 0,
EXCEPTION = 1,
MAX_STEPS = 2,// includes step mode (where maxSteps is 1)
NORMAL_TERMINATION = 3,
CLIFF_TERMINATION = 4,// run off bottom of program
PAUSE = 5,
STOP = 6
}
declare enum DialogType {
ERROR_MESSAGE = 0,
INFORMATION_MESSAGE = 1,
WARNING_MESSAGE = 2,
QUESTION_MESSAGE = 3
}
declare enum ConfirmResult {
YES = 0,
NO = 1,
CANCEL = 2
}
type HandlerMap = {
openFile: {
in: [filename: string, flags: number, append: boolean];
out: number;
};
closeFile: {
in: [fileDescriptor: number];
out: void;
};
writeFile: {
in: [fileDescriptor: number, buffer: number[]];
out: void;
};
readFile: {
in: [fileDescriptor: number, destination: number[], length: number];
out: number;
};
confirm: {
in: [message: string];
out: ConfirmResult;
};
inputDialog: {
in: [message: string];
out: string;
};
outputDialog: {
in: [message: string, type: DialogType];
out: void;
};
askDouble: {
in: [message: string];
out: number;
};
askFloat: {
in: [message: string];
out: number;
};
askInt: {
in: [message: string];
out: number;
};
askString: {
in: [message: string];
out: string;
};
readDouble: {
in: [];
out: number;
};
readFloat: {
in: [];
out: number;
};
readInt: {
in: [];
out: number;
};
readString: {
in: [];
out: string;
};
readChar: {
in: [];
out: string;
};
logLine: {
in: [message: string];
out: void;
};
log: {
in: [message: string];
out: void;
};
printChar: {
in: [c: string];
out: void;
};
printDouble: {
in: [d: number];
out: void;
};
printFloat: {
in: [f: number];
out: void;
};
printInt: {
in: [i: number];
out: void;
};
printString: {
in: [l: string];
out: void;
};
sleep: {
in: [milliseconds: number];
out: void;
};
stdIn: {
in: [buffer: number[], length: number];
out: void;
};
stdOut: {
in: [buffer: number[]];
out: void;
};
};
type JsInstruction = {
name: string;
example: string;
description: string;
tokens: JsInstructionToken[];
getIsRv64Only: () => boolean;
};
type RiscvTokenizedLine = {
line: string;
tokens: JsInstructionToken[];
};
type RISCVAssembleError = {
isWarning: boolean;
message: string;
macroExpansionHistory: string;
filename: string;
lineNumber: number;
columnNumber: number;
};
type RISCVAssembleResult = {
report: string;
errors: RISCVAssembleError[];
hasErrors: boolean;
};
declare class RISCV {
static makeRiscVFromSource: typeof makeRiscVfromSource;
static initializeRISCV: typeof initializeRISCV;
static getInstructionSet(): JsInstruction[];
static setIs64Bit(is64Bit: boolean): void;
static is64Bit(): any;
}
type JsRiscVStackFrame = {
/**
* The program counter value at the moment the stack frame was created.
*/
pc: number;
/**
* The address of the target instruction.
*/
toAddress: number;
/**
* The stack pointer value at the moment the stack frame was created.
*/
sp: number;
/**
* The frame pointer value at the moment the stack frame was created.
*/
fp: number;
/**
* The values of all registers at the moment the stack frame was created.
*/
registers: number[];
};
/**
* Represents a statement in the assembled program.
*/
interface JsProgramStatement {
/**
* The line number in the original source code.
*/
readonly sourceLine: number;
/**
* The memory address of the instruction.
*/
readonly address: number;
/**
* The binary representation of the instruction.
*/
readonly binaryStatement: number;
/**
* The original source code line.
*/
readonly source: string;
/**
* The machine code representation of the instruction.
*/
readonly machineStatement: string;
/**
* The assembly representation of the instruction.
*/
readonly assemblyStatement: string;
}
/**
* Enum representing the types of "undo" actions.
*/
declare enum BackStepAction {
MEMORY_RESTORE_RAW_WORD = 0,
MEMORY_RESTORE_DOUBLE_WORD = 1,
MEMORY_RESTORE_WORD = 2,
MEMORY_RESTORE_HALF = 3,
MEMORY_RESTORE_BYTE = 4,
REGISTER_RESTORE = 5,
PC_RESTORE = 6,
CONTROL_AND_STATUS_REGISTER_RESTORE = 7,
CONTROL_AND_STATUS_REGISTER_BACKDOOR = 8,
FLOATING_POINT_REGISTER_RESTORE = 9,
DO_NOTHING = 10
}
/**
* Represents a back step in the simulation undo stack.
*/
interface JsBackStep {
/**
* The action performed (e.g., register write, memory write).
*/
readonly action: BackStepAction;
/**
* Information about the action
*/
readonly param1: number;
/**
* Information about the action
*/
readonly param2: number;
/**
* The program counter value before the action.
*/
readonly pc: number;
}
declare enum RISCVRegisters {
zero = 0,
ra = 1,
sp = 2,
gp = 3,
tp = 4,
t0 = 5,
t1 = 6,
t2 = 7,
s0 = 8,
s1 = 9,
a0 = 10,
a1 = 11,
a2 = 12,
a3 = 13,
a4 = 14,
a5 = 15,
a6 = 16,
a7 = 17,
s2 = 18,
s3 = 19,
s4 = 20,
s5 = 21,
s6 = 22,
s7 = 23,
s8 = 24,
s9 = 25,
s10 = 26,
s11 = 27,
t3 = 28,
t4 = 29,
t5 = 30,
t6 = 31
}
type RegisterName = keyof typeof RISCVRegisters;
declare const RISCV_REGISTERS: RegisterName[];
type HandlerName = keyof HandlerMap;
type HandlerMapFns = {
[K in HandlerName]: (...args: HandlerMap[K]['in']) => HandlerMap[K]['out'];
};
declare function registerHandlers(riscv: JsRiscV, handlers: HandlerMapFns): void;
declare function unimplementedHandler(name: HandlerName): () => never;
/**
* Interface for interacting with a RISCV simulator.
*/
interface JsRiscV {
/**
* Assembles the program.
*/
assemble(): RISCVAssembleResult;
/**
* Initializes the simulator.
* @param startAtMain If true, starts execution at the 'main' label. Otherwise, starts at the first instruction.
*/
initialize(startAtMain: boolean): void;
/**
* Gets the current stop reason.
*/
getStopReason(): StopReason;
/**
* Executes a single instruction.
* @returns True if the execution is complete, false otherwise.
*/
step(): StopReason;
/**
* Gets the 8 condition flags.
*/
getConditionFlags(): number[];
/**
* Sets the size of the undo stack, must be called before assembling the program.
* @param size
*/
setUndoSize(size: number): void;
/**
* Undoes the last instruction executed.
*/
undo(): void;
/**
* Gets the statement at the given address.
* @param address
*/
getStatementAtAddress(address: number): JsProgramStatement;
/**
* Gets the statement at the given source line.
* @param line
*/
getStatementAtSourceLine(line: number): JsProgramStatement;
getTokenizedLines(): RiscvTokenizedLine[];
/**
* Checks if the simulation can be undone.
* @returns True if the simulation can be undone, false otherwise.
* */
canUndo: boolean;
/**
* Gets the call stack.
* @returns An array of memory addresses representing the call stack.
*/
getCallStack(): JsRiscVStackFrame[];
/**
* Gets the compiled statements.
* @returns An array of `JsProgramStatement` objects representing the compiled program.
*/
getCompiledStatements(): JsProgramStatement[];
getParsedStatements(): JsInstructionToken[];
/**
* Gets the label at the given address.
* @param address The memory address.
* @returns The label at the given address, or null if no label is found.
*/
getLabelAtAddress(address: number): string | null;
/**
* Sets whether the undo feature is enabled.
* @param enabled True to enable the undo feature, false to disable it.
*/
setUndoEnabled(enabled: boolean): void;
/**
* Simulates until the stop condition is met. it might be a breakpoint, exception, etc...
*/
simulate(): StopReason;
/**
* Simulates the program for a limited number of instructions.
* @param limit The maximum number of instructions to execute.
* @returns True if the execution is complete, false otherwise.
*/
simulateWithLimit(limit: number): StopReason;
/**
* Simulates the program until a breakpoint is reached.
* @param breakpoints An array of memory addresses where the simulation should pause.
* @returns True if the execution is complete, false otherwise.
*/
simulateWithBreakpoints(breakpoints: number[]): StopReason;
/**
* Simulates the program with both breakpoints and a limit.
* @param breakpoints An array of memory addresses where the simulation should pause.
* @param limit The maximum number of instructions to execute.
* @returns True if the execution is complete, false otherwise.
*/
simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): StopReason;
/**
* Gets the value of a register.
* @param register The name of the register.
* @returns The value of the register.
*/
getRegisterValue(register: RegisterName): number;
/**
* Gets the value of a register in long format.
* @param register The name of the register.
* @returns The value of the register in long format.
*/
getRegisterValueLong(register: RegisterName): string;
/**
* Registers a handler function for a specific event or condition.
* @param name The name of the event or condition.
* @param handler The handler function to be called when the event occurs. The function signature depends on the event name.
*/
registerHandler<T extends HandlerName>(name: T, handler: (...args: HandlerMap[T]['in']) => HandlerMap[T]['out']): void;
/**
* Gets the current value of the stack pointer.
* @returns The value of the stack pointer.
*/
stackPointer: number;
/**
* Gets the current value of the stack pointer in long format.
* @returns The value of the stack pointer in long format.
*/
stackPointerLong: string;
/**
* Gets the current value of the program counter.
* @returns The value of the program counter.
*/
programCounter: number;
/**
* Gets the current value of the program counter in long format.
* @returns The value of the program counter in long format.
*/
programCounterLong: string;
/**
* Gets the values of all registers.
* @returns An array containing the register values. The order of the values is implementation defined.
*/
getRegistersValues(): number[];
/**
* Gets the values of all registers in long format.
* @returns An array containing the register values in long format. The order of the values is implementation defined.
*/
getRegistersValuesLong(): string[];
/**
* Gets the undo stack.
* @returns An array of `JsBackStep` objects representing the history of the simulation.
*/
getUndoStack(): JsBackStep[];
/**
* Reads a sequence of bytes from memory.
* @param address The starting memory address.
* @param length The number of bytes to read.
* @returns An array of bytes read from memory.
*/
readMemoryBytes(address: number, length: number): number[];
/**
* Writes a sequence of bytes to memory.
* @param address The starting memory address.
* @param bytes An array of bytes to write to memory.
*/
setMemoryBytes(address: number, bytes: number[]): void;
/**
* Gets the index of the current statement in the assembled program.
* @returns The index of the current statement.
*/
getCurrentStatementIndex(): number;
/**
* Gets the next statement to be executed.
* @returns The next `JsProgramStatement`.
*/
getNextStatement(): JsProgramStatement;
/**
* Sets the value of a register.
* @param register The name of the register.
* @param value The value to set the register to.
*/
setRegisterValue(register: RegisterName, value: number): void;
/**
* Checks if the simulation has terminated.
* @returns True if the simulation has terminated, false otherwise.
*/
terminated: boolean;
}
/**
* Creates a new RISCV simulator from the given source code.
* @param source The source code to assemble.
* @returns A new `JsRiscV` object.
*/
declare function makeRiscVfromSource(source: string): JsRiscV;
/**
* Initializes the RISCV simulator.
*/
declare function initializeRISCV(): void;
export { BackStepAction, ConfirmResult, DialogType, type HandlerMap, type HandlerMapFns, type JsBackStep, type JsInstruction, type JsInstructionToken, type JsProgramStatement, type JsRiscV, type JsRiscVStackFrame, RISCV, type RISCVAssembleError, type RISCVAssembleResult, RISCVRegisters, RISCV_REGISTERS, type RegisterName, type RiscvTokenizedLine, StopReason, registerHandlers, unimplementedHandler };