All Projects â†’ taniarascia â†’ Chip8

taniarascia / Chip8

Licence: mit
🎮 ‎ A Chip-8 emulator written in JavaScript for web, CLI, and native UI

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Chip8

chip8emu
A Terminal Based Chip-8 Emulator
Stars: ✭ 28 (-89.15%)
Mutual labels:  emulator
pokegb
A gameboy emulator that only plays Pokemon Blue, in ~50 lines of c++.
Stars: ✭ 1,166 (+351.94%)
Mutual labels:  emulator
speljongen
gameboy emulator written in c++
Stars: ✭ 15 (-94.19%)
Mutual labels:  emulator
SnesJs
A SNES emulator, in javascript
Stars: ✭ 28 (-89.15%)
Mutual labels:  emulator
mos6502
MOS 6502 emulator written in Rust
Stars: ✭ 25 (-90.31%)
Mutual labels:  emulator
JFTSE
JFTSE Open Source MMO Framework
Stars: ✭ 23 (-91.09%)
Mutual labels:  emulator
i8080-Space-Invaders
Intel i8080 Space Invaders Arcade Emulator
Stars: ✭ 19 (-92.64%)
Mutual labels:  emulator
Riscv Rust
RISC-V processor emulator written in Rust+WASM
Stars: ✭ 253 (-1.94%)
Mutual labels:  emulator
slippers
A Club Penguin server emulator for the original 2005 client
Stars: ✭ 30 (-88.37%)
Mutual labels:  emulator
wcecl
Allows to run Windows CE applications on Windows!
Stars: ✭ 54 (-79.07%)
Mutual labels:  emulator
miniqubit
Quantum emulator of the IBM Quantum experience
Stars: ✭ 24 (-90.7%)
Mutual labels:  emulator
Kotlin-Gameboy-Emulator
A GameBoy emulator written in Kotlin
Stars: ✭ 12 (-95.35%)
Mutual labels:  emulator
l2auth
Lineage 2 C4 server written in C as a learning exercise
Stars: ✭ 31 (-87.98%)
Mutual labels:  emulator
khedgb
Experiments in Game Boy emulation
Stars: ✭ 15 (-94.19%)
Mutual labels:  emulator
OldScape
Oldschool Runescape Emulation
Stars: ✭ 30 (-88.37%)
Mutual labels:  emulator
spym
MIPS ISA toolchain including a (dis)assembler, debugger, and vm.
Stars: ✭ 15 (-94.19%)
Mutual labels:  emulator
1964js
Nintendo64 emulator in JavaScript
Stars: ✭ 57 (-77.91%)
Mutual labels:  emulator
Ktnes
A multiplatform NES emulator written in Kotlin
Stars: ✭ 257 (-0.39%)
Mutual labels:  emulator
PokemonBattleEngine
A C# library that can emulate Pokémon battles.
Stars: ✭ 92 (-64.34%)
Mutual labels:  emulator
QEMU-Manager
macOS graphical frontend to QEMU
Stars: ✭ 175 (-32.17%)
Mutual labels:  emulator

Chip8.js

License: MIT chip8js on NPM Build Status Coverage Status

A Chip-8 emulator written in JavaScript.

Chip-8 is a simple, interpreted, programming language which was first used on some do-it-yourself computer systems in the late 1970s and early 1980s.

View the demo | Read the article

Table of Contents

Installation

This guide assumes you already have Node.js and npm installed.

Prior to installing Chip8.js, you must have CMake installed.

brew install cmake

Clone the repository and install.

git clone [email protected]:taniarascia/chip8.git
cd chip8
npm i

Usage

Chip8.js can be run on the web, in a terminal, or using native keybindings.

Web

Development

Spin up a local server during development.

# watch for changes and rebuild
npm run watch:web

# spin up server on localhost:8080
cd web && http-server 

Deployment

Build and bundle the code for the web.

npm run build:web

Deploy to GitHub.

# remove web/bundle.js from .gitignore
git add web && git commit -m "update web version"

# delete gh-pages branch from origin before push
git subtree push --prefix web origin gh-pages

Terminal

Run Chip8.js in the terminal by selecting a ROM.

npm run play:terminal roms/<ROM>

Native

Run Chip8.js natively with raylib (experimental).

npm run play:native roms/<ROM>

Motivation

