Increase test coverage, fix docs

This commit is contained in:
Will Scullin 2021-10-12 20:05:28 -07:00
parent 547f6df1cd
commit cbfce46dee
No known key found for this signature in database
GPG Key ID: 26DCD1042C6638CD
2 changed files with 158 additions and 17 deletions

View File

@ -161,27 +161,44 @@ type StrictInstruction =
type Instructions = Record<byte, StrictInstruction>
type callback = (cpu: CPU6502) => void;
type callback = (cpu: CPU6502) => boolean | void;
export default class CPU6502 {
/** 65C02 emulation mode flag */
private readonly is65C02: boolean;
/* Registers */
private pc: word = 0; //
private sr: byte = flags.X // Process Status Register
private ar: byte = 0; // Accumulator
private xr: byte = 0; // X Register
private yr: byte = 0; // Y Register
private sp: byte = 0xff; // Stack Pointer
/**
* Registers
*/
/** Program counter */
private pc: word = 0;
/** Status register */
private sr: byte = flags.X
/** Accumulator */
private ar: byte = 0;
/** X index */
private xr: byte = 0;
/** Y index */
private yr: byte = 0;
/** Stack pointer */
private sp: byte = 0xff;
/** Current instruction */
private op: Instruction
private addr: word = 0; // address bus
/** Last accessed memory address */
private addr: word = 0;
/** Filled array of memory handlers by address page */
private memPages: Memory[] = new Array(0x100);
/** Callbacks invoked on reset signal */
private resetHandlers: ResettablePageHandler[] = [];
/** Elapsed cycles */
private cycles = 0;
/** Command being fetched signal */
private sync = false;
/** Filled array of CPU operations */
private readonly opary: Instruction[];
constructor(options: CpuOptions = {}) {
@ -391,7 +408,7 @@ export default class CPU6502 {
}
}
private writePageCross(pc: word, addr: word, addrIdx: word): void {
private workCycleIndexedWrite(pc: word, addr: word, addrIdx: word): void {
const oldPage = addr & 0xff00;
if (this.is65C02) {
this.readByte(pc);
@ -401,7 +418,7 @@ export default class CPU6502 {
}
}
private readPageCross(pc: word, addr: word, addrIdx: word): void {
private workCycleIndexedRead(pc: word, addr: word, addrIdx: word): void {
const oldPage = addr & 0xff00;
const newPage = addrIdx & 0xff00;
if (newPage !== oldPage) {
@ -446,7 +463,7 @@ export default class CPU6502 {
const addr = this.readWordPC();
const pc = this.addr;
const addrIdx = (addr + this.xr) & 0xffff;
this.readPageCross(pc, addr, addrIdx);
this.workCycleIndexedRead(pc, addr, addrIdx);
return this.readByte(addrIdx);
}
@ -455,7 +472,7 @@ export default class CPU6502 {
const addr = this.readWordPC();
const pc = this.addr;
const addrIdx = (addr + this.yr) & 0xffff;
this.readPageCross(pc, addr, addrIdx);
this.workCycleIndexedRead(pc, addr, addrIdx);
return this.readByte(addrIdx);
}
@ -487,7 +504,7 @@ export default class CPU6502 {
const pc = this.addr;
const addr = this.readZPWord(zpAddr);
const addrIdx = (addr + this.yr) & 0xffff;
this.readPageCross(pc, addr, addrIdx);
this.workCycleIndexedRead(pc, addr, addrIdx);
return this.readByte(addrIdx);
}
@ -515,7 +532,7 @@ export default class CPU6502 {
const addr = this.readWordPC();
const pc = this.addr;
const addrIdx = (addr + this.xr) & 0xffff;
this.writePageCross(pc, addr, addrIdx);
this.workCycleIndexedWrite(pc, addr, addrIdx);
this.writeByte(addrIdx, val);
}
@ -524,7 +541,7 @@ export default class CPU6502 {
const addr = this.readWordPC();
const pc = this.addr;
const addrIdx = (addr + this.yr) & 0xffff;
this.writePageCross(pc, addr, addrIdx);
this.workCycleIndexedWrite(pc, addr, addrIdx);
this.writeByte(addrIdx, val);
}
@ -556,7 +573,7 @@ export default class CPU6502 {
const pc = this.addr;
const addr = this.readZPWord(zpAddr);
const addrIdx = (addr + this.yr) & 0xffff;
this.writePageCross(pc, addr, addrIdx);
this.workCycleIndexedWrite(pc, addr, addrIdx);
this.writeByte(addrIdx, val);
}

View File

@ -89,6 +89,122 @@ describe('CPU6502', function() {
cpu.addPageHandler(bios);
});
describe('#step functions', function() {
const code = [0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA];
const initialState = {...DEFAULT_STATE};
it('step', function() {
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.step();
expect(cpu.getState()).toEqual(
{ ...DEFAULT_STATE, pc: 0x401, cycles: 2 }
);
expect(cpu.getCycles()).toEqual(2);
});
it('step with callback', function() {
const callback = jest.fn();
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.step(callback);
expect(cpu.getState()).toEqual({
...DEFAULT_STATE, pc: 0x401, cycles: 2,
});
expect(cpu.getCycles()).toEqual(2);
expect(callback).toHaveBeenCalledTimes(1);
});
it('stepN', function() {
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.stepN(4);
expect(cpu.getState()).toEqual({
...DEFAULT_STATE, pc: 0x404, cycles: 8,
});
expect(cpu.getCycles()).toEqual(8);
});
it('stepN with callback', function() {
const callback = jest.fn();
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.stepN(4, callback);
expect(cpu.getState()).toEqual({
...DEFAULT_STATE, pc: 0x404, cycles: 8,
});
expect(cpu.getCycles()).toEqual(8);
expect(callback).toHaveBeenCalledTimes(4);
});
it('stepN with breakpoint', function() {
const callback = jest.fn().mockReturnValue(true);
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.stepN(4, callback);
expect(cpu.getState()).toEqual({
...DEFAULT_STATE, pc: 0x401, cycles: 2,
});
expect(cpu.getCycles()).toEqual(2);
expect(callback).toHaveBeenCalledTimes(1);
});
it('stepCycles', function() {
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.stepCycles(4);
expect(cpu.getState()).toEqual({
...DEFAULT_STATE, pc: 0x402, cycles: 4,
});
expect(cpu.getCycles()).toEqual(4);
});
it('stepCyclesDebug', function() {
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.stepCyclesDebug(4);
expect(cpu.getState()).toEqual({
...DEFAULT_STATE, pc: 0x402, cycles: 4,
});
expect(cpu.getCycles()).toEqual(4);
});
it('stepCyclesDebug with callback', function() {
const callback = jest.fn();
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.stepCyclesDebug(4, callback);
expect(cpu.getState()).toEqual({
...DEFAULT_STATE, pc: 0x402, cycles: 4,
});
expect(cpu.getCycles()).toEqual(4);
expect(callback).toHaveBeenCalledTimes(2);
});
it('stepCyclesDebug with breakpoint', function() {
const callback = jest.fn().mockReturnValue(true);
cpu.setState(initialState);
program = new Program(0x04, code);
cpu.addPageHandler(program);
cpu.stepCyclesDebug(4, callback);
expect(cpu.getState()).toEqual({
...DEFAULT_STATE, pc: 0x401, cycles: 2,
});
expect(cpu.getCycles()).toEqual(2);
expect(callback).toHaveBeenCalledTimes(1);
});
});
describe('#signals', function () {
it('should reset', function () {
cpu.reset();
@ -161,6 +277,14 @@ describe('CPU6502', function() {
pc: 0x1234
});
});
it('should log unimplemented opcodes', () => {
jest.spyOn(console, 'log').mockImplementation();
testCode([0xFF], 1, {}, {
cycles: 1
});
expect(console.log).toHaveBeenLastCalledWith('Unknown OpCode: FF at 0400');
});
});
describe('#registers', function() {