Git Product home page Git Product logo

chip8's Introduction

Chip8.js Build Status License: MIT

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

Motivation

Chip8.js is an ongoing 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:

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 --save 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 [email protected]:taniarascia/chip8.git
cd chip8
yarn

Usage

Chip-8 compatible ROMs can be saved in the roms/ directory. A copy of Connect 4 is shipped with Chip8.js (at roms/CONNECT4) for example and testing purposes.

Load ROM

Create a ROM buffer of a ROM and load the data into the CPU. Execute the program.

yarn start roms/<ROM>

View hex dump

View a 16-bit hex dump of a ROM. (View more information on bits, bytes, bases, and hex dumps).

yarn hexdump roms/<ROM>

The output will look something like this (using CONNECT4 as an example).

000000 121a 434f 4e4e 4543 5434 2062 7920 4461
000010 7669 6420 5749 4e54 4552 a2bb f665 a2b4
000020 f655 6900 6801 6b00 6d0f 6e1f a2a5 600d
000030 6132 6200 d02f d12f 720f 321e 1234 d021
...

Run example ROM

yarn example

Reference

In progress.

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 constants/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
// constants/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('test instruction 06: 3xkk - SE Vx, byte', () => {
  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 constants/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 === kk, otherwise it's going to the next instruction as usual.

// classes/CPU.js

case 'SE_VX_NN':
  // Skip next instruction if Vx = kk.
  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('test cpu 06: 3xkk - SE Vx, byte', () => {
  cpu.load({ data: [0x3abb] })
  cpu.step()

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

  cpu.load({ data: [0x3abb] })
  cpu.registers[0xa] = 0xbb
  cpu.step()

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

Todos

  • Tests: Errors: 8
    • RET
    • CALL_ADDR
    • DRW_VX_VY_N
    • LD_F_VX
    • LD_B_VX
    • LD_I_VX
    • LD_VX_I
    • DW
  • Create Interface Classes

Acknowledgements

Author

License

The code is open source and available under the MIT License.

chip8's People

Contributors

taniarascia avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.