A web-based emulator for the classic Chip-8 virtual machine. The core of the emulator is written in JavaScript, with HTML and CSS for the user interface, and Express for the backend. The project is designed in a way to closely resemble the original Chip-8 architecture, with configurable quirks to support a wider range of Chip-8 ROMs.

Features

ROM Management

  • Built-in Chip8Archive ROM library, offering easy access to public domain Chip-8 ROMs.
  • Drag‑and‑drop import for custom ROM files.

Debugging

  • Realtime visualization of the internal state of the emulator including the memory, registers, timers, pointers, and stack.
  • Pause, resume, single‑step execution (instruction or cycle) for ROM debugging.

Configurable Quirks

  • Support for shift quirks, load-store quirks, jump quirks, clipping/wrapping quirks, logic quirks, and v-blank quirks. See chip-8.github.io/database/#options for more information.

  • Shift Quirks

case 8XY6:
    if (!this.quirkShift) {
        Vx = Vy;
    }
    var result = Vx >> 1;
    this._set_carry(instruction.x, result, Vy & 0x1);
    return true;

case 8XYE:
    if (!this.quirkShift) {
        Vx = Vy;
    }
    var result = Vx << 1;
    this._set_carry(instruction.x, result, (Vy >> 7) & 0x1);
    return true;
  • Load-Store Quirks
case FX55:
    for (let i = 0; i <= instruction.x; i++) {
        this.memory.set(
            (this.indexRegister + i) & 0xfff,
            this.registers.get(i)
        );
    }

    this.indexRegister += i_increment;
    return true;

case FX65:
    for (let i = 0; i <= instruction.x; i++) {
        this.registers.set(
            i,
            this.memory.get((this.indexRegister + i) & 0xfff)
        );
    }

    this.indexRegister += i_increment;
    return true;
  • Jump Quirks
case BNNN:
    if (this.quirkJump) {
        this.programCounter = instruction.nnn + Vx;
    } else {
        this.programCounter =
            instruction.nnn + this.registers.get(0);
    }
    return true;
  • Clip/Wrap Quirks
case DXYN:
    let screenX = Vx % this.screen.renderWidth;
    let screenY = Vy % this.screen.renderHeight;
    let spriteHeight = instruction.n;
    let spriteWidth = 8;
    let yCondition = this.quirkWrap
        ? (y) => y < spriteHeight
        : (y) => y < spriteHeight && y + screenY < this.screen.renderHeight;
    let xCondition = this.quirkWrap
        ? (x) => x < spriteWidth
        : (x) => x < spriteWidth && x + screenX < this.screen.renderWidth;
    this.registers.set(0xf, 0);

    for (let y = 0; yCondition(y); y++) {
        let pixel_row = this.memory.get(this.indexRegister + y);
        for (let x = 0; xCondition(x); x++) {
            this.registers.set(
                0xf,
                this.screen.setPixel(
                    x + screenX,
                    y + screenY,
                    (pixel_row >> 7) & 0b1
                ) || this.registers.get(0xf)
            );
            pixel_row <<= 1;
        }
    }

    return true;
  • Logic Quirks
case 8XY1:
    this.registers.set(instruction.x, Vx | Vy);
    if (this.quirkLogic) {
        this.registers.set(0xf, 0);
    }
    return true;

case 8XY2:
    this.registers.set(instruction.x, Vx & Vy);
    if (this.quirkLogic) {
        this.registers.set(0xf, 0);
    }
    return true;

case 8XY3:
    this.registers.set(instruction.x, Vx ^ Vy);
    if (this.quirkLogic) {
        this.registers.set(0xf, 0);
    }
    return true;
processFrame() {
    ...
    for (let i = 0; i < this.instructionsPerFrame; i++) {
        let instruction = this.cpu.fetch();
        if (this.cpu.quirkVBlank && instruction.type == 0xD) {
            break;
        }
        this.cpu.programCounter += 2;
        this.cpu.execute(this.cpu.decode(instruction));
    }
    ...
}

Cross-Platform

  • Smooth performance on a wide range of devices, including mobile devices, tablets, and desktops.
  • Virtual on-screen keypad with multi-touch support for mobile/touchscreen devices, in addition to physical keyboard input mapping.

Service Worker and PWA

  • Service worker for caching, allowing the emulator to run without an internet connection.
  • Support for installing the emulator as a Progressive Web App (PWA), providing a native app-like experience on supported devices.

View code on GitHub or view the live demo chip8.mpreetsingh.com.