gba: latch timer register writes for 1 cycle (#1489)

Latches writes to timer registers for 1 cycle. This fixes common
off-by-one errors with precise timer use, which for example positively
impacts the test cases in #154.
このコミットが含まれているのは:
png183 2024-05-11 13:38:28 -07:00 committed by GitHub
コミット 9fb9dd59ba
この署名に対応する既知のキーがデータベースに存在しません
GPGキーID: B5690EEEBB952194
6個のファイルの変更63行の追加16行の削除

ファイルの表示

@ -70,6 +70,13 @@ auto CPU::step(u32 clocks) -> void {
timer[1].run();
timer[2].run();
timer[3].run();
if(context.timerLatched) {
timer[0].stepLatch();
timer[1].stepLatch();
timer[2].stepLatch();
timer[3].stepLatch();
context.timerLatched = 0;
}
context.clock++;
}

ファイルの表示

@ -128,6 +128,7 @@ struct CPU : ARM7TDMI, Thread, IO {
struct Timer {
//timer.cpp
auto stepLatch() -> void;
auto run() -> void;
auto step() -> void;
@ -142,6 +143,13 @@ struct CPU : ARM7TDMI, Thread, IO {
n1 cascade;
n1 irq;
n1 enable;
struct Latch {
n16 reload;
n8 control;
n2 reloadFlags;
n1 controlFlag;
} latch;
} timer[4];
struct Serial {
@ -229,6 +237,7 @@ struct CPU : ARM7TDMI, Thread, IO {
n1 stopped;
n1 booted; //set to true by the GBA BIOS
n1 dmaActive;
n1 timerLatched;
} context;
};

ファイルの表示

@ -301,24 +301,23 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
}
//TM0CNT_L, TM1CNT_L, TM2CNT_L, TM3CNT_L
case 0x0400'0100: case 0x0400'0104: case 0x0400'0108: case 0x0400'010c: timer().reload.byte(0) = data; return;
case 0x0400'0101: case 0x0400'0105: case 0x0400'0109: case 0x0400'010d: timer().reload.byte(1) = data; return;
case 0x0400'0100: case 0x0400'0104: case 0x0400'0108: case 0x0400'010c:
timer().latch.reload.byte(0) = data;
timer().latch.reloadFlags.bit(0) = 1;
context.timerLatched = 1;
return;
case 0x0400'0101: case 0x0400'0105: case 0x0400'0109: case 0x0400'010d:
timer().latch.reload.byte(1) = data;
timer().latch.reloadFlags.bit(1) = 1;
context.timerLatched = 1;
return;
//TM0CNT_H, TM1CNT_H, TM2CNT_H, TM3CNT_H
case 0x0400'0102: case 0x0400'0106: case 0x0400'010a: case 0x0400'010e: {
bool enable = timer().enable;
timer().frequency = data.bit(0,1);
timer().irq = data.bit(6);
timer().enable = data.bit(7);
if(address != 0x0400'0102) timer().cascade = data.bit(2);
if(!enable && timer().enable) { //0->1 transition
timer().pending = true;
}
case 0x0400'0102: case 0x0400'0106: case 0x0400'010a: case 0x0400'010e:
timer().latch.control = data;
timer().latch.controlFlag = 1;
context.timerLatched = 1;
return;
}
case 0x0400'0103: case 0x0400'0107: case 0x0400'010b: case 0x0400'010f:
return;

ファイルの表示

@ -42,6 +42,10 @@ auto CPU::serialize(serializer& s) -> void {
s(timer.cascade);
s(timer.irq);
s(timer.enable);
s(timer.latch.reload);
s(timer.latch.control);
s(timer.latch.reloadFlags);
s(timer.latch.controlFlag);
}
s(serial.shiftClockSelect);
@ -104,4 +108,5 @@ auto CPU::serialize(serializer& s) -> void {
s(context.stopped);
s(context.booted);
s(context.dmaActive);
s(context.timerLatched);
}

ファイルの表示

@ -1,3 +1,30 @@
auto CPU::Timer::stepLatch() -> void {
if(latch.reloadFlags.bit(0)) {
reload.byte(0) = latch.reload.byte(0);
latch.reloadFlags.bit(0) = 0;
}
if(latch.reloadFlags.bit(1)) {
reload.byte(1) = latch.reload.byte(1);
latch.reloadFlags.bit(1) = 0;
}
if(latch.controlFlag) {
n1 wasEnabled = enable;
frequency = latch.control.bit(0,1);
irq = latch.control.bit(6);
enable = latch.control.bit(7);
if(id != 0) cascade = latch.control.bit(2);
if(!wasEnabled && enable) { //0->1 transition
pending = true;
}
latch.controlFlag = 0;
}
}
inline auto CPU::Timer::run() -> void {
if(pending) {
pending = false;

ファイルの表示

@ -1,4 +1,4 @@
static const string SerializerVersion = "v131";
static const string SerializerVersion = "v138";
auto System::serialize(bool synchronize) -> serializer {
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);