LR35902: Fix lots of timing issues by attaching to the "tick" event.

Signed-off-by: Adrian Conlon <adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon 2020-11-13 10:14:06 +00:00
parent efed7dae23
commit fa7e1480d3
4 changed files with 37 additions and 39 deletions

View File

@ -117,7 +117,7 @@ namespace EightBit {
poke(IF, peek(IF) | cause);
}
void checkTimers(int cycles);
void incrementTimers();
int timerClockTicks();
@ -125,7 +125,7 @@ namespace EightBit {
bool timerEnabled();
bool timerDisabled();
void incrementDIV(int cycles);
void incrementDIV();
void incrementTIMA();
void incrementLY();
@ -185,7 +185,7 @@ namespace EightBit {
bool m_p11 = true; // left/b
bool m_p10 = true; // right/a
void checkTimer(int cycles);
void incrementTimer();
void mask(const uint16_t address, const uint8_t masking) {
poke(address, peek(address) | ~masking);

View File

@ -26,10 +26,6 @@ namespace EightBit {
Signal<LR35902> ExecutingInstruction;
Signal<LR35902> ExecutedInstruction;
[[nodiscard]] auto clockCycles() const noexcept {
return cycles() * 4;
}
[[nodiscard]] register16_t& AF() final;
[[nodiscard]] register16_t& BC() final;
[[nodiscard]] register16_t& DE() final;
@ -49,46 +45,46 @@ namespace EightBit {
void handleINT() final;
void memoryWrite() final {
tick();
tick(4);
IntelProcessor::memoryWrite();
}
uint8_t memoryRead() final {
tick();
tick(4);
return IntelProcessor::memoryRead();
}
void pushWord(register16_t value) final {
tick();
tick(4);
IntelProcessor::pushWord(value);
}
void jr(int8_t offset) final {
IntelProcessor::jr(offset);
tick();
tick(4);
}
int jumpConditional(const int condition) final {
if (IntelProcessor::jumpConditional(condition))
tick();
tick(4);
return condition;
}
int returnConditional(const int condition) final {
IntelProcessor::returnConditional(condition);
tick();
tick(4);
return condition;
}
int jrConditional(const int condition) final {
if (!IntelProcessor::jrConditional(condition))
tick();
tick(4);
return condition;
}
void ret() final {
IntelProcessor::ret();
tick();
tick(4);
}
private:

View File

@ -146,15 +146,14 @@ void EightBit::GameBoy::IoRegisters::Bus_WrittenByte(EightBit::EventArgs) {
}
}
void EightBit::GameBoy::IoRegisters::checkTimers(int cycles) {
incrementDIV(cycles);
checkTimer(cycles);
void EightBit::GameBoy::IoRegisters::incrementTimers() {
incrementDIV();
incrementTimer();
}
void EightBit::GameBoy::IoRegisters::checkTimer(int cycles) {
void EightBit::GameBoy::IoRegisters::incrementTimer() {
if (timerEnabled()) {
m_timerCounter -= cycles;
if (m_timerCounter <= 0) {
if (--m_timerCounter == 0) {
m_timerCounter += m_timerRate;
incrementTIMA();
}
@ -164,13 +163,13 @@ void EightBit::GameBoy::IoRegisters::checkTimer(int cycles) {
int EightBit::GameBoy::IoRegisters::timerClockTicks() {
switch (timerClock()) {
case 0b00:
return 1024; // 4.096 Khz
return 256; // 4.096 Khz
case 0b01:
return 16; // 262.144 Khz
return 4; // 262.144 Khz
case 0b10:
return 64; // 65.536 Khz
return 16; // 65.536 Khz
case 0b11:
return 256; // 16.384 Khz
return 64; // 16.384 Khz
default:
UNREACHABLE;
}
@ -189,8 +188,8 @@ bool EightBit::GameBoy::IoRegisters::timerDisabled() {
return (peek(TAC) & Chip::Bit2) == 0;
}
void EightBit::GameBoy::IoRegisters::incrementDIV(int cycles) {
m_divCounter += cycles;
void EightBit::GameBoy::IoRegisters::incrementDIV() {
++m_divCounter;
poke(DIV, m_divCounter.high);
}

View File

@ -7,6 +7,12 @@
EightBit::GameBoy::LR35902::LR35902(Bus& memory)
: IntelProcessor(memory),
m_bus(memory) {
Ticked.connect([this](EventArgs) {
if ((cycles() % 4) == 0) {
m_bus.IO().incrementTimers();
m_bus.IO().transferDma();
}
});
}
EightBit::register16_t& EightBit::GameBoy::LR35902::AF() {
@ -30,7 +36,7 @@ void EightBit::GameBoy::LR35902::handleRESET() {
IntelProcessor::handleRESET();
di();
SP() = Mask16 - 1;
tick(4);
tick(4 * 4);
}
void EightBit::GameBoy::LR35902::handleINT() {
@ -104,7 +110,7 @@ void EightBit::GameBoy::LR35902::reti() {
EightBit::register16_t EightBit::GameBoy::LR35902::add(uint8_t& f, const register16_t operand, const register16_t value) {
tick();
tick(4);
const int addition = operand.word + value.word;
const register16_t result = addition;
@ -332,12 +338,9 @@ int EightBit::GameBoy::LR35902::step() {
} else {
Processor::execute(fetchByte());
}
m_bus.IO().checkTimers(clockCycles());
m_bus.IO().transferDma();
}
ExecutedInstruction.fire(*this);
return clockCycles();
return cycles();
}
int EightBit::GameBoy::LR35902::execute() {
@ -359,7 +362,7 @@ int EightBit::GameBoy::LR35902::execute() {
if (UNLIKELY(cycles() == 0))
throw std::logic_error("Unhandled opcode");
return clockCycles();
return cycles();
}
void EightBit::GameBoy::LR35902::executeCB(const int x, const int y, const int z, int, int) {
@ -504,7 +507,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
default:
UNREACHABLE;
}
tick();
tick(4);
break;
case 4: { // 8-bit INC
auto operand = R(y);
@ -604,7 +607,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
case 5: { // GB: ADD SP,dd
const auto before = SP().word;
const int8_t value = fetchByte();
tick(2);
tick(2 * 4);
const auto result = before + value;
SP() = result;
const auto carried = before ^ value ^ (result & Mask16);
@ -619,7 +622,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
case 7: { // GB: LD HL,SP + dd
const auto before = SP().word;
const int8_t value = fetchByte();
tick();
tick(4);
const auto result = before + value;
HL() = result;
const auto carried = before ^ value ^ (result & Mask16);
@ -650,7 +653,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
break;
case 3: // LD SP,HL
SP() = HL();
tick();
tick(4);
break;
default:
UNREACHABLE;
@ -690,7 +693,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
switch (y) {
case 0: // JP nn
jump(MEMPTR() = fetchWord());
tick();
tick(4);
break;
case 1: // CB prefix
m_prefixCB = true;