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.
このコミットが含まれているのは:
コミット
9fb9dd59ba
|
@ -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);
|
||||
|
|
読み込み中…
新しいイシューから参照