fix: resolve timing regressions

make sure to use fetch timings when fetching instructions
This commit is contained in:
Rekai Nyangadzayi Musuka 2022-10-21 05:13:08 -03:00
parent 130d40bc7f
commit 85d4690c56
2 changed files with 25 additions and 39 deletions

View File

@ -428,22 +428,24 @@ pub const Arm7tdmi = struct {
} }
pub fn step(self: *Self) void { pub fn step(self: *Self) void {
if (self.cpsr.t.read()) blk: { defer {
const opcode = @truncate(u16, self.pipe.step(self, u16) orelse break :blk); if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4);
self.pipe.flushed = false;
}
if (self.cpsr.t.read()) {
const opcode = @truncate(u16, self.pipe.step(self, u16) orelse return);
if (self.logger) |*trace| trace.mgbaLog(self, opcode); if (self.logger) |*trace| trace.mgbaLog(self, opcode);
thumb.lut[thumb.idx(opcode)](self, self.bus, opcode); thumb.lut[thumb.idx(opcode)](self, self.bus, opcode);
} else blk: { } else {
const opcode = self.pipe.step(self, u32) orelse break :blk; const opcode = self.pipe.step(self, u32) orelse return;
if (self.logger) |*trace| trace.mgbaLog(self, opcode); if (self.logger) |*trace| trace.mgbaLog(self, opcode);
if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) { if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) {
arm.lut[arm.idx(opcode)](self, self.bus, opcode); arm.lut[arm.idx(opcode)](self, self.bus, opcode);
} }
} }
if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4);
self.pipe.flushed = false;
} }
pub fn stepDmaTransfer(self: *Self) bool { pub fn stepDmaTransfer(self: *Self) bool {
@ -481,8 +483,8 @@ pub const Arm7tdmi = struct {
// Return if IME is disabled, CPSR I is set or there is nothing to handle // Return if IME is disabled, CPSR I is set or there is nothing to handle
if (!self.bus.io.ime or self.cpsr.i.read() or should_handle == 0) return; if (!self.bus.io.ime or self.cpsr.i.read() or should_handle == 0) return;
// If pipeline isn't full, return but reschedule the handling of the event // If Pipeline isn't full, we have a bug
if (!self.pipe.isFull()) return; std.debug.assert(self.pipe.isFull());
// log.debug("Handling Interrupt!", .{}); // log.debug("Handling Interrupt!", .{});
self.bus.io.haltcnt = .Execute; self.bus.io.haltcnt = .Execute;
@ -501,23 +503,18 @@ pub const Arm7tdmi = struct {
self.pipe.reload(self); self.pipe.reload(self);
} }
inline fn fetch(self: *Self, comptime T: type) T { inline fn fetch(self: *Self, comptime T: type, address: u32) T {
comptime std.debug.assert(T == u32 or T == u16); // Opcode may be 32-bit (ARM) or 16-bit (THUMB) comptime std.debug.assert(T == u32 or T == u16); // Opcode may be 32-bit (ARM) or 16-bit (THUMB)
defer self.r[15] += if (T == u32) 4 else 2;
// FIXME: You better hope this is optimized out // Bus.read will advance the scheduler. There are different timings for CPU fetches,
// so we want to undo what Bus.read will apply. We can do this by caching the current tick
// This is very dumb.
//
// FIXME: Please rework this
const tick_cache = self.sched.tick; const tick_cache = self.sched.tick;
defer self.sched.tick = tick_cache + Bus.fetch_timings[@boolToInt(T == u32)][@truncate(u4, self.r[15] >> 24)]; defer self.sched.tick = tick_cache + Bus.fetch_timings[@boolToInt(T == u32)][@truncate(u4, address >> 24)];
return self.bus.read(T, self.r[15]); return self.bus.read(T, address);
}
fn debug_log(self: *const Self, file: *const File, opcode: u32) void {
if (self.binary_log) {
self.skyLog(file) catch unreachable;
} else {
self.mgbaLog(file, opcode) catch unreachable;
}
} }
pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn { pub fn panic(self: *const Self, comptime format: []const u8, args: anytype) noreturn {
@ -655,15 +652,6 @@ const Pipeline = struct {
}; };
} }
pub fn flush(self: *Self) void {
for (self.stage) |*opcode| opcode.* = null;
self.flushed = true;
// Note: If using this, add
// if (!self.pipe.flushed) self.r[15] += if (self.cpsr.t.read()) 2 else @as(u32, 4);
// to the end of Arm7tdmi.step
}
pub fn isFull(self: *const Self) bool { pub fn isFull(self: *const Self) bool {
return self.stage[0] != null and self.stage[1] != null; return self.stage[0] != null and self.stage[1] != null;
} }
@ -672,22 +660,22 @@ const Pipeline = struct {
comptime std.debug.assert(T == u32 or T == u16); comptime std.debug.assert(T == u32 or T == u16);
// FIXME: https://github.com/ziglang/zig/issues/12642 // FIXME: https://github.com/ziglang/zig/issues/12642
const opcode = self.stage[0..1][0]; var opcode = self.stage[0];
self.stage[0] = self.stage[1]; self.stage[0] = self.stage[1];
self.stage[1] = cpu.bus.read(T, cpu.r[15]); self.stage[1] = cpu.fetch(T, cpu.r[15]);
return opcode; return opcode;
} }
pub fn reload(self: *Self, cpu: *Arm7tdmi) void { pub fn reload(self: *Self, cpu: *Arm7tdmi) void {
if (cpu.cpsr.t.read()) { if (cpu.cpsr.t.read()) {
self.stage[0] = cpu.bus.read(u16, cpu.r[15]); self.stage[0] = cpu.fetch(u16, cpu.r[15]);
self.stage[1] = cpu.bus.read(u16, cpu.r[15] + 2); self.stage[1] = cpu.fetch(u16, cpu.r[15] + 2);
cpu.r[15] += 4; cpu.r[15] += 4;
} else { } else {
self.stage[0] = cpu.bus.read(u32, cpu.r[15]); self.stage[0] = cpu.fetch(u32, cpu.r[15]);
self.stage[1] = cpu.bus.read(u32, cpu.r[15] + 4); self.stage[1] = cpu.fetch(u32, cpu.r[15] + 4);
cpu.r[15] += 8; cpu.r[15] += 8;
} }

View File

@ -73,8 +73,6 @@ pub fn fmt4(comptime op: u4) InstrFn {
cpu.cpsr.z.write(result == 0); cpu.cpsr.z.write(result == 0);
cpu.cpsr.c.write(overflow); cpu.cpsr.c.write(overflow);
cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1); cpu.cpsr.v.write(((op1 ^ result) & (op2 ^ result)) >> 31 & 1 == 1);
// FIXME: Pretty sure CMN Is the same
}, },
0x6 => { 0x6 => {
// SBC // SBC