New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.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 - npm Package Compare versions

Comparing version 0.1.6 to 0.1.7

classes/interfaces/MockCpuInterface.js

221

classes/CPU.js

@@ -1,8 +0,13 @@

const { Disassembler } = require('./Disassembler')
const { FONT_SET } = require('../constants/fontSet')
const Disassembler = require('./Disassembler')
const { FONT_SET } = require('../data/fontSet')
const { DISPLAY_HEIGHT, DISPLAY_WIDTH } = require('../data/constants')
class CPU {
/**
* @param { class } cpuInterface I/O for Chip8
*/
constructor(cpuInterface) {
this.interface = cpuInterface
}
/**

@@ -19,2 +24,4 @@ * Set or reset the state to initial values.

* SP - Stack Pointer (8-bit) points at topost level of stack
*
* Any time an error would cause program execution to halt, halted is set to true
*/

@@ -31,5 +38,7 @@ reset() {

this.halted = false
this.soundEnabled = false
}
load(romBuffer) {
// Reset the CPU every time it is loaded
this.reset()

@@ -42,2 +51,3 @@

// Get ROM data from ROM buffer
const romData = romBuffer.data

@@ -47,4 +57,8 @@ let memoryStart = 0x200

// Place ROM data in memory starting at 0x200
// Since memory is stored in an 8-bit array and opcodes are 16-bit, we have to store the opcodes
// across two indices in memory
for (let i = 0; i < romData.length; i++) {
// set the first index with the most significant byte (i.e., 0x1234 would be 0x12)
this.memory[memoryStart + 2 * i] = romData[i] >> 8
// set the second index with the least significant byte (i.e., 0x1234 would be 0x34)
this.memory[memoryStart + 2 * i + 1] = romData[i] & 0x00ff

@@ -56,15 +70,19 @@ }

if (this.DT > 0) {
// Decrement the delay timer by one until it reaches zero
this.DT--
} else {
console.log('fixme')
}
if (this.ST > 0) {
// The sound timer is active whenever the sound timer register (ST) is non-zero.
this.ST--
} else {
console.log('fixme')
// When ST reaches zero, the sound timer deactivates.
if (this.soundEnabled) {
this.interface.disableSound()
this.soundEnabled = false
}
}
}
step() {
async step() {
if (this.halted) {

@@ -75,12 +93,11 @@ throw new Error(

}
// Fetch 16-bit opcode from memory
const opcode = this._fetch()
// Decode the opcode and get an object with the instruction and arguments
const instruction = this._decode(opcode)
console.log(
'PC: ' + this.PC.toString(16).padStart(4, '0') + ' ' + Disassembler.format(instruction),
opcode.toString(16).padStart(4, '0'),
instruction.instruction.id
)
this._execute(instruction)
// Execute code based on the instruction set
await this._execute(instruction) // will remove opcode after debugging
}

@@ -104,2 +121,6 @@

// We have to combine two bytes in memory back into one big endian opcode
// Left shifting by eight will move one byte over two positions - 0x12 will become 0x1200
// Left shifting by zero will keep one byte in the same position - 0x34 is still 0x34
// OR them together and get one 16-bit opcode - 0x1200 | 0x34 returns 0x1234
return (this.memory[this.PC] << 8) | (this.memory[this.PC + 1] << 0)

@@ -109,17 +130,20 @@ }

_decode(opcode) {
// Return { instruction <object>, args <array> }
return Disassembler.disassemble(opcode)
}
_execute(instruction) {
async _execute(instruction) {
const id = instruction.instruction.id
const args = instruction.args
// Execute code based on the ID of the instruction
switch (id) {
case 'CLS':
// Clear the display
console.log('todo - CLS')
// 00E0 - Clear the display
this.interface.clearDisplay()
this._nextInstruction()
break
case 'RET':
// Return from a subroutine.
// 00EE - Return from a subroutine.
if (this.SP === -1) {

@@ -133,8 +157,10 @@ this.halted = true

break
case 'JP_ADDR':
// Jump to location nnn.
// 1nnn - Jump to location nnn.
this.PC = args[0]
break
case 'CALL_ADDR':
// Call subroutine at nnn.
// 2nnn - Call subroutine at nnn.
if (this.SP === 15) {

@@ -149,4 +175,5 @@ this.halted = true

break
case 'SE_VX_NN':
// Skip next instruction if Vx = kk.
// 3xnn - Skip next instruction if Vx = nn.
if (this.registers[args[0]] === args[1]) {

@@ -158,4 +185,5 @@ this._skipInstruction()

break
case 'SNE_VX_NN':
// Skip next instruction if Vx != kk.
// 4xnn - Skip next instruction if Vx != nn.
if (this.registers[args[0]] !== args[1]) {

@@ -167,4 +195,5 @@ this._skipInstruction()

break
case 'SE_VX_VY':
// Skip next instruction if Vx = Vy.
// 5xy0 - Skip next instruction if Vx = Vy.
if (this.registers[args[0]] === this.registers[args[1]]) {

@@ -176,48 +205,61 @@ this._skipInstruction()

break
case 'LD_VX_NN':
// Set Vx = kk.
// 6xnn - Set Vx = nn.
this.registers[args[0]] = args[1]
this._nextInstruction()
break
case 'ADD_VX_NN':
// Set Vx = Vx + kk.
this.registers[args[0]] = this.registers[args[0]] + args[1]
// 7xnn - Set Vx = Vx + nn.
let v = this.registers[args[0]] + args[1]
if (v > 255) {
v -= 256
}
this.registers[args[0]] = v
this._nextInstruction()
break
case 'LD_VX_VY':
// Set Vx = Vy.
// 8xy0 - Set Vx = Vy.
this.registers[args[0]] = this.registers[args[1]]
this._nextInstruction()
break
case 'OR_VX_VY':
// Set Vx = Vx OR Vy.
// 8xy1 - Set Vx = Vx OR Vy.
this.registers[args[0]] |= this.registers[args[1]]
this._nextInstruction()
break
case 'AND_VX_VY':
// Set Vx = Vx AND Vy.
// 8xy2 - Set Vx = Vx AND Vy.
this.registers[args[0]] &= this.registers[args[1]]
this._nextInstruction()
break
case 'XOR_VX_VY':
// Set Vx = Vx XOR Vy.
// 8xy3 - Set Vx = Vx XOR Vy.
this.registers[args[0]] ^= this.registers[args[1]]
this._nextInstruction()
break
case 'ADD_VX_VY':
// Set Vx = Vx + Vy, set VF = carry.
// 8xy4 - Set Vx = Vx + Vy, set VF = carry.
this.registers[args[0]] += this.registers[args[1]]
this.registers[0xf] = this.registers[args[0]] + this.registers[args[1]] > 0xff ? 1 : 0
this.registers[args[0]] = this.registers[args[0]] + this.registers[args[1]]
this._nextInstruction()
break
case 'SUB_VX_VY':
// Set Vx = Vx - Vy, set VF = NOT borrow.
// 8xy5 - Set Vx = Vx - Vy, set VF = NOT borrow.
this.registers[0xf] = this.registers[args[0]] > this.registers[args[1]] ? 1 : 0
this.registers[args[0]] -= this.registers[args[1]]
this.registers[args[0]] = this.registers[args[0]] - this.registers[args[1]]
this._nextInstruction()
break
case 'SHR_VX_VY':
// Set Vx = Vx SHR 1.
// 8xy6 - Set Vx = Vx SHR 1.
this.registers[0xf] = this.registers[args[0]] & 1

@@ -227,4 +269,5 @@ this.registers[args[0]] >>= 1

break
case 'SUBN_VX_VY':
// Set Vx = Vy - Vx, set VF = NOT borrow.
// 8xy7 - Set Vx = Vy - Vx, set VF = NOT borrow.
this.registers[0xf] = this.registers[args[1]] > this.registers[args[0]] ? 1 : 0

@@ -235,11 +278,13 @@

break
case 'SHL_VX_VY':
// Set Vx = Vx SHL 1.
// 8xyE - Set Vx = Vx SHL 1.
this.registers[0xf] = this.registers[args[0]] >> 7
this.registers[args[0]] = this.registers[args[0]] << 1
this.registers[args[0]] <<= 1
this._nextInstruction()
break
case 'SNE_VX_VY':
// Skip next instruction if Vx != Vy.
// 9xy0 - Skip next instruction if Vx != Vy.
if (this.registers[args[0]] !== this.registers[args[1]]) {

@@ -251,20 +296,23 @@ this._skipInstruction()

break
case 'LD_I_ADDR':
// Set I = nnn.
// Annn - Set I = nnn.
this.I = args[1]
this._nextInstruction()
break
case 'JP_V0_ADDR':
// Jump to location nnn + V0.
// Bnnn - Jump to location nnn + V0.
this.PC = this.registers[0] + args[1]
break
case 'RND_VX_NN':
// Set Vx = random byte AND kk.
let random = Math.floor(Math.random() * 256)
// Cxnn - Set Vx = random byte AND nn.
let random = Math.floor(Math.random() * 0xff)
this.registers[args[0]] = random & args[1]
this._nextInstruction()
break
case 'DRW_VX_VY_N':
// Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
// The interpreter reads n bytes from memory, starting at the address stored in I.
// Dxyn - Display n-byte sprite starting at memory location I at (Vx, Vy), set VF = collision.
if (this.I > 4095 - args[2]) {

@@ -275,25 +323,28 @@ this.halted = true

let sprite = ''
// If no pixels are erased, set VF to 0
this.registers[0xf] = 0
// The interpreter reads n bytes from memory, starting at the address stored in I.
for (let i = 0; i < args[2]; i++) {
let line = this.memory[this.I + i]
// Each byte is a line of eight pixels
for (let position = 0; position < 8; position++) {
// Get the byte to set by position
let value = line & (1 << (7 - position)) ? 1 : 0
// If this causes any pixels to be erased, VF is set to 1
let x = (this.registers[args[0]] + position) % DISPLAY_WIDTH // wrap around width
let y = (this.registers[args[1]] + i) % DISPLAY_HEIGHT // wrap around height
for (let position = 7; position >= 0; position--) {
if (line & (1 << position)) {
sprite += '█'
} else {
sprite += ' '
if (this.interface.drawPixel(x, y, value)) {
this.registers[0xf] = 1
}
}
sprite += '\n'
}
console.log(sprite)
this._nextInstruction()
break
case 'SKP_VX':
// Skip next instruction if key with the value of Vx is pressed.
console.log('fixme 0')
if (0 === this.registers[args[0]]) {
// Ex9E - Skip next instruction if key with the value of Vx is pressed.
if (this.interface.getKeys() & (1 << this.registers[args[0]])) {
this._skipInstruction()

@@ -304,6 +355,6 @@ } else {

break
case 'SKNP_VX':
// Skip next instruction if key with the value of Vx is not pressed.
console.log('fixme 0')
if (0 !== this.registers[args[0]]) {
// ExA1 - Skip next instruction if key with the value of Vx is not pressed.
if (!(this.interface.getKeys() & (1 << this.registers[args[0]]))) {
this._skipInstruction()

@@ -314,30 +365,39 @@ } else {

break
case 'LD_VX_DT':
// Set Vx = delay timer value.
// Fx07 - Set Vx = delay timer value.
this.registers[args[0]] = this.DT
this._nextInstruction()
break
case 'LD_VX_K':
// Wait for a key press, store the value of the key in Vx.
console.log('fixme 0')
this.registers[args[0]] = 0
case 'LD_VX_N':
// Fx0A - Wait for a key press, store the value of the key in Vx.
this.registers[args[0]] = await this.interface.waitKey()
this._nextInstruction()
break
case 'LD_DT_VX':
// Set delay timer = Vx.
// Fx15 - Set delay timer = Vx.
this.DT = this.registers[args[1]]
this._nextInstruction()
break
case 'LD_ST_VX':
// Set sound timer = Vx.
// Fx18 - Set sound timer = Vx.
this.ST = this.registers[args[1]]
if (this.ST > 0) {
this.soundEnabled = true
this.interface.enableSound()
}
this._nextInstruction()
break
case 'ADD_I_VX':
// Set I = I + Vx.
// Fx1E - Set I = I + Vx.
this.I = this.I + this.registers[args[1]]
this._nextInstruction()
break
case 'LD_F_VX':
// Set I = location of sprite for digit Vx.
// Fx29 - Set I = location of sprite for digit Vx.
if (this.registers[args[1]] > 0xf) {

@@ -351,4 +411,7 @@ this.halted = true

break
case 'LD_B_VX':
// Store BCD representation of Vx in memory locations I, I+1, and I+2.
// Fx33 - Store BCD representation of Vx in memory locations I, I+1, and I+2.
// BCD means binary-coded decimal
// If VX is 0xef, or 239, we want 2, 3, and 9 in I, I+1, and I+2.
if (this.I > 4093) {

@@ -360,7 +423,7 @@ this.halted = true

let x = this.registers[args[1]]
const a = Math.floor(x / 100)
x = x - a * 100
const b = Math.floor(x / 10)
x = x - b * 10
const c = Math.floor(x)
const a = Math.floor(x / 100) // for 239, a is 2
x = x - a * 100 // subtract value of a * 100 from x (200)
const b = Math.floor(x / 10) // x is now 39, b is 3
x = x - b * 10 // subtract value of b * 10 from x (30)
const c = Math.floor(x) // x is now 9

@@ -373,4 +436,5 @@ this.memory[this.I] = a

break
case 'LD_I_VX':
// Store registers V0 through Vx in memory starting at location I.
// Fx55 - Store registers V0 through Vx in memory starting at location I.
if (this.I > 4095 - args[1]) {

@@ -384,6 +448,8 @@ this.halted = true

}
this._nextInstruction()
break
case 'LD_VX_I':
// Read registers V0 through Vx from memory starting at location I.
// Fx65 - Read registers V0 through Vx from memory starting at location I.
if (this.I > 4095 - args[0]) {

@@ -394,5 +460,6 @@ this.halted = true

for (let i = 0; i <= args[1]; i++) {
for (let i = 0; i <= args[0]; i++) {
this.registers[i] = this.memory[this.I + i]
}
this._nextInstruction()

@@ -399,0 +466,0 @@ break

@@ -1,12 +0,20 @@

const { INSTRUCTION_SET } = require('../constants/instructionSet')
const { INSTRUCTION_SET } = require('../data/instructionSet')
class Disassembler {
static disassemble(opcode) {
const instruction = INSTRUCTION_SET.filter(
const Disassembler = {
disassemble(opcode) {
// Find the instruction in which the opcode & mask equals the pattern
// For example, the opcode 0x1234 with the mask of 0xf000 applied would return 0x1000.
// This matches the JP_ADDR mask and pattern
const instruction = INSTRUCTION_SET.find(
instruction => (opcode & instruction.mask) === instruction.pattern
)[0]
)
// Each instruction may also have arguments. An argument will either be 4, 8, or 12 bits.
// In the case of SE_VX_NN, one argument's mask is 0x0f00 and shift is 8.
// An example opcode of 0x3abb with the mask of 0x0f00 will give us 0xa00, right shifted by 8 will give us 0xa.
const args = instruction.arguments.map(arg => (opcode & arg.mask) >> arg.shift)
// Return an object containing the instruction argument object and an array of arguments
return { instruction, args }
}
},

@@ -26,7 +34,9 @@ /**

*/
static format(decodedInstruction) {
format(decodedInstruction) {
// Print out formatted instructions from the disassembled instructions
const types = decodedInstruction.instruction.arguments.map(arg => arg.type)
const rawArgs = decodedInstruction.args
let formatted
let formattedInstruction
// Format the display of arguments based on type
if (rawArgs.length > 0) {

@@ -59,11 +69,11 @@ let args = []

})
formatted = decodedInstruction.instruction.name + ' ' + args.join(', ')
formattedInstruction = decodedInstruction.instruction.name + ' ' + args.join(', ')
} else {
formatted = decodedInstruction.instruction.name
formattedInstruction = decodedInstruction.instruction.name
}
return formatted
}
return formattedInstruction
},
static dump(data) {
dump(data) {
const lines = data.map((code, i) => {

@@ -78,7 +88,5 @@ const address = (i * 2).toString(16).padStart(6, '0')

return lines.join('\n')
}
},
}
module.exports = {
Disassembler,
}
module.exports = Disassembler

@@ -8,9 +8,29 @@ class CpuInterface {

draw() {
throw new TypeError('Draw must be implemented on the inherited class.')
clearDisplay() {
throw new TypeError('Must be implemented on the inherited class.')
}
clear() {
throw new TypeError('Clear must be implemented on the inherited class.')
renderDisplay() {
throw new TypeError('Must be implemented on the inherited class.')
}
waitKey() {
throw new TypeError('Must be implemented on the inherited class.')
}
getKeys() {
throw new TypeError('Must be implemented on the inherited class.')
}
drawPixel() {
throw new TypeError('Must be implemented on the inherited class.')
}
enableSound() {
throw new TypeError('Must be implemented on the inherited class.')
}
disableSound() {
throw new TypeError('Must be implemented on the inherited class.')
}
}

@@ -17,0 +37,0 @@

@@ -7,8 +7,6 @@ const fs = require('fs')

// Read the raw data buffer from the file
const buffer = fs.readFileSync(filename)
if (buffer.length % 2 !== 0) {
throw new Error('Invalid input')
}
// Create 16-bit big endian opcodes from the buffer
for (let i = 0; i < buffer.length; i += 2) {

@@ -15,0 +13,0 @@ this.data.push((buffer[i] << 8) | (buffer[i + 1] << 0))

const { RomBuffer } = require('./classes/RomBuffer')
const { CPU } = require('./classes/CPU')
const { TerminalCpuInterface } = require('./classes/TerminalCpuInterface')

@@ -7,2 +8,3 @@ module.exports = {

CPU,
TerminalCpuInterface,
}
{
"name": "chip8js",
"version": "0.1.6",
"version": "0.1.7",
"description": "A Chip-8 emulator written in JavaScript (Node.js).",

@@ -14,6 +14,6 @@ "author": {

"scripts": {
"start": "node index.js",
"play": "node scripts/play",
"hexdump": "node scripts/hexdump",
"example": "node scripts/run roms/CONNECT4",
"test": "jest"
"example": "node scripts/play roms/CONNECT4",
"test": "jest --verbose"
},

@@ -31,3 +31,6 @@ "devDependencies": {

"license": "MIT",
"private": false
"private": false,
"dependencies": {
"blessed": "^0.1.81"
}
}

@@ -14,3 +14,2 @@ # Chip8.js [![Build Status](https://travis-ci.org/taniarascia/chip8.svg?branch=master)](https://travis-ci.org/taniarascia/chip8) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

- [View hex dump](#view-hex-dump)
- [Reference](#reference)
- [Automated Testing](#automated-testing)

@@ -25,3 +24,3 @@ - [Instruction tests](#instruction-tests)

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.
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.

@@ -47,3 +46,3 @@ Here are some of the concepts I learned while writing this program:

> This guide assumes you already have [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/) installed.
> This guide assumes you already have [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/) installed.

@@ -54,3 +53,3 @@ You can add the module directly from the [chip8js](https://www.npmjs.com/package/chip8js) npm package.

yarn add chip8js
# npm i --save chip8js
# npm i chip8js
```

@@ -75,3 +74,3 @@

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.
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.

@@ -110,6 +109,2 @@ ### Load ROM

## Reference
In progress.
## Automated Testing

@@ -134,3 +129,3 @@

The [instruction tests](tests/instructions.test.js) cover the `INSTRUCTION_SET` found in `constants/instructionSet.js`. Each instruction has:
The [instruction tests](tests/instructions.test.js) cover the `INSTRUCTION_SET` found in `data/instructionSet.js`. Each instruction has:

@@ -148,3 +143,3 @@ - A `key`: for internal use

```js
// constants/instructionSet.js
// data/instructionSet.js

@@ -170,3 +165,3 @@ {

test('test instruction 06: 3xkk - SE Vx, byte', () => {
test('6: Expect disassembler to match opcode 3xnn to instruction SE_VX_NN', () => {
expect(Disassembler.disassemble(0x3abb).instruction).toHaveProperty('id', 'SE_VX_NN')

@@ -183,5 +178,5 @@ expect(Disassembler.disassemble(0x3abb).args).toHaveLength(2)

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](tests/cpu.test.js) test the state of the CPU after an executing an instruction.
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](tests/cpu.test.js) 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.
In the below example, the instruction is skipping an instruction if `Vx === nn`, otherwise it's going to the next instruction as usual.

@@ -192,3 +187,3 @@ ```js

case 'SE_VX_NN':
// Skip next instruction if Vx = kk.
// Skip next instruction if Vx = nn.
if (this.registers[args[0]] === args[1]) {

@@ -214,11 +209,13 @@ this._skipInstruction()

test('test cpu 06: 3xkk - SE Vx, byte', () => {
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] })
cpu.step()
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
cpu.step()
await cpu.step()

@@ -231,14 +228,8 @@ expect(cpu.PC).toBe(0x204)

- [x] Tests: Errors: 8
- [x] RET
- [x] CALL_ADDR
- [x] DRW_VX_VY_N
- [x] LD_F_VX
- [x] LD_B_VX
- [x] LD_I_VX
- [x] LD_VX_I
- [x] DW
- [ ] Create Interface Classes
- [ ] Fix speed and timers
- [ ] Web I/O
- [ ] Libui I/O
- [ ] Convert to TypeScript
- [ ] Write an assembler
## Acknowledgements

@@ -256,2 +247,2 @@

The code is open source and available under the [MIT License](LICENSE).
This project is open source and available under the [MIT License](LICENSE).

@@ -5,5 +5,5 @@ const { CPU } = require('../classes/CPU')

for (let i = 0; i < 76; i = i + 5) {
cpu.load({ data: [ 0xdab5 ] })
cpu.load({ data: [0xdab5] })
cpu.I = i
cpu.step()
}

@@ -1,37 +0,55 @@

describe('CPU tests', () => {
describe('CPU tests', async () => {
const { CPU } = require('../classes/CPU')
const cpu = new CPU(null)
const { MockCpuInterface } = require('../classes/interfaces/MockCpuInterface')
const cpuInterface = new MockCpuInterface()
const cpu = new CPU(cpuInterface)
test('CPU does not execute after halting', () => {
test('CPU should not execute after halting', async () => {
cpu.load({ data: 0x0000 })
cpu.halted = true
expect(() => {
cpu.step()
}).toThrowError(
'A problem has been detected and Chip-8 has been shut down to prevent damage to your computer.'
let error
try {
await cpu.step()
} catch (e) {
error = e
}
// BSOD on halted program
expect(error).toEqual(
new Error(
'A problem has been detected and Chip-8 has been shut down to prevent damage to your computer.'
)
)
})
// test.skip('test cpu 02: CLS', () => {})
// test.skip('2: CLS', () => {})
test('test cpu 03: RET', () => {
cpu.load({ data: [ 0x00ee ] })
cpu.SP = 2
cpu.stack[2] = 0xf
cpu.step()
test('3: RET (00ee) - Program counter should be set to stack pointer, then decrement stack pointer', async () => {
cpu.load({ data: [0x00ee] })
cpu.SP = 0x2
cpu.stack[0x2] = 0xf
await cpu.step()
expect(cpu.PC).toBe(0xf)
expect(cpu.SP).toBe(0x1)
})
cpu.load({ data: [ 0x00ee ] })
test('3: RET (00ee) - CPU should halt if stack pointer is set to 0', async () => {
cpu.load({ data: [0x00ee] })
expect(() => {
cpu.step()
}).toThrowError('Stack underflow.')
let error
try {
await cpu.step()
} catch (e) {
error = e
}
// BSOD on halted program
expect(error).toEqual(new Error('Stack underflow.'))
})
test('test cpu 04: 1nnn - JP addr', () => {
cpu.load({ data: [ 0x1333 ] })
cpu.step()
test('4: JP_ADDR (1nnn) - Program counter should be set to address in argument', async () => {
cpu.load({ data: [0x1333] })
await cpu.step()

@@ -41,6 +59,7 @@ expect(cpu.PC).toBe(0x333)

test('test cpu 05: 2nnn - CALL addr', () => {
cpu.load({ data: [ 0x2062 ] })
test('5: CALL_ADDR (2nnn) - Stack pointer should increment, program counter should be set to address in argument', async () => {
cpu.load({ data: [0x2062] })
// Set PC to retain original value
const PC = cpu.PC
cpu.step()
await cpu.step()

@@ -50,20 +69,30 @@ expect(cpu.SP).toBe(0)

expect(cpu.PC).toBe(0x062)
})
cpu.load({ data: [ 0x2062 ] })
test('5: CALL_ADDR (2nnn) - CPU should halt if stack pointer is set to 15', async () => {
cpu.load({ data: [0x2062] })
cpu.SP = 15
expect(() => {
cpu.step()
}).toThrowError('Stack overflow.')
let error
try {
await cpu.step()
} catch (e) {
error = e
}
// BSOD on halted program
expect(error).toEqual(new Error('Stack overflow.'))
})
test('test cpu 06: 3xkk - SE Vx, byte', () => {
cpu.load({ data: [ 0x3abb ] })
cpu.step()
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)
})
cpu.load({ data: [ 0x3abb ] })
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
cpu.step()
await cpu.step()

@@ -73,11 +102,13 @@ expect(cpu.PC).toBe(0x204)

test('test cpu 07: 4xkk - SNE Vx, byte', () => {
cpu.load({ data: [ 0x4acc ] })
cpu.step()
test('7: SNE_VX_NN (4xnn) - Program counter should increment by four bytes if register x is not equal to nn argument', async () => {
cpu.load({ data: [0x4acc] })
await cpu.step()
expect(cpu.PC).toBe(0x204)
})
cpu.load({ data: [ 0x4acc ] })
test('7: SNE_VX_NN (4xnn) - Program counter should increment by two bytes if register x is equal to nn argument', async () => {
cpu.load({ data: [0x4acc] })
cpu.registers[0xa] = 0xcc
cpu.step()
await cpu.step()

@@ -87,11 +118,16 @@ expect(cpu.PC).toBe(0x202)

test('test cpu 08: 5xy0 - SE Vx, Vy', () => {
cpu.load({ data: [ 0x5ab0 ] })
cpu.step()
test('8: SE_VX_VY (5xy0) - Program counter should increment by four if register x is equal to register y', async () => {
cpu.load({ data: [0x5ab0] })
cpu.registers[0xa] = 0x5
cpu.registers[0xb] = 0x5
await cpu.step()
expect(cpu.PC).toBe(0x204)
})
cpu.load({ data: [ 0x5ab0 ] })
test('8: SE_VX_VY (5xy0) - Program counter should increment by two if register x is not equal to register y', async () => {
cpu.load({ data: [0x5ab0] })
cpu.registers[0xa] = 0x5
cpu.step()
cpu.registers[0xa] = 0x6
await cpu.step()

@@ -101,6 +137,6 @@ expect(cpu.PC).toBe(0x202)

test('test cpu 09: 6xkk - LD Vx, byte', () => {
cpu.load({ data: [ 0x6abb ] })
test('9: LD_VX_NN (6xnn) - Register x should be set to the value of argument nn', async () => {
cpu.load({ data: [0x6abb] })
cpu.registers[0xa] = 0x10
cpu.step()
await cpu.step()

@@ -110,6 +146,6 @@ expect(cpu.registers[0xa]).toBe(0xbb)

test('test cpu 10: 7xkk - ADD Vx, byte', () => {
cpu.load({ data: [ 0x7abb ] })
test('10: ADD_VX_NN (7xnn) - Register x should be set to the value of register x plus argument nn', async () => {
cpu.load({ data: [0x7abb] })
cpu.registers[0xa] = 0x10
cpu.step()
await cpu.step()

@@ -119,6 +155,6 @@ expect(cpu.registers[0xa]).toBe(0x10 + 0xbb)

test('test cpu 11: 8xy0 - LD Vx, Vy', () => {
cpu.load({ data: [ 0x8ab0 ] })
test('11: LD_VX_VY (8xy0) - Register x should be set to the value of register y', async () => {
cpu.load({ data: [0x8ab0] })
cpu.registers[0xb] = 0x8
cpu.step()
await cpu.step()

@@ -128,42 +164,47 @@ expect(cpu.registers[0xa]).toBe(0x8)

test('test cpu 12: 8xy1 - OR Vx, Vy', () => {
cpu.load({ data: [ 0x8ab1 ] })
test('12: OR_VX_VY (8xy1) - Register x should be set to the value of register x OR register y', async () => {
cpu.load({ data: [0x8ab1] })
cpu.registers[0xa] = 0x3
cpu.registers[0xb] = 0x4
cpu.step()
await cpu.step()
expect(0x3 | 0x4).toBe(0x7)
expect(cpu.registers[0xa]).toBe(0x7)
})
test('test cpu 13: 8xy2 - AND Vx, Vy', () => {
cpu.load({ data: [ 0x8ab2 ] })
test('13: AND_VX_VY (8xy2) - Register x should be set to the value of register x AND register y', async () => {
cpu.load({ data: [0x8ab2] })
cpu.registers[0xa] = 0x3
cpu.registers[0xb] = 0x4
cpu.step()
await cpu.step()
expect(0x3 & 0x4).toBe(0)
expect(cpu.registers[0xa]).toBe(0)
})
test('test cpu 14: 8xy3 - XOR Vx, Vy', () => {
cpu.load({ data: [ 0x8ab3 ] })
test('14: XOR_VX_VY (8xy3) - Register x should be set to the value of register x XOR register y', async () => {
cpu.load({ data: [0x8ab3] })
cpu.registers[0xa] = 0x3
cpu.registers[0xb] = 0x3
cpu.step()
await cpu.step()
expect(0x3 ^ 0x3).toBe(0)
expect(cpu.registers[0xa]).toBe(0)
})
test('test cpu 15: 8xy4 - ADD Vx, Vy', () => {
cpu.load({ data: [ 0x8ab4 ] })
test('15: ADD_VX_VY (8xy4) - Register x should be set to the value of the sum of register x and register y (VF with no carry)', async () => {
cpu.load({ data: [0x8ab4] })
cpu.registers[0xa] = 0x3
cpu.registers[0xb] = 0x4
cpu.step()
await cpu.step()
expect(cpu.registers[0xa]).toBe(0x7)
expect(cpu.registers[0xf]).toBe(0)
})
cpu.load({ data: [ 0x8ab4 ] })
test('15: ADD_VX_VY (8xy4) - Register x should be set to the value of the sum of register x and register y (VF with carry)', async () => {
cpu.load({ data: [0x8ab4] })
cpu.registers[0xa] = 0xff
cpu.registers[0xb] = 0xff
cpu.step()
await cpu.step()

@@ -174,15 +215,17 @@ expect(cpu.registers[0xa]).toBe(0xfe)

test('test cpu 16: 8xy5 - SUB Vx, Vy', () => {
cpu.load({ data: [ 0x8ab5 ] })
test('16: SUB_VX_VY (8xy5) - Register x should be set to the difference of register x and register y (VF with carry)', async () => {
cpu.load({ data: [0x8ab5] })
cpu.registers[0xa] = 0x4
cpu.registers[0xb] = 0x2
cpu.step()
await cpu.step()
expect(cpu.registers[0xa]).toBe(2)
expect(cpu.registers[0xf]).toBe(1)
})
cpu.load({ data: [ 0x8ab5 ] })
test('16: SUB_VX_VY (8xy5) - Register x should be set to the difference of register x and register y (VF with no carry)', async () => {
cpu.load({ data: [0x8ab5] })
cpu.registers[0xa] = 0x2
cpu.registers[0xb] = 0x3
cpu.step()
await cpu.step()

@@ -193,6 +236,6 @@ expect(cpu.registers[0xa]).toBe(255)

test('test cpu 17: 8xy6 - SHR Vx {, Vy}', () => {
cpu.load({ data: [ 0x8ab6 ] })
test('17: SHR_VX_VY (8xy6) - Shift register x right 1 (AKA divide x by 2). Set VF to 1 if least significant bit of register x is 1', async () => {
cpu.load({ data: [0x8ab6] })
cpu.registers[0xa] = 0x3
cpu.step()
await cpu.step()

@@ -203,15 +246,17 @@ expect(cpu.registers[0xa]).toBe(0x3 >> 1)

test('test cpu 18: 8xy7 - SUBN Vx, Vy', () => {
cpu.load({ data: [ 0x8ab7 ] })
test('18: SUBN_VX_VY (8xy7) - Set register x to the difference of register y and register x (VF with no carry)', async () => {
cpu.load({ data: [0x8ab7] })
cpu.registers[0xa] = 0x3
cpu.registers[0xb] = 0x2
cpu.step()
await cpu.step()
expect(cpu.registers[0xa]).toBe(255)
expect(cpu.registers[0xf]).toBe(0)
})
cpu.load({ data: [ 0x8ab7 ] })
test('18: SUBN_VX_VY (8xy7) - Set register x to the difference of register y and register x (VF with carry)', async () => {
cpu.load({ data: [0x8ab7] })
cpu.registers[0xa] = 0x2
cpu.registers[0xb] = 0x3
cpu.step()
await cpu.step()

@@ -222,6 +267,6 @@ expect(cpu.registers[0xa]).toBe(1)

test('test cpu 19: 8xyE - SHL Vx, {, Vy}', () => {
cpu.load({ data: [ 0x8abe ] })
test('19: SHL_VX_VY (8xyE) - Shift register x left one (AKA multiply by 2). Set VF to 1 if least significant bit of register x is 1', async () => {
cpu.load({ data: [0x8abe] })
cpu.registers[0xa] = 0x3
cpu.step()
await cpu.step()

@@ -232,14 +277,16 @@ expect(cpu.registers[0xa]).toBe(0x3 << 1)

test('test cpu 20: 9xy0 - SNE Vx, Vy', () => {
cpu.load({ data: [ 0x9ab0 ] })
test('20: SNE_VX_VY (9xy0) - Program counter should increment by four bytes if register x is not equal to register y', async () => {
cpu.load({ data: [0x9ab0] })
cpu.registers[0xa] = 0x3
cpu.registers[0xb] = 0x4
cpu.step()
await cpu.step()
expect(cpu.PC).toBe(0x204)
})
cpu.load({ data: [ 0x9ab0 ] })
test('20: SNE_VX_VY (9xy0) - Program counter should increment by two bytes if register x is equal to register y', async () => {
cpu.load({ data: [0x9ab0] })
cpu.registers[0xa] = 0x3
cpu.registers[0xb] = 0x3
cpu.step()
await cpu.step()

@@ -249,5 +296,5 @@ expect(cpu.PC).toBe(0x202)

test('test cpu 21: Annn - LD I, addr', () => {
cpu.load({ data: [ 0xa999 ] })
cpu.step()
test('21: LD_I_ADDR (Annn) - I should be set to the value of argument nnn', async () => {
cpu.load({ data: [0xa999] })
await cpu.step()

@@ -257,6 +304,6 @@ expect(cpu.I).toBe(0x999)

test('test cpu 22: Bnnn - JP V0, addr', () => {
cpu.load({ data: [ 0xb300 ] })
test('22: JP_V0_ADDR (Bnnn) - Program counter should be set to the sum of V0 and argument nnn', async () => {
cpu.load({ data: [0xb300] })
cpu.registers[0] = 0x2
cpu.step()
await cpu.step()

@@ -266,25 +313,88 @@ expect(cpu.PC).toBe(0x2 + 0x300)

// test.skip('test cpu 23: Cxkk - RND Vx, byte', () => {})
// 23: RND_VX_NN (Cxnn) Can't seed/test random number
test('test cpu 24: Dxyn - DRW Vx, Vy, nibble', () => {
cpu.load({ data: [ 0xdab5 ] })
test('24: DRW_VX_VY_N (Dxyn) - CPU should halt if I + argument nn exceed 4095', async () => {
cpu.load({ data: [0xd005] })
cpu.I = 4091
expect(() => {
cpu.step()
}).toThrowError('Memory out of bounds.')
// todo: passing test
let error
try {
await cpu.step()
} catch (e) {
error = e
}
// BSOD on halted program
expect(error).toEqual(new Error('Memory out of bounds.'))
})
test('test cpu 25: Ex9E - SKP Vx', () => {
// todo
cpu.load({ data: [ 0xea9e ] })
cpu.registers[0xa] = 0
cpu.step()
test('24: DRW_VX_VY_N (Dxyn) - n byte sprite should be disiplayed at coordinates in register x, register y', async () => {
cpu.load({ data: [0xd125] })
cpu.registers[0x1] = 1
cpu.registers[0x2] = 1
await cpu.step()
cpu.interface.renderDisplay()
expect(cpuInterface.display[1][1]).toBe(1)
expect(cpuInterface.display[2][1]).toBe(1)
expect(cpuInterface.display[3][1]).toBe(1)
expect(cpuInterface.display[4][1]).toBe(1)
expect(cpuInterface.display[2][1]).toBe(1)
expect(cpuInterface.display[2][2]).toBe(0)
expect(cpuInterface.display[2][3]).toBe(0)
expect(cpuInterface.display[2][4]).toBe(1)
expect(cpuInterface.display[3][1]).toBe(1)
expect(cpuInterface.display[3][2]).toBe(0)
expect(cpuInterface.display[3][3]).toBe(0)
expect(cpuInterface.display[3][4]).toBe(1)
expect(cpuInterface.display[4][1]).toBe(1)
expect(cpuInterface.display[4][2]).toBe(0)
expect(cpuInterface.display[4][3]).toBe(0)
expect(cpuInterface.display[4][4]).toBe(1)
expect(cpuInterface.display[5][1]).toBe(1)
expect(cpuInterface.display[5][1]).toBe(1)
expect(cpuInterface.display[5][1]).toBe(1)
expect(cpuInterface.display[5][1]).toBe(1)
// No pixels were erased (no collision)
expect(cpu.registers[0xf]).toBe(0)
// This test relies on the previous one, to erase the previous values with collisions
cpu.load({ data: [0xd125] })
cpu.registers[0x1] = 1
cpu.registers[0x2] = 1
await cpu.step()
cpu.interface.renderDisplay()
expect(cpuInterface.display[1][1]).toBe(0)
expect(cpuInterface.display[2][1]).toBe(0)
expect(cpuInterface.display[3][1]).toBe(0)
expect(cpuInterface.display[4][1]).toBe(0)
expect(cpuInterface.display[2][1]).toBe(0)
expect(cpuInterface.display[2][4]).toBe(0)
expect(cpuInterface.display[3][1]).toBe(0)
expect(cpuInterface.display[3][4]).toBe(0)
expect(cpuInterface.display[4][1]).toBe(0)
expect(cpuInterface.display[4][4]).toBe(0)
expect(cpuInterface.display[5][1]).toBe(0)
expect(cpuInterface.display[5][1]).toBe(0)
expect(cpuInterface.display[5][1]).toBe(0)
expect(cpuInterface.display[5][1]).toBe(0)
// All pixels were erased (collision)
expect(cpu.registers[0xf]).toBe(1)
})
test('25: SKP_VX (Ex9E) - Program counter should increment by four bytes if key with value of register x is selected', async () => {
cpu.load({ data: [0xea9e] })
cpu.registers[0xa] = 4
// Mock CPU interface keys 0, 2, 3, 4 selected
await cpu.step()
expect(cpu.PC).toBe(0x204)
})
cpu.load({ data: [ 0xea9e ] })
test('25: SKP_VX (Ex9E) - Program counter should increment by two bytes if key with value of register x is not selected', async () => {
cpu.load({ data: [0xea9e] })
cpu.registers[0xa] = 1
cpu.step()
// Mock CPU interface does not have 1 selected
await cpu.step()

@@ -294,13 +404,14 @@ expect(cpu.PC).toBe(0x202)

test('test cpu 26: ExA1 - SKNP Vx', () => {
// todo
cpu.load({ data: [ 0xeba1 ] })
cpu.registers[0xb] = 0
cpu.step()
test('26: SKNP_VX (ExA1) - Program counter should increment by two bytes if value of register x is a selected key', async () => {
cpu.load({ data: [0xeba1] })
cpu.registers[0xb] = 4
await cpu.step()
expect(cpu.PC).toBe(0x202)
})
cpu.load({ data: [ 0xea9e ] })
test('26: SKNP_VX (ExA1) - Program counter should increment by four bytes if value of register x is not a selected key', async () => {
cpu.load({ data: [0xea9e] })
cpu.registers[0xb] = 1
cpu.step()
await cpu.step()

@@ -310,6 +421,6 @@ expect(cpu.PC).toBe(0x204)

test('test cpu 27: Fx07 - LD Vx, DT', () => {
cpu.load({ data: [ 0xfa07 ] })
test('27: LD_VX_DT (Fx07) - Register x should be set to the value of DT (delay timer)', async () => {
cpu.load({ data: [0xfa07] })
cpu.DT = 0xf
cpu.step()
await cpu.step()

@@ -319,14 +430,15 @@ expect(cpu.registers[0xa]).toBe(0xf)

test('test cpu 28: Fx0A - LD Vx, K', () => {
// todo
cpu.load({ data: [ 0xfb0a ] })
cpu.step()
test('28: LD_VX_N (Fx0A) - Register x should be set to the value of keypress', async () => {
// todo tick
cpu.load({ data: [0xfb0a] })
await cpu.step()
expect(cpu.registers[0xb]).toBe(0)
expect(cpu.registers[0xb]).toBe(5)
})
test('test cpu 29: Fx15 - LD DT, Vx', () => {
cpu.load({ data: [ 0xfb15 ] })
test('29: LD_DT_VX (Fx15) - Delay timer should be set to the value of register x', async () => {
// todo tick
cpu.load({ data: [0xfb15] })
cpu.registers[0xb] = 0xf
cpu.step()
await cpu.step()

@@ -336,6 +448,7 @@ expect(cpu.DT).toBe(0xf)

test('test cpu 30: Fx18 - LD ST, Vx', () => {
cpu.load({ data: [ 0xfa18 ] })
test('30: LD_ST_VX (Fx18) - Sound timer should be set to the value of register x', async () => {
// todo tick
cpu.load({ data: [0xfa18] })
cpu.registers[0xa] = 0xf
cpu.step()
await cpu.step()

@@ -345,7 +458,7 @@ expect(cpu.ST).toBe(0xf)

test('test cpu 31: Fx1E - ADD I, Vx', () => {
cpu.load({ data: [ 0xfa1e ] })
test('31: ADD_I_VX (Fx1E) - I should be set to the value of the sum of I and register x', async () => {
cpu.load({ data: [0xfa1e] })
cpu.I = 0xe
cpu.registers[0xa] = 0xf
cpu.step()
await cpu.step()

@@ -355,28 +468,46 @@ expect(cpu.I).toBe(0xe + 0xf)

test('test cpu 32: Fx29 - LD F, Vx', () => {
cpu.load({ data: [ 0xfa29 ] })
test('32: LD_F_VX (Fx29) - CPU should halt if register x is not equal to 0 through F', async () => {
cpu.load({ data: [0xfa29] })
cpu.registers[0xa] = 16
expect(() => {
cpu.step()
}).toThrowError('Invalid digit.')
let error
try {
await cpu.step()
} catch (e) {
error = e
}
cpu.load({ data: [ 0xfa29 ] })
// BSOD on halted program
expect(error).toEqual(new Error('Invalid digit.'))
})
test('32: LD_F_VX (Fx29) - I should be set to the location of the sprite for digit in register x', async () => {
cpu.load({ data: [0xfa29] })
cpu.registers[0xa] = 0xa
cpu.step()
await cpu.step()
expect(cpu.I).toBe(0xa * 5)
})
// todo print a to console for now
cpu.load({ data: [ 0xfa29, 0xdab5 ] })
cpu.registers[0xa] = 0xa
cpu.step()
cpu.step()
test('33: LD_B_VX (Fx33) - CPU should halt if I is greater than 4093', async () => {
cpu.load({ data: [0xfa33] })
cpu.registers[0xa] = 0x7b
cpu.I = 4094
let error
try {
await cpu.step()
} catch (e) {
error = e
}
// BSOD on halted program
expect(error).toEqual(new Error('Memory out of bounds.'))
})
test('test cpu 33: Fx33 - LD B, Vx', () => {
cpu.load({ data: [ 0xfa33 ] })
test('33: LD_B_VX (Fx33) - BCD representation of register x should be loaded into memory I, I+1, I+2 ', async () => {
cpu.load({ data: [0xfa33] })
cpu.registers[0xa] = 0x7b
cpu.I = 0x300
cpu.step()
await cpu.step()

@@ -386,14 +517,21 @@ expect(cpu.memory[0x300]).toBe(1)

expect(cpu.memory[0x302]).toBe(3)
})
cpu.load({ data: [ 0xfa33 ] })
cpu.registers[0xa] = 0x7b
cpu.I = 4094
test('34: LD_I_VX (Fx55) - CPU should halt if memory will exceed 4095', async () => {
cpu.load({ data: [0xfb55] })
cpu.I = 4085
expect(() => {
cpu.step()
}).toThrowError('Memory out of bounds.')
let error
try {
await cpu.step()
} catch (e) {
error = e
}
// BSOD on halted program
expect(error).toEqual(new Error('Memory out of bounds.'))
})
test('test cpu 34: Fx55 - LD [I], Vx', () => {
cpu.load({ data: [ 0xfb55 ] })
test('34: LD_I_VX (Fx55) - Memory should be set to register 0 through register x starting at location I', async () => {
cpu.load({ data: [0xfb55] })
cpu.I = 0x400

@@ -404,3 +542,3 @@

}
cpu.step()
await cpu.step()

@@ -410,20 +548,29 @@ for (let i = 0; i <= 0xb; i++) {

}
expect(cpu.memory[cpu.I + 0xc]).toBe(0)
})
cpu.load({ data: [ 0xfb55 ] })
cpu.I = 4085
test('35: LD_VX_I (Fx65) - CPU should halt if memory will exceed 4095', async () => {
cpu.load({ data: [0xfa65] })
cpu.I = 4086
expect(() => {
cpu.step()
}).toThrowError('Memory out of bounds.')
let error
try {
await cpu.step()
} catch (e) {
error = e
}
// BSOD on halted program
expect(error).toEqual(new Error('Memory out of bounds.'))
})
test('test cpu 35: Fx65 - LD Vx, [I]', () => {
cpu.load({ data: [ 0xfa65 ] })
test('35: LD_VX_I (Fx65) - Registers 0 through x should be set to the value of memory starting at location I', async () => {
cpu.load({ data: [0xfa65] })
cpu.I = 0x400
for (let i = 0; i <= 0xa; i++) {
cpu.registers[i] = i
cpu.memory[cpu.I + i] = i
}
cpu.step()
await cpu.step()

@@ -434,18 +581,17 @@ for (let i = 0; i <= 0xa; i++) {

expect(cpu.registers[0xb]).toBe(0)
})
cpu.load({ data: [ 0xfa65 ] })
cpu.I = 4086
test('36: DW (Data Word) - CPU should halt if instruction is a data word', async () => {
cpu.load({ data: [0x5154] })
expect(() => {
cpu.step()
}).toThrowError('Memory out of bounds.')
})
let error
try {
await cpu.step()
} catch (e) {
error = e
}
test('test data word', () => {
cpu.load({ data: [ 0x5154 ] })
expect(() => {
cpu.step()
}).toThrowError('Illegal instruction.')
// BSOD on halted program
expect(error).toEqual(new Error('Illegal instruction.'))
})
})

@@ -1,5 +0,5 @@

describe('Instructions tests', () => {
const { Disassembler } = require('../classes/Disassembler')
describe('Instruction set tests', () => {
const Disassembler = require('../classes/Disassembler')
test('test instruction 02: 00E0 - CLS', () => {
test('2: Expect disassembler to match opcode 00E0 to instruction CLS', () => {
expect(Disassembler.disassemble(0x00e0).instruction).toHaveProperty('id', 'CLS')

@@ -9,3 +9,3 @@ expect(Disassembler.disassemble(0x00e0).args).toHaveLength(0)

test('test instruction 03: 00EE - RET', () => {
test('3: Expect disassembler to match opcode 00EE to instruction RET', () => {
expect(Disassembler.disassemble(0x00ee).instruction).toHaveProperty('id', 'RET')

@@ -15,3 +15,3 @@ expect(Disassembler.disassemble(0x00ee).args).toHaveLength(0)

test('test instruction 04: 1nnn - JP addr', () => {
test('4: Expect disassembler to match opcode 1nnn to instruction JP_ADDR', () => {
expect(Disassembler.disassemble(0x1333).instruction).toHaveProperty('id', 'JP_ADDR')

@@ -22,3 +22,3 @@ expect(Disassembler.disassemble(0x1333).args).toHaveLength(1)

test('test instruction 05: 2nnn - CALL addr', () => {
test('5: Expect disassembler to match opcode 2nnn to instruction CALL_ADDR', () => {
expect(Disassembler.disassemble(0x2062).instruction).toHaveProperty('id', 'CALL_ADDR')

@@ -29,3 +29,3 @@ expect(Disassembler.disassemble(0x2062).args).toHaveLength(1)

test('test instruction 06: 3xkk - SE Vx, byte', () => {
test('6: Expect disassembler to match opcode 3xnn to instruction SE_VX_NN', () => {
expect(Disassembler.disassemble(0x3abb).instruction).toHaveProperty('id', 'SE_VX_NN')

@@ -37,3 +37,3 @@ expect(Disassembler.disassemble(0x3abb).args).toHaveLength(2)

test('test instruction 07: 4xkk - SNE Vx, byte', () => {
test('7: Expect disassembler to match opcode 4xnn to instruction SNE_VX_NN', () => {
expect(Disassembler.disassemble(0x4acc).instruction).toHaveProperty('id', 'SNE_VX_NN')

@@ -45,3 +45,3 @@ expect(Disassembler.disassemble(0x4acc).args).toHaveLength(2)

test('test instruction 08: 5xy0 - SE Vx, Vy', () => {
test('8: Expect disassembler to match opcode 5xy0 to instruction SE_VX_VY', () => {
expect(Disassembler.disassemble(0x5ab0).instruction).toHaveProperty('id', 'SE_VX_VY')

@@ -53,3 +53,3 @@ expect(Disassembler.disassemble(0x5ab0).args).toHaveLength(2)

test('test instruction 09: 6xkk - LD Vx, byte', () => {
test('9: Expect disassembler to match opcode 6xnn to instruction LD_VX_NN', () => {
expect(Disassembler.disassemble(0x6abb).instruction).toHaveProperty('id', 'LD_VX_NN')

@@ -61,3 +61,3 @@ expect(Disassembler.disassemble(0x6abb).args).toHaveLength(2)

test('test instruction 10: 7xkk - ADD Vx, byte', () => {
test('10: Expect disassembler to match opcode 7xnn to instruction ADD_VX_NN', () => {
expect(Disassembler.disassemble(0x7abb).instruction).toHaveProperty('id', 'ADD_VX_NN')

@@ -69,3 +69,3 @@ expect(Disassembler.disassemble(0x7abb).args).toHaveLength(2)

test('test instruction 11: 8xy0 - LD Vx, Vy', () => {
test('11: Expect disassembler to match opcode 8xy0 to instruction LD_VX_VY', () => {
expect(Disassembler.disassemble(0x8ab0).instruction).toHaveProperty('id', 'LD_VX_VY')

@@ -77,3 +77,3 @@ expect(Disassembler.disassemble(0x8ab0).args).toHaveLength(2)

test('test instruction 12: 8xy1 - OR Vx, Vy', () => {
test('12: Expect disassembler to match opcode 8xy1 to instruction OR_VX_VY', () => {
expect(Disassembler.disassemble(0x8ab1).instruction).toHaveProperty('id', 'OR_VX_VY')

@@ -85,3 +85,3 @@ expect(Disassembler.disassemble(0x8ab1).args).toHaveLength(2)

test('test instruction 13: 8xy2 - AND Vx, Vy', () => {
test('13: Expect disassembler to match opcode 8xy2 to instruction AND_VX_VY', () => {
expect(Disassembler.disassemble(0x8ab2).instruction).toHaveProperty('id', 'AND_VX_VY')

@@ -93,3 +93,3 @@ expect(Disassembler.disassemble(0x8ab2).args).toHaveLength(2)

test('test instruction 14: 8xy3 - XOR Vx, Vy', () => {
test('14: Expect disassembler to match opcode 8xy3 to instruction XOR_VX_VY', () => {
expect(Disassembler.disassemble(0x8ab3).instruction).toHaveProperty('id', 'XOR_VX_VY')

@@ -101,3 +101,3 @@ expect(Disassembler.disassemble(0x8ab3).args).toHaveLength(2)

test('test instruction 15: 8xy4 - ADD Vx, Vy', () => {
test('15: Expect disassembler to match opcode 8xy4 to instruction ADD_VX_VY', () => {
expect(Disassembler.disassemble(0x8ab4).instruction).toHaveProperty('id', 'ADD_VX_VY')

@@ -109,3 +109,3 @@ expect(Disassembler.disassemble(0x8ab4).args).toHaveLength(2)

test('test instruction 16: 8xy5 - SUB Vx, Vy', () => {
test('16: Expect disassembler to match opcode 8xy5 to instruction SUB_VX_VY', () => {
expect(Disassembler.disassemble(0x8ab5).instruction).toHaveProperty('id', 'SUB_VX_VY')

@@ -117,3 +117,3 @@ expect(Disassembler.disassemble(0x8ab5).args).toHaveLength(2)

test('test instruction 17: 8xy6 - SHR Vx {, Vy}', () => {
test('17: Expect disassembler to match opcode 8xy6 to instruction SHR_VX_VY', () => {
expect(Disassembler.disassemble(0x8ab6).instruction).toHaveProperty('id', 'SHR_VX_VY')

@@ -125,3 +125,3 @@ expect(Disassembler.disassemble(0x8ab6).args).toHaveLength(2)

test('test instruction 18: 8xy7 - SUBN Vx, Vy', () => {
test('18: Expect disassembler to match opcode 8xy7 to instruction SUBN_VX_VY', () => {
expect(Disassembler.disassemble(0x8ab7).instruction).toHaveProperty('id', 'SUBN_VX_VY')

@@ -133,3 +133,3 @@ expect(Disassembler.disassemble(0x8ab7).args).toHaveLength(2)

test('test instruction 19: 8xyE - SHL Vx, {, Vy}', () => {
test('19: Expect disassembler to match opcode 8xyE to instruction SHL_VX_VY', () => {
expect(Disassembler.disassemble(0x8abe).instruction).toHaveProperty('id', 'SHL_VX_VY')

@@ -141,3 +141,3 @@ expect(Disassembler.disassemble(0x8abe).args).toHaveLength(2)

test('test instruction 20: 9xy0 - SNE Vx, Vy', () => {
test('20: Expect disassembler to match opcode 9xy0 to instruction SNE_VX_VY', () => {
expect(Disassembler.disassemble(0x9ab0).instruction).toHaveProperty('id', 'SNE_VX_VY')

@@ -149,3 +149,3 @@ expect(Disassembler.disassemble(0x9ab0).args).toHaveLength(2)

test('test instruction 21: Annn - LD I, addr', () => {
test('21: Expect disassembler to match opcode Annn to instruction LD_I_ADDR', () => {
expect(Disassembler.disassemble(0xa999).instruction).toHaveProperty('id', 'LD_I_ADDR')

@@ -157,3 +157,3 @@ expect(Disassembler.disassemble(0xa999).args).toHaveLength(2)

test('test instruction 22: Bnnn - JP V0, addr', () => {
test('22: Expect disassembler to match opcode Bnnn to instruction JP_V0_ADDR', () => {
expect(Disassembler.disassemble(0xb400).instruction).toHaveProperty('id', 'JP_V0_ADDR')

@@ -165,3 +165,3 @@ expect(Disassembler.disassemble(0xb400).args).toHaveLength(2)

test('test instruction 23: Cxkk - RND Vx, byte', () => {
test('23: Expect disassembler to match opcode Cxnn to instruction RND_VX_NN', () => {
expect(Disassembler.disassemble(0xcabb).instruction).toHaveProperty('id', 'RND_VX_NN')

@@ -173,3 +173,3 @@ expect(Disassembler.disassemble(0xcabb).args).toHaveLength(2)

test('test instruction 24: Dxyn - DRW Vx, Vy, nibble', () => {
test('24: Expect disassembler to match opcode Dxyn to instruction DRW_VX_VY_N', () => {
expect(Disassembler.disassemble(0xdab9).instruction).toHaveProperty('id', 'DRW_VX_VY_N')

@@ -182,3 +182,3 @@ expect(Disassembler.disassemble(0xdab9).args).toHaveLength(3)

test('test instruction 25: Ex9E - SKP Vx', () => {
test('25: Expect disassembler to match opcode Ex9E to instruction SKP_VX', () => {
expect(Disassembler.disassemble(0xea9e).instruction).toHaveProperty('id', 'SKP_VX')

@@ -189,3 +189,3 @@ expect(Disassembler.disassemble(0xea9e).args).toHaveLength(1)

test('test instruction 26: ExA1 - SKNP Vx', () => {
test('26: Expect disassembler to match opcode ExA1 to instruction SKNP_VX', () => {
expect(Disassembler.disassemble(0xeba1).instruction).toHaveProperty('id', 'SKNP_VX')

@@ -196,3 +196,3 @@ expect(Disassembler.disassemble(0xeba1).args).toHaveLength(1)

test('test instruction 27: Fx07 - LD Vx, DT', () => {
test('27: Expect disassembler to match opcode Fx07 to instruction LD_VX_DT', () => {
expect(Disassembler.disassemble(0xfa07).instruction).toHaveProperty('id', 'LD_VX_DT')

@@ -204,4 +204,4 @@ expect(Disassembler.disassemble(0xfa07).args).toHaveLength(2)

test('test instruction 28: Fx0A - LD Vx, K', () => {
expect(Disassembler.disassemble(0xfb0a).instruction).toHaveProperty('id', 'LD_VX_K')
test('28: Expect disassembler to match opcode Fx0A to instruction LD_VX_N', () => {
expect(Disassembler.disassemble(0xfb0a).instruction).toHaveProperty('id', 'LD_VX_N')
expect(Disassembler.disassemble(0xfb0a).args).toHaveLength(2)

@@ -212,3 +212,3 @@ expect(Disassembler.disassemble(0xfb0a).args[0]).toBe(0xb)

test('test instruction 29: Fx15 - LD DT, Vx', () => {
test('29: Expect disassembler to match opcode Fx15 to instruction LD_DT_VX', () => {
expect(Disassembler.disassemble(0xfb15).instruction).toHaveProperty('id', 'LD_DT_VX')

@@ -220,3 +220,3 @@ expect(Disassembler.disassemble(0xfb15).args).toHaveLength(2)

test('test instruction 30: Fx18 - LD ST, Vx', () => {
test('30: Expect disassembler to match opcode Fx18 to instruction LD_ST_VX', () => {
expect(Disassembler.disassemble(0xfa18).instruction).toHaveProperty('id', 'LD_ST_VX')

@@ -228,3 +228,3 @@ expect(Disassembler.disassemble(0xfa18).args).toHaveLength(2)

test('test instruction 31: Fx1E - ADD I, Vx', () => {
test('31: Expect disassembler to match opcode Fx1E to instruction ADD_I_VX', () => {
expect(Disassembler.disassemble(0xfa1e).instruction).toHaveProperty('id', 'ADD_I_VX')

@@ -236,3 +236,3 @@ expect(Disassembler.disassemble(0xfa1e).args).toHaveLength(2)

test('test instruction 32: Fx29 - LD F, Vx', () => {
test('32: Expect disassembler to match opcode Fx29 to instruction LD_F_VX', () => {
expect(Disassembler.disassemble(0xfa29).instruction).toHaveProperty('id', 'LD_F_VX')

@@ -244,3 +244,3 @@ expect(Disassembler.disassemble(0xfa29).args).toHaveLength(2)

test('test instruction 33: Fx33 - LD B, Vx', () => {
test('33: Expect disassembler to match opcode Fx33 to instruction LD_B_VX', () => {
expect(Disassembler.disassemble(0xfa33).instruction).toHaveProperty('id', 'LD_B_VX')

@@ -252,3 +252,3 @@ expect(Disassembler.disassemble(0xfa33).args).toHaveLength(2)

test('test instruction 34: Fx55 - LD [I], Vx', () => {
test('34: Expect disassembler to match opcode Fx55 to instruction LD_I_VX', () => {
expect(Disassembler.disassemble(0xfa55).instruction).toHaveProperty('id', 'LD_I_VX')

@@ -260,3 +260,3 @@ expect(Disassembler.disassemble(0xfa55).args).toHaveLength(2)

test('test instruction 35: Fx65 - LD Vx, [I]', () => {
test('35: Expect disassembler to match opcode Fx65 to instruction LD_VX_I', () => {
expect(Disassembler.disassemble(0xfa65).instruction).toHaveProperty('id', 'LD_VX_I')

@@ -268,3 +268,3 @@ expect(Disassembler.disassemble(0xfa65).args).toHaveLength(2)

test('test data word', () => {
test('36: Expect all other opcodes to match DW (Data Word)', () => {
expect(Disassembler.disassemble(0x5154).instruction).toHaveProperty('id', 'DW')

@@ -271,0 +271,0 @@ expect(Disassembler.disassemble(0x5154).args).toHaveLength(1)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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