Comparing version 0.1.5 to 0.1.6
@@ -5,2 +5,5 @@ const { Disassembler } = require('./Disassembler') | ||
class CPU { | ||
constructor(cpuInterface) { | ||
this.interface = cpuInterface | ||
} | ||
/** | ||
@@ -32,4 +35,2 @@ * Set or reset the state to initial values. | ||
this.reset() | ||
const romData = romBuffer.data | ||
let memoryStart = 0x200 | ||
@@ -41,2 +42,5 @@ // 0-80 in memory is reserved for font set | ||
const romData = romBuffer.data | ||
let memoryStart = 0x200 | ||
// Place ROM data in memory starting at 0x200 | ||
@@ -49,9 +53,22 @@ for (let i = 0; i < romData.length; i++) { | ||
run() { | ||
while (true) { | ||
this.step() | ||
tick() { | ||
if (this.DT > 0) { | ||
this.DT-- | ||
} else { | ||
console.log('fixme') | ||
} | ||
if (this.ST > 0) { | ||
this.ST-- | ||
} else { | ||
console.log('fixme') | ||
} | ||
} | ||
step() { | ||
if (this.halted) { | ||
throw new Error( | ||
'A problem has been detected and Chip-8 has been shut down to prevent damage to your computer.' | ||
) | ||
} | ||
const opcode = this._fetch() | ||
@@ -80,2 +97,7 @@ const instruction = this._decode(opcode) | ||
_fetch() { | ||
if (this.PC > 4094) { | ||
this.halted = true | ||
throw new Error('Memory out of bounds.') | ||
} | ||
return (this.memory[this.PC] << 8) | (this.memory[this.PC + 1] << 0) | ||
@@ -100,2 +122,7 @@ } | ||
// Return from a subroutine. | ||
if (this.SP === -1) { | ||
this.halted = true | ||
throw new Error('Stack underflow.') | ||
} | ||
this.PC = this.stack[this.SP] | ||
@@ -110,2 +137,7 @@ this.SP-- | ||
// Call subroutine at nnn. | ||
if (this.SP === 15) { | ||
this.halted = true | ||
throw new Error('Stack overflow.') | ||
} | ||
this.SP++ | ||
@@ -229,2 +261,7 @@ this.stack[this.SP] = this.PC + 2 | ||
// The interpreter reads n bytes from memory, starting at the address stored in I. | ||
if (this.I > 4095 - args[2]) { | ||
this.halted = true | ||
throw new Error('Memory out of bounds.') | ||
} | ||
let sprite = '' | ||
@@ -295,2 +332,7 @@ | ||
// Set I = location of sprite for digit Vx. | ||
if (this.registers[args[1]] > 0xf) { | ||
this.halted = true | ||
throw new Error('Invalid digit.') | ||
} | ||
this.I = this.registers[args[1]] * 5 | ||
@@ -301,2 +343,7 @@ this._nextInstruction() | ||
// Store BCD representation of Vx in memory locations I, I+1, and I+2. | ||
if (this.I > 4093) { | ||
this.halted = true | ||
throw new Error('Memory out of bounds.') | ||
} | ||
let x = this.registers[args[1]] | ||
@@ -317,2 +364,7 @@ const a = Math.floor(x / 100) | ||
// Store registers V0 through Vx in memory starting at location I. | ||
if (this.I > 4095 - args[1]) { | ||
this.halted = true | ||
throw new Error('Memory out of bounds.') | ||
} | ||
for (let i = 0; i <= args[1]; i++) { | ||
@@ -325,2 +377,7 @@ this.memory[this.I + i] = this.registers[i] | ||
// Read registers V0 through Vx from memory starting at location I. | ||
if (this.I > 4095 - args[0]) { | ||
this.halted = true | ||
throw new Error('Memory out of bounds.') | ||
} | ||
for (let i = 0; i <= args[1]; i++) { | ||
@@ -333,3 +390,4 @@ this.registers[i] = this.memory[this.I + i] | ||
// Data word | ||
console.log(args[0]) | ||
this.halted = true | ||
throw new Error('Illegal instruction.') | ||
} | ||
@@ -336,0 +394,0 @@ } |
{ | ||
"name": "chip8js", | ||
"version": "0.1.5", | ||
"version": "0.1.6", | ||
"description": "A Chip-8 emulator written in JavaScript (Node.js).", | ||
@@ -16,3 +16,3 @@ "author": { | ||
"hexdump": "node scripts/hexdump", | ||
"example": "node index.js roms/CONNECT4", | ||
"example": "node scripts/run roms/CONNECT4", | ||
"test": "jest" | ||
@@ -31,3 +31,3 @@ }, | ||
"license": "MIT", | ||
"public": true | ||
"private": false | ||
} |
@@ -99,2 +99,8 @@ # 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) | ||
### Run example ROM | ||
``` | ||
yarn example | ||
``` | ||
## Reference | ||
@@ -214,5 +220,14 @@ | ||
- [ ] Check for halts, throw errors and reset | ||
- [ ] Begin I/O | ||
- [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 | ||
## Acknowledgements | ||
@@ -226,3 +241,3 @@ | ||
I'm [Tania Rascia](https://www.taniarascia.com). I write articles and tutorials about programming. | ||
- [Tania Rascia](https://www.taniarascia.com) | ||
@@ -229,0 +244,0 @@ ## License |
const filename = process.argv.slice(2)[0] | ||
const { RomBuffer } = require('../classes/RomBuffer') | ||
const romBuffer = new RomBuffer(filename) | ||
const romBuffer = new RomBuffer(filename) | ||
console.log(romBuffer.dump()) |
describe('CPU tests', () => { | ||
const { CPU } = require('../classes/CPU') | ||
const cpu = new CPU() | ||
const cpu = new CPU(null) | ||
test.skip('test cpu 02: CLS', () => {}) | ||
test('CPU does not execute after halting', () => { | ||
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.' | ||
) | ||
}) | ||
// test.skip('test cpu 02: CLS', () => {}) | ||
test('test cpu 03: RET', () => { | ||
cpu.load({ data: [0x00ee] }) | ||
cpu.load({ data: [ 0x00ee ] }) | ||
cpu.SP = 2 | ||
@@ -15,6 +26,12 @@ cpu.stack[2] = 0xf | ||
expect(cpu.SP).toBe(0x1) | ||
cpu.load({ data: [ 0x00ee ] }) | ||
expect(() => { | ||
cpu.step() | ||
}).toThrowError('Stack underflow.') | ||
}) | ||
test('test cpu 04: 1nnn - JP addr', () => { | ||
cpu.load({ data: [0x1333] }) | ||
cpu.load({ data: [ 0x1333 ] }) | ||
cpu.step() | ||
@@ -26,3 +43,3 @@ | ||
test('test cpu 05: 2nnn - CALL addr', () => { | ||
cpu.load({ data: [0x2062] }) | ||
cpu.load({ data: [ 0x2062 ] }) | ||
const PC = cpu.PC | ||
@@ -34,6 +51,13 @@ cpu.step() | ||
expect(cpu.PC).toBe(0x062) | ||
cpu.load({ data: [ 0x2062 ] }) | ||
cpu.SP = 15 | ||
expect(() => { | ||
cpu.step() | ||
}).toThrowError('Stack overflow.') | ||
}) | ||
test('test cpu 06: 3xkk - SE Vx, byte', () => { | ||
cpu.load({ data: [0x3abb] }) | ||
cpu.load({ data: [ 0x3abb ] }) | ||
cpu.step() | ||
@@ -43,3 +67,3 @@ | ||
cpu.load({ data: [0x3abb] }) | ||
cpu.load({ data: [ 0x3abb ] }) | ||
cpu.registers[0xa] = 0xbb | ||
@@ -52,3 +76,3 @@ cpu.step() | ||
test('test cpu 07: 4xkk - SNE Vx, byte', () => { | ||
cpu.load({ data: [0x4acc] }) | ||
cpu.load({ data: [ 0x4acc ] }) | ||
cpu.step() | ||
@@ -58,3 +82,3 @@ | ||
cpu.load({ data: [0x4acc] }) | ||
cpu.load({ data: [ 0x4acc ] }) | ||
cpu.registers[0xa] = 0xcc | ||
@@ -67,3 +91,3 @@ cpu.step() | ||
test('test cpu 08: 5xy0 - SE Vx, Vy', () => { | ||
cpu.load({ data: [0x5ab0] }) | ||
cpu.load({ data: [ 0x5ab0 ] }) | ||
cpu.step() | ||
@@ -73,3 +97,3 @@ | ||
cpu.load({ data: [0x5ab0] }) | ||
cpu.load({ data: [ 0x5ab0 ] }) | ||
cpu.registers[0xa] = 0x5 | ||
@@ -82,3 +106,3 @@ cpu.step() | ||
test('test cpu 09: 6xkk - LD Vx, byte', () => { | ||
cpu.load({ data: [0x6abb] }) | ||
cpu.load({ data: [ 0x6abb ] }) | ||
cpu.registers[0xa] = 0x10 | ||
@@ -91,3 +115,3 @@ cpu.step() | ||
test('test cpu 10: 7xkk - ADD Vx, byte', () => { | ||
cpu.load({ data: [0x7abb] }) | ||
cpu.load({ data: [ 0x7abb ] }) | ||
cpu.registers[0xa] = 0x10 | ||
@@ -100,3 +124,3 @@ cpu.step() | ||
test('test cpu 11: 8xy0 - LD Vx, Vy', () => { | ||
cpu.load({ data: [0x8ab0] }) | ||
cpu.load({ data: [ 0x8ab0 ] }) | ||
cpu.registers[0xb] = 0x8 | ||
@@ -109,3 +133,3 @@ cpu.step() | ||
test('test cpu 12: 8xy1 - OR Vx, Vy', () => { | ||
cpu.load({ data: [0x8ab1] }) | ||
cpu.load({ data: [ 0x8ab1 ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -119,3 +143,3 @@ cpu.registers[0xb] = 0x4 | ||
test('test cpu 13: 8xy2 - AND Vx, Vy', () => { | ||
cpu.load({ data: [0x8ab2] }) | ||
cpu.load({ data: [ 0x8ab2 ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -129,3 +153,3 @@ cpu.registers[0xb] = 0x4 | ||
test('test cpu 14: 8xy3 - XOR Vx, Vy', () => { | ||
cpu.load({ data: [0x8ab3] }) | ||
cpu.load({ data: [ 0x8ab3 ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -139,3 +163,3 @@ cpu.registers[0xb] = 0x3 | ||
test('test cpu 15: 8xy4 - ADD Vx, Vy', () => { | ||
cpu.load({ data: [0x8ab4] }) | ||
cpu.load({ data: [ 0x8ab4 ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -148,3 +172,3 @@ cpu.registers[0xb] = 0x4 | ||
cpu.load({ data: [0x8ab4] }) | ||
cpu.load({ data: [ 0x8ab4 ] }) | ||
cpu.registers[0xa] = 0xff | ||
@@ -159,3 +183,3 @@ cpu.registers[0xb] = 0xff | ||
test('test cpu 16: 8xy5 - SUB Vx, Vy', () => { | ||
cpu.load({ data: [0x8ab5] }) | ||
cpu.load({ data: [ 0x8ab5 ] }) | ||
cpu.registers[0xa] = 0x4 | ||
@@ -168,3 +192,3 @@ cpu.registers[0xb] = 0x2 | ||
cpu.load({ data: [0x8ab5] }) | ||
cpu.load({ data: [ 0x8ab5 ] }) | ||
cpu.registers[0xa] = 0x2 | ||
@@ -179,3 +203,3 @@ cpu.registers[0xb] = 0x3 | ||
test('test cpu 17: 8xy6 - SHR Vx {, Vy}', () => { | ||
cpu.load({ data: [0x8ab6] }) | ||
cpu.load({ data: [ 0x8ab6 ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -189,3 +213,3 @@ cpu.step() | ||
test('test cpu 18: 8xy7 - SUBN Vx, Vy', () => { | ||
cpu.load({ data: [0x8ab7] }) | ||
cpu.load({ data: [ 0x8ab7 ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -198,3 +222,3 @@ cpu.registers[0xb] = 0x2 | ||
cpu.load({ data: [0x8ab7] }) | ||
cpu.load({ data: [ 0x8ab7 ] }) | ||
cpu.registers[0xa] = 0x2 | ||
@@ -209,3 +233,3 @@ cpu.registers[0xb] = 0x3 | ||
test('test cpu 19: 8xyE - SHL Vx, {, Vy}', () => { | ||
cpu.load({ data: [0x8abe] }) | ||
cpu.load({ data: [ 0x8abe ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -219,3 +243,3 @@ cpu.step() | ||
test('test cpu 20: 9xy0 - SNE Vx, Vy', () => { | ||
cpu.load({ data: [0x9ab0] }) | ||
cpu.load({ data: [ 0x9ab0 ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -227,3 +251,3 @@ cpu.registers[0xb] = 0x4 | ||
cpu.load({ data: [0x9ab0] }) | ||
cpu.load({ data: [ 0x9ab0 ] }) | ||
cpu.registers[0xa] = 0x3 | ||
@@ -237,3 +261,3 @@ cpu.registers[0xb] = 0x3 | ||
test('test cpu 21: Annn - LD I, addr', () => { | ||
cpu.load({ data: [0xa999] }) | ||
cpu.load({ data: [ 0xa999 ] }) | ||
cpu.step() | ||
@@ -245,3 +269,3 @@ | ||
test('test cpu 22: Bnnn - JP V0, addr', () => { | ||
cpu.load({ data: [0xb300] }) | ||
cpu.load({ data: [ 0xb300 ] }) | ||
cpu.registers[0] = 0x2 | ||
@@ -253,29 +277,46 @@ cpu.step() | ||
test.skip('test cpu 23: Cxkk - RND Vx, byte', () => { | ||
// untestable | ||
// test.skip('test cpu 23: Cxkk - RND Vx, byte', () => {}) | ||
test('test cpu 24: Dxyn - DRW Vx, Vy, nibble', () => { | ||
cpu.load({ data: [ 0xdab5 ] }) | ||
cpu.I = 4091 | ||
expect(() => { | ||
cpu.step() | ||
}).toThrowError('Memory out of bounds.') | ||
// todo: passing test | ||
}) | ||
test.skip('test cpu 24: Dxyn - DRW Vx, Vy, nibble', () => { | ||
cpu.load({ data: [0xdab5] }) | ||
test('test cpu 25: Ex9E - SKP Vx', () => { | ||
// todo | ||
cpu.load({ data: [ 0xea9e ] }) | ||
cpu.registers[0xa] = 0 | ||
cpu.step() | ||
// cpu.registers[0xa] = 0 | ||
// cpu.registers[0xb] = 0 | ||
// cpu.I = 0x300 | ||
// cpu.memory[0x300] = 0b11110000 | ||
// cpu.memory[0x301] = 0b11100000 | ||
// cpu.step() | ||
// expect(cpu.memory[0x300]).toBe(0) | ||
// I'll get back to this one | ||
expect(cpu.PC).toBe(0x204) | ||
cpu.load({ data: [ 0xea9e ] }) | ||
cpu.registers[0xa] = 1 | ||
cpu.step() | ||
expect(cpu.PC).toBe(0x202) | ||
}) | ||
test.skip('test cpu 25: Ex9E - SKP Vx', () => { | ||
test('test cpu 26: ExA1 - SKNP Vx', () => { | ||
// todo | ||
}) | ||
cpu.load({ data: [ 0xeba1 ] }) | ||
cpu.registers[0xb] = 0 | ||
cpu.step() | ||
test.skip('test cpu 26: ExA1 - SKNP Vx', () => { | ||
// todo | ||
expect(cpu.PC).toBe(0x202) | ||
cpu.load({ data: [ 0xea9e ] }) | ||
cpu.registers[0xb] = 1 | ||
cpu.step() | ||
expect(cpu.PC).toBe(0x204) | ||
}) | ||
test('test cpu 27: Fx07 - LD Vx, DT', () => { | ||
cpu.load({ data: [0xfa07] }) | ||
cpu.load({ data: [ 0xfa07 ] }) | ||
cpu.DT = 0xf | ||
@@ -287,8 +328,12 @@ cpu.step() | ||
test.skip('test cpu 28: Fx0A - LD Vx, K', () => { | ||
test('test cpu 28: Fx0A - LD Vx, K', () => { | ||
// todo | ||
cpu.load({ data: [ 0xfb0a ] }) | ||
cpu.step() | ||
expect(cpu.registers[0xb]).toBe(0) | ||
}) | ||
test('test cpu 29: Fx15 - LD DT, Vx', () => { | ||
cpu.load({ data: [0xfb15] }) | ||
cpu.load({ data: [ 0xfb15 ] }) | ||
cpu.registers[0xb] = 0xf | ||
@@ -301,3 +346,3 @@ cpu.step() | ||
test('test cpu 30: Fx18 - LD ST, Vx', () => { | ||
cpu.load({ data: [0xfa18] }) | ||
cpu.load({ data: [ 0xfa18 ] }) | ||
cpu.registers[0xa] = 0xf | ||
@@ -310,3 +355,3 @@ cpu.step() | ||
test('test cpu 31: Fx1E - ADD I, Vx', () => { | ||
cpu.load({ data: [0xfa1e] }) | ||
cpu.load({ data: [ 0xfa1e ] }) | ||
cpu.I = 0xe | ||
@@ -319,12 +364,25 @@ cpu.registers[0xa] = 0xf | ||
test.skip('test cpu 32: Fx29 - LD F, Vx', () => { | ||
cpu.load({ data: [0xfa29] }) | ||
test('test cpu 32: Fx29 - LD F, Vx', () => { | ||
cpu.load({ data: [ 0xfa29 ] }) | ||
cpu.registers[0xa] = 16 | ||
expect(() => { | ||
cpu.step() | ||
}).toThrowError('Invalid digit.') | ||
cpu.load({ data: [ 0xfa29 ] }) | ||
cpu.registers[0xa] = 0xa | ||
cpu.step() | ||
expect(cpu.I).toBe(0xa * 5) | ||
expect(this.memory[0xa * 50]).toBe(0xf0) | ||
// todo print a to console for now | ||
cpu.load({ data: [ 0xfa29, 0xdab5 ] }) | ||
cpu.registers[0xa] = 0xa | ||
cpu.step() | ||
cpu.step() | ||
}) | ||
test('test cpu 33: Fx33 - LD B, Vx', () => { | ||
cpu.load({ data: [0xfa33] }) | ||
cpu.load({ data: [ 0xfa33 ] }) | ||
cpu.registers[0xa] = 0x7b | ||
@@ -337,6 +395,14 @@ cpu.I = 0x300 | ||
expect(cpu.memory[0x302]).toBe(3) | ||
cpu.load({ data: [ 0xfa33 ] }) | ||
cpu.registers[0xa] = 0x7b | ||
cpu.I = 4094 | ||
expect(() => { | ||
cpu.step() | ||
}).toThrowError('Memory out of bounds.') | ||
}) | ||
test('test cpu 34: Fx55 - LD [I], Vx', () => { | ||
cpu.load({ data: [0xfb55] }) | ||
cpu.load({ data: [ 0xfb55 ] }) | ||
cpu.I = 0x400 | ||
@@ -353,6 +419,13 @@ | ||
expect(cpu.memory[cpu.I + 0xc]).toBe(0) | ||
cpu.load({ data: [ 0xfb55 ] }) | ||
cpu.I = 4085 | ||
expect(() => { | ||
cpu.step() | ||
}).toThrowError('Memory out of bounds.') | ||
}) | ||
test('test cpu 35: Fx65 - LD Vx, [I]', () => { | ||
cpu.load({ data: [0xfa65] }) | ||
cpu.load({ data: [ 0xfa65 ] }) | ||
cpu.I = 0x400 | ||
@@ -369,8 +442,18 @@ | ||
expect(cpu.registers[0xb]).toBe(0) | ||
cpu.load({ data: [ 0xfa65 ] }) | ||
cpu.I = 4086 | ||
expect(() => { | ||
cpu.step() | ||
}).toThrowError('Memory out of bounds.') | ||
}) | ||
test.skip('test data word', () => { | ||
cpu.load({ data: [0x5154] }) | ||
// todo | ||
test('test data word', () => { | ||
cpu.load({ data: [ 0x5154 ] }) | ||
expect(() => { | ||
cpu.step() | ||
}).toThrowError('Illegal instruction.') | ||
}) | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
196018
21
1431
243