4 min read

Emulador CHIP-8 usando WebAssembly (WASM)

Table of Contents

๐ŸŽฎ Chip8_WASM

CHIP-8 Emulator using WebAssembly (WASM)

This project implements a CHIP-8 emulator compiled to WebAssembly with Emscripten, offering a high-performance emulation experience in web browsers.


Grabaciรณn 2025-08-16 193637

๐Ÿ“‹ Overview

CHIP-8 is an interpreted programming language developed by Joseph Weisbecker in the mid-1970s. This emulator allows running classic CHIP-8 ROMs directly in the browser using WebAssembly for optimal performance.

โœจ Features

  • ๐Ÿš€ High performance with WebAssembly
  • ๐ŸŽฏ Full compatibility with CHIP-8 ROMs
  • ๐Ÿ”ง Integrated debugging API
  • ๐ŸŒ Browser execution without plugins
  • ๐Ÿ“ฑ Responsive and cross-platform

โšก Compilation

Prerequisites

  • Emscripten SDK installed and configured
  • Source file chip8.cpp
  • .ch8 ROMs for testing

Compilation Command

Use emcc to compile chip8.cpp into a JavaScript + WASM module:

emcc chip8.cpp -o chip8.js \
  -s EXPORTED_FUNCTIONS='["_load_rom","_getDebuggerString","_step","_malloc","_free"]' \
  -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap","HEAPU8","UTF8ToString"]' \
  -s ALLOW_MEMORY_GROWTH=1 \
  -s TOTAL_MEMORY=67108864

Compilation Parameters

ParameterDescription
EXPORTED_FUNCTIONSC++ functions exposed to JavaScript
EXPORTED_RUNTIME_METHODSRuntime methods for interoperability
ALLOW_MEMORY_GROWTHAllows dynamic memory growth
TOTAL_MEMORYInitial allocated memory (64MB)

๐Ÿ—๏ธ System Architecture

Flow Diagram

          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ”‚   ROM .ch8 โ”‚
          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
                 โ–ผ
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚  C++ / Chip-8   โ”‚
        โ”‚ (emulator core) โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚ Compiled with
               โ”‚ Emscripten
               โ–ผ
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚   WebAssembly   โ”‚
        โ”‚   (chip8.wasm)  โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚
               โ–ผ
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚  JavaScript API โ”‚
        โ”‚ (ccall, cwrap)  โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ”‚   render() JS   โ”‚
       โ”‚ (CHIP8 graphics)โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Main Components

  1. C++ Core: Main CHIP-8 emulator logic
  2. WebAssembly Module: Optimized compiled code
  3. JavaScript Bridge: API for browser communication
  4. Rendering System: Graphics rendering system

๐Ÿ”ง API Reference

Exported Functions

load_rom(romData, size)

Loads a CHIP-8 ROM into memory.

  • Parameters:
    • romData: Pointer to ROM data
    • size: ROM size in bytes
  • Returns: true if loading was successful

step()

Executes one instruction cycle of the emulator.

  • Returns: Current emulator state

getDebuggerString()

Gets debug information of the current state.

  • Returns: String with register and memory information

๐Ÿš€ Basic Usage

Change rom

 async function loadRom(path) {
      const response = await fetch(path);
      if (!response.ok) {
        throw new Error(`Error cargando ROM: ${response.statusText}`);
      }
      const buffer = await response.arrayBuffer();
      const bytes = new Uint8Array(buffer);

      const ptr = Module._malloc(bytes.length);
      if (!ptr) throw new Error('No memory load');

      Module.HEAPU8.set(bytes, ptr);
      Module.ccall('load_rom', null, ['number', 'number'], [ptr, bytes.length]);
      Module._free(ptr);

      console.log('ROM load !!!!:', path);
    }

    Module.onRuntimeInitialized = async function () {
  console.log('WASM runtime listo');
  try {
    await loadRom('rom/1dcell.ch8');

    const cyclesPerFrame = 10; 
    const frameMs = 1000 / 60; 

    setInterval(() => {
      for (let i = 0; i < cyclesPerFrame; i++) {
        Module.ccall('step', null, [], []);
      }
      updateDebugger(); // actualizar debugger
    }, frameMs);

  } catch (e) {
    console.error('Error load rom fail:', e);
  }
};
    function updateDebugger() {
      const ptr = Module.ccall('getDebuggerString', 'number', [], []);
      const dbgStr = Module.UTF8ToString(ptr);
      const html = dbgStr.replace(/\n/g, "<br>");
      document.getElementById('screen_debugger').innerHTML = html;
    }

๐ŸŽฎ Controls

CHIP-8 Keyboard Mapping

CHIP-8 Keypad    Keyboard
โ”Œโ”€โ”ฌโ”€โ”ฌโ”€โ”ฌโ”€โ”       โ”Œโ”€โ”ฌโ”€โ”ฌโ”€โ”ฌโ”€โ”
โ”‚1โ”‚2โ”‚3โ”‚Cโ”‚       โ”‚1โ”‚2โ”‚3โ”‚4โ”‚
โ”œโ”€โ”ผโ”€โ”ผโ”€โ”ผโ”€โ”ค  -->  โ”œโ”€โ”ผโ”€โ”ผโ”€โ”ผโ”€โ”ค
โ”‚4โ”‚5โ”‚6โ”‚Dโ”‚       โ”‚Qโ”‚Wโ”‚Eโ”‚Rโ”‚
โ”œโ”€โ”ผโ”€โ”ผโ”€โ”ผโ”€โ”ค       โ”œโ”€โ”ผโ”€โ”ผโ”€โ”ผโ”€โ”ค
โ”‚7โ”‚8โ”‚9โ”‚Eโ”‚       โ”‚Aโ”‚Sโ”‚Dโ”‚Fโ”‚
โ”œโ”€โ”ผโ”€โ”ผโ”€โ”ผโ”€โ”ค       โ”œโ”€โ”ผโ”€โ”ผโ”€โ”ผโ”€โ”ค
โ”‚Aโ”‚0โ”‚Bโ”‚Fโ”‚       โ”‚Zโ”‚Xโ”‚Cโ”‚Vโ”‚
โ””โ”€โ”ดโ”€โ”ดโ”€โ”ดโ”€โ”˜       โ””โ”€โ”ดโ”€โ”ดโ”€โ”ดโ”€โ”˜

๐Ÿ› ๏ธ Development and Debug

Debug Flags

For debug compilation:

emcc chip8.cpp -o chip8.js \
  -s EXPORTED_FUNCTIONS='["_load_rom","_getDebuggerString","_step","_malloc","_free"]' \
  -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap","HEAPU8","UTF8ToString"]' \
  -s ALLOW_MEMORY_GROWTH=1 \
  -s TOTAL_MEMORY=67108864 \
  -s ASSERTIONS=1 \
  -O0 -g

๐Ÿ“ฆ Project Structure

chip8_wasm/
โ”œโ”€โ”€ rom/
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ chip8.cpp
โ”œโ”€โ”€ chip8.wasm #compiler output
|โ”€โ”€ chip8.js #compiler output
โ””โ”€โ”€ README.md

๐Ÿค Contributing

  1. Fork the project
  2. Create a feature branch (git checkout -b feature/new-feature)
  3. Commit your changes (git commit -am 'Add new feature')
  4. Push to the branch (git push origin feature/new-feature)
  5. Open a Pull Request

๐Ÿ”— References


Enjoy coding and playing with CHIP-8! ๐ŸŽฎโœจ