npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@specy/mips

v3.6.0

Published

The MARS mips interpreter in JS

Readme

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 simulator with TeaVM, with some glue code on top to make it easier to use. It is part of a family of javascript assembly interpreters/simulators:

Installation

npm install @specy/mips

Usage

Create a simulator from a virtual source tree and the canonical path of its entry file. Assembly starts at that file and expands .include directives transitively; files that are not reached are ignored.

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.

import { makeMipsFromFiles, JsMips, RegisterName, BackStepAction } from '@specy/mips';

const files = {
  'src/main.asm': `
    .include "lib/exit.asm"
    .text
    .globl main

  main:
    exit
  `,
  'src/lib/exit.asm': `
    .macro exit
      li $v0, 10
      syscall
    .end_macro
  `,
} as const;

const mipsSimulator: JsMips = makeMipsFromFiles(files, 'src/main.asm');

mipsSimulator.assemble();
mipsSimulator.initialize(true); // Start at 'main'

while (!mipsSimulator.terminated) {
  await 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);
});

// Handlers may also be async: returning a promise suspends the simulation until it settles,
// so IO can be backed by an async API without blocking the event loop.
mipsSimulator.registerHandler("readInt", async () => {
    return await promptUserForANumber();
});

// 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
await mipsSimulator.simulateWithBreakpoints(breakpoints);

//Simulating with a limit
const limit = 100
await mipsSimulator.simulateWithLimit(limit);

//Simulating with breakpoints and a limit
await mipsSimulator.simulateWithBreakpointsAndLimit(breakpoints, limit);

//Setting Register Values:
mipsSimulator.setRegisterValue("$t0", 42);

IO handlers

Every syscall that needs to talk to the outside world goes through a handler you register. A handler can return its result directly, or return a promise of it:

mipsSimulator.registerHandler("readInt", () => 42);                  // synchronous
mipsSimulator.registerHandler("readString", () => fetchLine());      // asynchronous

When a handler returns a promise the simulation suspends at that instruction and resumes once the promise settles, so nothing has to be shoehorned into a synchronous API. Because of that, step, simulateWithLimit, simulateWithBreakpoints and simulateWithBreakpointsAndLimit all return a promise. Everything else on JsMips (registers, memory, statements, the undo stack) stays synchronous.

If a handler's promise rejects, the pending step/simulate* call rejects as well, with the rejection reason in the message.

Program time is a handler too, so a run can be given a clock of its own: sleep answers syscall 32 and time answers syscall 30. A live run resolves sleep on a timer and returns Date.now() from time; a scripted run can settle sleep immediately, advance a virtual clock by the requested milliseconds and return that clock instead, which keeps elapsed-time output reproducible.

Memory observers

A memory-mapped device - a framebuffer, a keyboard register - is modelled by observing the memory the program reads and writes:

// Every write in a framebuffer: (address, length, value), with the width of the store in bytes.
const frame = mipsSimulator.addMemoryWriteObserver(0x10010000, 0x10012ffc, (address, length, value) => {
    screen.markDirty(address)
})

// One memory-mapped register: reads and writes, either of which may be null.
const receiver = mipsSimulator.addMemoryAccessObserver(
    0xffff0004,
    () => keyboard.consumeCharacter(),
    null
)

mipsSimulator.removeMemoryObserver(frame)
mipsSimulator.removeMemoryObservers()
  • Addresses must be word-aligned, endAddress is inclusive and covers its whole word, and a range may not cross 0x80000000; a registration that breaks any of these throws. Either form of a high address is accepted: 0xffff0000 and 0xffff0000 | 0 name the same word.
  • Handlers are given signed 32 bit integers, as the guest holds them: the register at 0xffff000c arrives as -65524, and a pixel word with its high bit set arrives negative. Apply >>> 0 wherever the unsigned form is wanted.
  • Handlers run synchronously inside the instruction that caused the access, so they must be cheap and must not write back into their own range. A returned promise is ignored, unlike an IO handler's.
  • An observer is notified after the access, with the value the program read or stored. A register whose value is consumed by reading it must therefore be reloaded from the handler, with setPeripheralWord, for the next read.
  • Observers live on the simulator's memory, which assembling and initializing only clear the contents of, so a registration survives assemble() and initialize() and - like a registered IO handler - is shared by every JsMips instance. Notifications start once a program has been assembled.
  • undo() restores memory through the same stores, so an observed range reports the restored values as ordinary writes and a device that follows notifications alone stays in step.

API

makeMipsFromFiles(files: MIPSSourceSet, entryFile: string): JsMips

Creates a JsMips instance from a snapshotted virtual source tree. Source paths are canonical, root-relative, case-sensitive POSIX paths such as src/main.asm. Relative includes resolve from the including file, and includes beginning with / resolve from the virtual root.

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(): Promise<boolean>: Executes a single instruction. Resolves to true if the execution is complete, false otherwise.
  • simulateWithLimit(limit: number): Promise<boolean>: Simulates the program for a maximum of limit instructions. Resolves to true if the execution is complete, false otherwise.
  • simulateWithBreakpoints(breakpoints: number[]): Promise<boolean>: Simulates the program until a breakpoint is reached. breakpoints is an array of memory addresses. Resolves to true if the execution is complete, false otherwise.
  • simulateWithBreakpointsAndLimit(breakpoints: number[], limit: number): Promise<boolean>: Simulates the program until a breakpoint is reached or the limit is reached. Resolves to 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. A handler may return its result directly or return a promise of it, see IO handlers.
  • 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. Notifies no memory observer: inspecting memory from the host is not the program reading it.
  • setMemoryBytes(address: number, bytes: number[]): void: Writes bytes to memory starting at address, the way the program does: write observers are notified and, while undo is enabled, an undo step is recorded per byte.
  • setPeripheralWord(address: number, value: number): void: Writes one word-aligned word as a peripheral would, notifying no observer and recording no undo step. See memory observers.
  • addMemoryWriteObserver(startAddress: number, endAddress: number, handler): number: Observes every write in an address range. See memory observers.
  • addMemoryAccessObserver(address: number, onRead, onWrite): number: Observes reads and writes of one word. See memory observers.
  • removeMemoryObserver(handle: number): void: Removes one registration.
  • removeMemoryObservers(): void: Removes every registration.
  • countMemoryObservers(): number: The number of live registrations.
  • getTokenizedLines(): MipsTokenizedLine[]: Returns the flattened tokenized lines with their original source paths and one-based line numbers.
  • getParsedStatements(): JsProgramStatement[]: Returns the parsed source statements.
  • getCompiledStatements(): JsProgramStatement[]: Returns every generated machine statement.
  • getStatementsAtSourceLocation(sourcePath: string, sourceLine: number): JsProgramStatement[]: Returns every machine statement generated by one original source line, including pseudo-instruction and macro expansions.
  • 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.
  • MIPSSourceSet: Read-only mapping from canonical source paths to source text.
  • MipsTokenizedLine: A tokenized line with its original and processed source text and source location.
  • 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.