@specy/mips
Version:
The MARS mips interpreter in JS
119 lines (88 loc) • 5.88 kB
Markdown
# Mips-js
A JavaScript library for simulating MIPS assembly code. This library provides an interface to assemble, execute, and inspect the state of a MIPS simulator. It's built by compiling the [mars](https://github.com/dpetersanderson/MARS) simulator with [TeaVM](https://teavm.org/), with some glue code on top to make it easier to use.
It is part of a family of javascript assembly interpreters/simulators:
- MIPS: [git repo](https://github.com/Specy/mars), [npm package](https://www.npmjs.com/package/@specy/mips)
- RISC-V: [git repo](https://github.com/Specy/rars), [npm package](https://www.npmjs.com/package/@specy/risc-v)
- X86: [git repo](https://github.com/Specy/x86-js), [npm package](https://www.npmjs.com/package/@specy/x86)
- M68K: [git repo](https://github.com/Specy/s68k), [npm package](https://www.npmjs.com/package/@specy/s68k)
## Installation
```bash
npm install @specy/mips
```
## Usage
First create an instance of the simulator with the `makeMipsfromSource` 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.
⚠️**WARNING**⚠️ You must have only one instance of the simulator at a time. Memory, registers, and other state are shared between instances.
```typescript
import { makeMipsfromSource, JsMips, RegisterName, BackStepAction } from '@specy/mips';
const sourceCode = `
.text
.globl main
main:
li $v0, 10 # Exit program
syscall
`;
const mipsSimulator: JsMips = makeMipsfromSource(sourceCode);
mipsSimulator.assemble();
mipsSimulator.initialize(true); // Start at 'main'
while (!mipsSimulator.terminated) {
mipsSimulator.step();
}
const pc = mipsSimulator.programCounter;
const v0 = mipsSimulator.getRegisterValue('$v0');
console.log(`Program Counter: ${pc}`);
console.log(`$v0: ${v0}`);
// Accessing memory:
const data = mipsSimulator.readMemoryBytes(0xffff0000, 4); // Read 4 bytes from address 0x1000
mipsSimulator.setMemoryBytes(0xffff0000, [0x01, 0x02, 0x03, 0x04]); // Write 4 bytes to address 0x1000
// Registering Handlers (for syscalls and other events):
mipsSimulator.registerHandler("printInt", (value: number) => {
console.log("printInt syscall called with:", value);
});
// Accessing the undo stack:
const undoStack = mipsSimulator.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
mipsSimulator.simulateWithBreakpoints(breakpoints);
//Simulating with a limit
const limit = 100
mipsSimulator.simulateWithLimit(limit);
//Simulating with breakpoints and a limit
mipsSimulator.simulateWithBreakpointsAndLimit(breakpoints, limit);
//Setting Register Values:
mipsSimulator.setRegisterValue("$t0", 42);
```
## API
### `makeMipsfromSource(source: string): JsMips`
Creates a new `JsMips` instance from MIPS assembly source code.
### `JsMips` Interface
#### Methods
* `assemble()`: Assembles the program.
* `initialize(startAtMain: boolean)`: Initializes the simulator. If `startAtMain` is true, execution begins at the `main` label; otherwise, it starts at the first instruction.
* `step(): boolean`: Executes a single instruction. Returns `true` if the execution is complete, `false` otherwise.
* `simulateWithLimit(limit: number): boolean`: Simulates the program for a maximum of `limit` instructions. Returns `true` if the execution is complete, `false` otherwise.
* `simulateWithBreakpoints(breakpoints: number[]): boolean`: Simulates the program until a breakpoint is reached. `breakpoints` is an array of memory addresses. Returns `true` if the execution is complete, `false` otherwise.
* `simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): boolean`: Simulates the program until a breakpoint is reached or the limit is reached. Returns `true` if the execution is complete, `false` otherwise.
* `getRegisterValue(register: RegisterName): number`: Returns the value of the specified register.
* `registerHandler(name: HandlerName, handler: Function): void`: Registers a handler function for a specific event (e.g., syscalls). See the `HandlerName` type for possible event names.
* `getStackPointer(): number`: Returns the current value of the stack pointer.
* `getProgramCounter(): number`: Returns the current value of the program counter.
* `getRegistersValues(): number[]`: Returns an array of all register values.
* `getUndoStack(): JsBackStep[]`: Returns the undo stack, which contains information about previous simulation steps.
* `readMemoryBytes(address: number, length: number): number[]`: Reads `length` bytes from memory starting at `address`.
* `setMemoryBytes(address: number, bytes: number[]): void`: Writes `bytes` to memory starting at `address`.
* `getCurrentStatementIndex(): number`: Returns the index of the current statement in the assembled program.
* `getNextStatement(): JsProgramStatement`: Returns the next `JsProgramStatement` to be executed.
* `setRegisterValue(register: RegisterName, value: number): void`: Sets the value of the specified register.
* `hasTerminated(): boolean`: Returns `true` if the simulation has terminated, `false` otherwise.
#### Types
* `RegisterName`: Type for MIPS register names (e.g., `$zero`, `$v0`, `$ra`).
* `BackStepAction`: Enum representing the types of undo actions.
* `JsProgramStatement`: Interface representing a statement in the assembled program.
* `JsBackStep`: Interface representing a back step in the simulation.
* `HandlerName`: Type representing the name of a handler function.