Chip8.js is a project to write a Chip-8 emulator in JavaScript. The main motivation is to learn lower level programming concepts and to increase familiarity with the Node.js environment.

Here are some of the concepts I learned while writing this program:

  • The base system: specifically base 2 (binary), base 10 (decimal), base 16 (hexadecimal), how they interact with each other and the concept of abstract numbers in programming
  • Bits, nibbles, bytes, ASCII encoding, and big and little endian values
  • Bitwise operators - AND (&), OR (|), XOR (^), left shift (<<), right shift (>>) and how to use them for masking, setting, and testing values
  • Using the Node built-in file system (fs)
  • The concept of a raw data buffer and how to work with it, how to convert an 8-bit buffer to a 16-bit big endian array
  • Writing and understanding a 8-bit and 16-bit hex dump
  • How to disassemble and decode an opcode into instructions a CPU can use
  • How a CPU can utilize memory, stack, program counters, stack pointers, memory addresses, and registers
  • How a CPU implements fetch, decode, and execute

And here are some articles I wrote based on those concepts:

Testing

The unit tests for Chip8.js use the Jest testing framework. You can run all test suites with or without displaying coverage.

# Run test suites
npm run test

# Run test suites and view coverage
npm run test --coverage

Chip8.js has two suites of unit tests:

  • Opcode instruction masks and arguments
  • CPU implementation of instructions

Instruction tests

The instruction tests cover the INSTRUCTION_SET found in data/instructionSet.js. Each instruction has:

  • A key: for internal use
  • An id: for a unique name
  • A name: for the type of instruction)
  • A mask: to filter out arguments from instruction signifiers)
  • A pattern: to match the mask to the specific instruction pattern
  • arguments, each of which contain:
    • A mask: to filter the nibble(s) to arguments
    • A shift: to shift it by location
    • A type: to signify the type of argument
// data/instructionSet.js

{
  key: 6,
  id: 'SE_VX_NN',
  name: 'SE',
  mask: 0xf000,
  pattern: 0x3000,
  arguments: [{ mask: 0x0f00, shift: 8, type: 'R' }, { mask: 0x00ff, shift: 0, type: 'NN' }],
}

Each unit test checks an opcode to an instruction and tests:

  • The unique id to ensure the correct instruction is running for the mask/pattern
  • The number of arguments
  • The value of the arguments
// tests/instructions.test.js

test('6: Expect disassembler to match opcode 3xnn to instruction SE_VX_NN', () => {
  expect(Disassembler.disassemble(0x3abb).instruction).toHaveProperty('id', 'SE_VX_NN')
  expect(Disassembler.disassemble(0x3abb).args).toHaveLength(2)
  expect(Disassembler.disassemble(0x3abb).args[0]).toBe(0xa)
  expect(Disassembler.disassemble(0x3abb).args[1]).toBe(0xbb)
})

There are 35 instruction tests for 35 opcodes (the first instruction, CLS, is no longer implemented).

CPU tests

The CPU decodes the opcode and returns the instruction object from data/instructionSet.js. Each instruction performs a specific, unique action in the case. The CPU tests test the state of the CPU after an executing an instruction.

In the below example, the instruction is skipping an instruction if Vx === nn, otherwise it's going to the next instruction as usual.

// classes/CPU.js

case 'SE_VX_NN':
  // Skip next instruction if Vx = nn.
  if (this.registers[args[0]] === args[1]) {
    this._skipInstruction()
  } else {
    this._nextInstruction()
  }
  break

Each CPU test:

  • Loads a RomBuffer containing the data of a single opcode
  • Sets up the state to make the instruction testable (if necessary)
  • Executes the step method
  • Tests all possible outcomes of an instruction and state updates

In this example, the instruction can either be skipped or not skipped depending on the arguments, and both cases are tested.

// tests/cpu.test.js

test('6: SE_VX_NN (3xnn) - Program counter should increment by two bytes if register x is not equal to nn argument', () => {
  cpu.load({ data: [0x3abb] })
  cpu.step()

  expect(cpu.PC).toBe(0x202)
})

test('6: SE_VX_NN (3xnn) - Program counter should increment by four bytes if register x is equal to nn argument', () => {
  cpu.load({ data: [0x3abb] })
  cpu.registers[0xa] = 0xbb

  cpu.step()

  expect(cpu.PC).toBe(0x204)
})

Acknowledgements

Author

License

This project is open source and available under the MIT License.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].