dont-track-me-google
Firefox and Chrome extensions to prevent Google from making links ugly.
๐ฎ โ A Chip-8 emulator written in JavaScript for web, CLI, and native UI.
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.
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 git@github.com:taniarascia/chip8.git cd chip8 npm i
Chip8.js can be run on the web, in a terminal, or using native keybindings.
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
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
Run Chip8.js in the terminal by selecting a ROM.
npm run play:terminal roms/<ROM>
Run Chip8.js natively with raylib (experimental).
npm run play:native roms/<ROM>
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:
&), OR (|), XOR (^), left shift (<<), right shift (>>) and how to use them for masking, setting, and testing valuesAnd here are some articles I wrote based on those concepts:
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:
The instruction tests cover the INSTRUCTION_SET found in data/instructionSet.js. Each instruction has:
key: for internal useid: for a unique namename: for the type of instruction)mask: to filter out arguments from instruction signifiers)pattern: to match the mask to the specific instruction patternarguments, each of which contain:
mask: to filter the nibble(s) to argumentsshift: to shift it by locationtype: 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:
id to ensure the correct instruction is running for the mask/pattern// 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).
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:
RomBuffer containing the data of a single opcodestep methodIn 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)
})
This project is open source and available under the MIT License.
more like this
Firefox and Chrome extensions to prevent Google from making links ugly.
search projects, people, and tags