mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2026-10-24 08:02:27 +00:00
fixed Z80 reset when probe/unprobe problem in buildCPU()
This commit is contained in:
1 parent
408c45481b
commit
801dc79c3a
3 files changed
+136
-9
No files matched your search
@@ -3383,12 +3383,17 @@ export class Z80 implements CPU, InstructionBased, IOBusConnected, SavesState<Z8
|
||||
retryData : number = -1;
|
||||
|
||||
private buildCPU() {
|
||||
if (this.memBus && this.ioBus) {
|
||||
// Build the core only once and route its callbacks through the current
|
||||
// bus fields. BasicHeadlessMachine.rewireCPUBuses() swaps memBus/ioBus
|
||||
// whenever a probe is attached or detached; rebuilding here would throw
|
||||
// away the whole register state (i.e. reset the machine). FastZ80 calls
|
||||
// core.mem_read/... at instruction time, so the closures pick up the swap.
|
||||
if (this.memBus && this.ioBus && !this.cpu) {
|
||||
this.cpu = new FastZ80({
|
||||
mem_read: this.memBus.read.bind(this.memBus),
|
||||
mem_write: this.memBus.write.bind(this.memBus),
|
||||
io_read: this.ioBus.read.bind(this.ioBus),
|
||||
io_write: this.ioBus.write.bind(this.ioBus),
|
||||
mem_read: (a) => this.memBus.read(a),
|
||||
mem_write: (a, v) => this.memBus.write(a, v),
|
||||
io_read: (a) => this.ioBus.read(a),
|
||||
io_write: (a, v) => this.ioBus.write(a, v),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,11 +238,13 @@ export class PacmanMachine extends BasicScanlineMachine {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.cpu.connectIOBus({
|
||||
// Go through the base connect* helpers so the probe wrapper is spliced
|
||||
// in when probing is toggled (and removed when it is not).
|
||||
this.connectCPUIOBus({
|
||||
read: (_p) => 0xff,
|
||||
write: (port, val) => { if ((port & 0xff) === 0) this.interruptVector = val & 0xff; }
|
||||
});
|
||||
this.cpu.connectMemoryBus({ read: this.readByte, write: this.writeByte });
|
||||
this.connectCPUMemoryBus({ read: this.readByte, write: this.writeByte });
|
||||
this.cpu.retryInterrupts = true;
|
||||
this.rom = new Uint8Array(this.defaultROMSize);
|
||||
this.gfx = new PacmanVideo(this.rom, this.vram, this.cram, this.ram);
|
||||
@@ -319,8 +321,6 @@ export class PacmanMachine extends BasicScanlineMachine {
|
||||
this.waveforms = hasCustom ? custom : PacmanMachine.WAVEFORMS;
|
||||
}
|
||||
|
||||
advanceCPU() { return this.cpu.advanceInsn(); }
|
||||
|
||||
/** Rebuild voice frequency from low-nibble registers. Voice 0 is 20-bit; 1/2 are 16-bit. */
|
||||
private voiceFreq(v: number): number {
|
||||
var r = this.soundRegs;
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import assert from "assert";
|
||||
import { describe, it } from "mocha";
|
||||
import { Z80 } from "../../src/common/cpu/ZilogZ80";
|
||||
import { BasicHeadlessMachine } from "../../src/common/devices";
|
||||
import { ProbeRecorder } 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('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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user