mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-25 15:05:25 +00:00
135 lines
4.3 KiB
TypeScript
135 lines
4.3 KiB
TypeScript
import assert from "assert";
|
|
import { describe, it } from "mocha";
|
|
import { Z80 } from "../../src/common/cpu/ZilogZ80";
|
|
import { BasicHeadlessMachine } from "../../src/common/devices";
|
|
import { ProbeRecorder, compactProbeLines } from "../../src/common/probe";
|
|
import { PacmanMachine } from "../../src/machine/pacman";
|
|
|
|
function makeBus(mem: Uint8Array) {
|
|
return {
|
|
read: (a: number) => mem[a & 0xffff],
|
|
write: (a: number, v: number) => { mem[a & 0xffff] = v & 0xff; },
|
|
};
|
|
}
|
|
|
|
function makeIO() {
|
|
return { read: (_a: number) => 0xff, write: (_a: number, _v: number) => { } };
|
|
}
|
|
|
|
describe('Z80 bus reconnection', function () {
|
|
it('should preserve CPU state when the buses are reconnected', function () {
|
|
const mem = new Uint8Array(0x10000);
|
|
// LD A,$42 ; LD B,$13
|
|
mem.set([0x3e, 0x42, 0x06, 0x13], 0);
|
|
const cpu = new Z80();
|
|
cpu.connectMemoryBus(makeBus(mem));
|
|
cpu.connectIOBus(makeIO());
|
|
cpu.advanceInsn();
|
|
cpu.advanceInsn();
|
|
const before = cpu.saveState();
|
|
assert.strictEqual(before.PC, 4);
|
|
assert.strictEqual((before.AF >> 8) & 0xff, 0x42);
|
|
|
|
// This is what BasicHeadlessMachine.rewireCPUBuses() does when a probe is
|
|
// attached or detached.
|
|
cpu.connectMemoryBus(makeBus(mem));
|
|
cpu.connectIOBus(makeIO());
|
|
|
|
const after = cpu.saveState();
|
|
assert.strictEqual(after.PC, before.PC);
|
|
assert.strictEqual(after.AF, before.AF);
|
|
assert.strictEqual(after.BC, before.BC);
|
|
});
|
|
|
|
it('should route accesses through the newly connected memory bus', function () {
|
|
const memA = new Uint8Array(0x10000);
|
|
const memB = new Uint8Array(0x10000);
|
|
// LD HL,$0010 ; LD A,(HL)
|
|
const prog = [0x21, 0x10, 0x00, 0x7e];
|
|
memA.set(prog, 0);
|
|
memB.set(prog, 0);
|
|
memA[0x10] = 0xaa;
|
|
memB[0x10] = 0xbb;
|
|
|
|
const cpu = new Z80();
|
|
cpu.connectMemoryBus(makeBus(memA));
|
|
cpu.connectIOBus(makeIO());
|
|
cpu.advanceInsn(); // LD HL,$0010
|
|
cpu.connectMemoryBus(makeBus(memB)); // swap in a different bus
|
|
cpu.advanceInsn(); // LD A,(HL) must read memB
|
|
assert.strictEqual((cpu.saveState().AF >> 8) & 0xff, 0xbb);
|
|
});
|
|
});
|
|
|
|
class TestZ80Machine extends BasicHeadlessMachine {
|
|
cpuFrequency = 1000000;
|
|
defaultROMSize = 0x10000;
|
|
cpu = new Z80();
|
|
ram = new Uint8Array(0x10000);
|
|
|
|
constructor() {
|
|
super();
|
|
this.connectCPUMemoryBus(this);
|
|
this.connectCPUIOBus(makeIO());
|
|
}
|
|
read(a: number): number { return this.ram[a & 0xffff]; }
|
|
write(a: number, v: number): void { this.ram[a & 0xffff] = v & 0xff; }
|
|
}
|
|
|
|
describe('Probing does not reset Z80 machines', function () {
|
|
it('should keep CPU state across connectProbe() on a generic Z80 machine', function () {
|
|
const m = new TestZ80Machine();
|
|
m.ram.set([0x3e, 0x42, 0x06, 0x13, 0x7e], 0);
|
|
m.advanceCPU();
|
|
m.advanceCPU();
|
|
const before = m.saveState().c;
|
|
assert.strictEqual(before.PC, 4);
|
|
|
|
const rec = new ProbeRecorder(m);
|
|
m.connectProbe(rec);
|
|
assert.strictEqual(m.saveState().c.PC, before.PC);
|
|
assert.strictEqual(m.saveState().c.AF, before.AF);
|
|
|
|
m.advanceCPU();
|
|
assert.ok(rec.idx > 0, 'probe should record data while connected');
|
|
|
|
m.connectProbe(null);
|
|
assert.strictEqual(m.saveState().c.PC, 5);
|
|
});
|
|
});
|
|
|
|
describe('Probe log line compaction', function () {
|
|
it('should drop empty clock ticks and keep tick order', function () {
|
|
const byClk = { 4: 'a', 0: 'b', 7: 'c' };
|
|
assert.deepStrictEqual(compactProbeLines(byClk), ['b', 'a', 'c']);
|
|
});
|
|
it('should keep each recorded tick exactly once', function () {
|
|
const byClk: { [clk: number]: number } = {};
|
|
for (let i = 0; i < 10; i += 3) byClk[i] = i;
|
|
assert.deepStrictEqual(compactProbeLines(byClk), [0, 3, 6, 9]);
|
|
});
|
|
});
|
|
|
|
describe('Pacman probing', function () {
|
|
it('should not reset and should record data when the probe is toggled', function () {
|
|
const m = new PacmanMachine();
|
|
m.rom.fill(0);
|
|
// LD A,$42 ; NOP ; LD B,$13
|
|
m.rom.set([0x3e, 0x42, 0x00, 0x06, 0x13], 0);
|
|
m.advanceCPU();
|
|
m.advanceCPU();
|
|
const before = m.saveState().c;
|
|
assert.strictEqual(before.PC, 3);
|
|
|
|
const rec = new ProbeRecorder(m);
|
|
m.connectProbe(rec);
|
|
assert.strictEqual(m.saveState().c.PC, before.PC, 'probe toggle must not reset Pacman');
|
|
|
|
m.advanceCPU();
|
|
assert.ok(rec.idx > 0, 'Pacman probe should record execute/clocks');
|
|
|
|
m.connectProbe(null);
|
|
assert.strictEqual(m.saveState().c.PC, 5);
|
|
});
|
|
});
|