Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

chip8js

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chip8js

A Chip-8 emulator written in JavaScript (Node.js and Browser).

  • 1.0.3
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
2
Maintainers
1
Weekly downloads
 
Created
Source

Chip8.js License: MIT chip8js on NPM Build Status

A Chip-8 emulator written in JavaScript (Node.js).

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.

Table of Contents

Installation

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

You can add the module directly from the chip8js npm package.

yarn add chip8js
npm i chip8js

And require the RomBuffer and CPU classes.

// index.js
const { RomBuffer, CPU } = require('chip8js')

Or you can clone the repo. The only dependency of Chip8.js is jest for testing. Run yarn to install.

git clone git@github.com:taniarascia/chip8.git
cd chip8
yarn

Usage

Chip8.js is available via Node.js in the terminal, or in the browser.

Browser

Development
yarn watch # watch for changes and rebuild
cd web && http-server # spin up server

yarn web # build for web
Deploy
# remove web/bundle.js
git add web && git commit -m "update web version"
git subtree push --prefix web origin gh-pages

Terminal

Chip-8 compatible ROMs can be saved in the roms/ directory. Create a ROM buffer of a ROM and load the data into the CPU. Execute the program.

yarn play roms/<ROM>

Automated 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
yarn test

# Run test suites and view coverage
yarn 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', async () => {
  cpu.load({ data: [0x3abb] })
  await 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', async () => {
  cpu.load({ data: [0x3abb] })
  cpu.registers[0xa] = 0xbb
  await cpu.step()

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

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:

Acknowledgements

Author

License

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

Keywords

FAQs

Package last updated on 11 Jul 2019

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc