LR35902 Simplify timing mechanism (corrects some timing issues as well!)

Signed-off-by: Adrian Conlon <adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon
2020-11-12 18:21:51 +00:00
parent b8a2db96f4
commit efed7dae23
4 changed files with 43 additions and 93 deletions

View File

@@ -48,38 +48,49 @@ namespace EightBit {
void handleRESET() final; void handleRESET() final;
void handleINT() final; void handleINT() final;
void jr(int8_t offset) final { void memoryWrite() final {
IntelProcessor::jr(offset); tick();
tick(5); IntelProcessor::memoryWrite();
} }
int callConditional(const int condition) final { uint8_t memoryRead() final {
if (IntelProcessor::callConditional(condition)) tick();
tick(3); return IntelProcessor::memoryRead();
tick(3); }
return condition;
void pushWord(register16_t value) final {
tick();
IntelProcessor::pushWord(value);
}
void jr(int8_t offset) final {
IntelProcessor::jr(offset);
tick();
} }
int jumpConditional(const int condition) final { int jumpConditional(const int condition) final {
IntelProcessor::jumpConditional(condition); if (IntelProcessor::jumpConditional(condition))
tick(3); tick();
return condition; return condition;
} }
int returnConditional(const int condition) final { int returnConditional(const int condition) final {
if (IntelProcessor::returnConditional(condition)) IntelProcessor::returnConditional(condition);
tick(3); tick();
tick(2);
return condition; return condition;
} }
int jrConditional(const int condition) final { int jrConditional(const int condition) final {
if (IntelProcessor::jrConditional(condition)) if (!IntelProcessor::jrConditional(condition))
tick(); tick();
tick(2);
return condition; return condition;
} }
void ret() final {
IntelProcessor::ret();
tick();
}
private: private:
Bus& m_bus; Bus& m_bus;
@@ -222,7 +233,7 @@ namespace EightBit {
[[nodiscard]] static uint8_t add(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0); [[nodiscard]] static uint8_t add(uint8_t& f, uint8_t operand, uint8_t value, int carry = 0);
[[nodiscard]] static uint8_t adc(uint8_t& f, uint8_t operand, uint8_t value); [[nodiscard]] static uint8_t adc(uint8_t& f, uint8_t operand, uint8_t value);
[[nodiscard]] static uint8_t sbc(uint8_t& f, uint8_t operand, uint8_t value); [[nodiscard]] static uint8_t sbc(uint8_t& f, uint8_t operand, uint8_t value);
[[nodiscard]] static uint8_t andr(uint8_t& f, uint8_t operand, uint8_t value); static uint8_t andr(uint8_t& f, uint8_t operand, uint8_t value);
[[nodiscard]] static uint8_t xorr(uint8_t& f, uint8_t operand, uint8_t value); [[nodiscard]] static uint8_t xorr(uint8_t& f, uint8_t operand, uint8_t value);
[[nodiscard]] static uint8_t orr(uint8_t& f, uint8_t operand, uint8_t value); [[nodiscard]] static uint8_t orr(uint8_t& f, uint8_t operand, uint8_t value);
[[nodiscard]] static void compare(uint8_t& f, uint8_t operand, uint8_t value); [[nodiscard]] static void compare(uint8_t& f, uint8_t operand, uint8_t value);

View File

@@ -38,7 +38,6 @@ void EightBit::GameBoy::LR35902::handleINT() {
raiseHALT(); raiseHALT();
di(); di();
restart(BUS().DATA()); restart(BUS().DATA());
tick(4);
} }
void EightBit::GameBoy::LR35902::di() { void EightBit::GameBoy::LR35902::di() {
@@ -83,11 +82,7 @@ bool EightBit::GameBoy::LR35902::convertCondition(const uint8_t f, int flag) {
} }
void EightBit::GameBoy::LR35902::returnConditionalFlag(const uint8_t f, const int flag) { void EightBit::GameBoy::LR35902::returnConditionalFlag(const uint8_t f, const int flag) {
if (convertCondition(f, flag)) { returnConditional(convertCondition(f, flag));
tick(3);
ret();
}
tick(2);
} }
void EightBit::GameBoy::LR35902::jrConditionalFlag(const uint8_t f, const int flag) { void EightBit::GameBoy::LR35902::jrConditionalFlag(const uint8_t f, const int flag) {
@@ -109,6 +104,8 @@ void EightBit::GameBoy::LR35902::reti() {
EightBit::register16_t EightBit::GameBoy::LR35902::add(uint8_t& f, const register16_t operand, const register16_t value) { EightBit::register16_t EightBit::GameBoy::LR35902::add(uint8_t& f, const register16_t operand, const register16_t value) {
tick();
const int addition = operand.word + value.word; const int addition = operand.word + value.word;
const register16_t result = addition; const register16_t result = addition;
@@ -238,8 +235,7 @@ void EightBit::GameBoy::LR35902::bit(uint8_t& f, const int n, const uint8_t oper
ASSUME(n >= 0); ASSUME(n >= 0);
ASSUME(n <= 7); ASSUME(n <= 7);
const auto carry = f & CF; const auto carry = f & CF;
uint8_t discarded = operand; andr(f, operand, Chip::bit(n));
andr(f, discarded, Chip::bit(n));
f = setBit(f, CF, carry); f = setBit(f, CF, carry);
} }
@@ -331,6 +327,7 @@ int EightBit::GameBoy::LR35902::step() {
} else if (UNLIKELY(lowered(INT()))) { } else if (UNLIKELY(lowered(INT()))) {
handleINT(); handleINT();
} else if (UNLIKELY(lowered(HALT()))) { } else if (UNLIKELY(lowered(HALT()))) {
IntelProcessor::memoryRead(PC());
Processor::execute(0); // NOP Processor::execute(0); // NOP
} else { } else {
Processor::execute(fetchByte()); Processor::execute(fetchByte());
@@ -397,29 +394,17 @@ void EightBit::GameBoy::LR35902::executeCB(const int x, const int y, const int z
default: default:
UNREACHABLE; UNREACHABLE;
} }
tick(2);
R(z, operand); R(z, operand);
F() = adjustZero<LR35902>(F(), operand); F() = adjustZero<LR35902>(F(), operand);
if (UNLIKELY(z == 6))
tick(2);
break; break;
} case 1: // BIT y, r[z] } case 1: // BIT y, r[z]
bit(F(), y, R(z)); bit(F(), y, R(z));
tick(2);
if (UNLIKELY(z == 6))
tick(2);
break; break;
case 2: // RES y, r[z] case 2: // RES y, r[z]
R(z, res(y, R(z))); R(z, res(y, R(z)));
tick(2);
if (UNLIKELY(z == 6))
tick(2);
break; break;
case 3: // SET y, r[z] case 3: // SET y, r[z]
R(z, set(y, R(z))); R(z, set(y, R(z)));
tick(2);
if (UNLIKELY(z == 6))
tick(2);
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
@@ -433,20 +418,16 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
case 0: // Relative jumps and assorted ops case 0: // Relative jumps and assorted ops
switch (y) { switch (y) {
case 0: // NOP case 0: // NOP
tick();
break; break;
case 1: // GB: LD (nn),SP case 1: // GB: LD (nn),SP
BUS().ADDRESS() = fetchWord(); BUS().ADDRESS() = fetchWord();
setWord(SP()); setWord(SP());
tick(5);
break; break;
case 2: // GB: STOP case 2: // GB: STOP
stop(); stop();
tick();
break; break;
case 3: // JR d case 3: // JR d
jr(fetchByte()); jr(fetchByte());
tick(4);
break; break;
case 4: // JR cc,d case 4: // JR cc,d
case 5: case 5:
@@ -462,11 +443,9 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
switch (q) { switch (q) {
case 0: // LD rp,nn case 0: // LD rp,nn
RP(p) = fetchWord(); RP(p) = fetchWord();
tick(3);
break; break;
case 1: // ADD HL,rp case 1: // ADD HL,rp
HL() = add(F(), HL(), RP(p)); HL() = add(F(), HL(), RP(p));
tick(2);
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
@@ -478,19 +457,15 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
switch (p) { switch (p) {
case 0: // LD (BC),A case 0: // LD (BC),A
IntelProcessor::memoryWrite(BC(), A()); IntelProcessor::memoryWrite(BC(), A());
tick(2);
break; break;
case 1: // LD (DE),A case 1: // LD (DE),A
IntelProcessor::memoryWrite(DE(), A()); IntelProcessor::memoryWrite(DE(), A());
tick(2);
break; break;
case 2: // GB: LDI (HL),A case 2: // GB: LDI (HL),A
IntelProcessor::memoryWrite(HL()++, A()); IntelProcessor::memoryWrite(HL()++, A());
tick(2);
break; break;
case 3: // GB: LDD (HL),A case 3: // GB: LDD (HL),A
IntelProcessor::memoryWrite(HL()--, A()); IntelProcessor::memoryWrite(HL()--, A());
tick(2);
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
@@ -500,19 +475,15 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
switch (p) { switch (p) {
case 0: // LD A,(BC) case 0: // LD A,(BC)
A() = IntelProcessor::memoryRead(BC()); A() = IntelProcessor::memoryRead(BC());
tick(2);
break; break;
case 1: // LD A,(DE) case 1: // LD A,(DE)
A() = IntelProcessor::memoryRead(DE()); A() = IntelProcessor::memoryRead(DE());
tick(2);
break; break;
case 2: // GB: LDI A,(HL) case 2: // GB: LDI A,(HL)
A() = IntelProcessor::memoryRead(HL()++); A() = IntelProcessor::memoryRead(HL()++);
tick(2);
break; break;
case 3: // GB: LDD A,(HL) case 3: // GB: LDD A,(HL)
A() = IntelProcessor::memoryRead(HL()--); A() = IntelProcessor::memoryRead(HL()--);
tick(2);
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
@@ -533,25 +504,18 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
default: default:
UNREACHABLE; UNREACHABLE;
} }
tick(2); tick();
break; break;
case 4: { // 8-bit INC case 4: { // 8-bit INC
auto operand = R(y); auto operand = R(y);
R(y, increment(F(), operand)); R(y, increment(F(), operand));
tick();
if (UNLIKELY(y == 6))
tick(2);
break; break;
} case 5: { // 8-bit DEC } case 5: { // 8-bit DEC
auto operand = R(y); auto operand = R(y);
R(y, decrement(F(), operand)); R(y, decrement(F(), operand));
tick();
if (UNLIKELY(y == 6))
tick(2);
break; break;
} case 6: // 8-bit load immediate } case 6: // 8-bit load immediate
R(y, fetchByte()); // LD r,n R(y, fetchByte()); // LD r,n
tick(2);
break; break;
case 7: // Assorted operations on accumulator/flags case 7: // Assorted operations on accumulator/flags
switch (y) { switch (y) {
@@ -582,7 +546,6 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
default: default:
UNREACHABLE; UNREACHABLE;
} }
tick();
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
@@ -593,10 +556,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
lowerHALT(); lowerHALT();
} else { } else {
R(y, R(z)); R(y, R(z));
if (UNLIKELY((y == 6) || (z == 6))) // M operations
tick();
} }
tick();
break; break;
case 2: // Operate on accumulator and register/memory location case 2: // Operate on accumulator and register/memory location
switch (y) { switch (y) {
@@ -627,9 +587,6 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
default: default:
UNREACHABLE; UNREACHABLE;
} }
tick();
if (UNLIKELY(z == 6))
tick();
break; break;
case 3: case 3:
switch (z) { switch (z) {
@@ -643,11 +600,11 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
break; break;
case 4: // GB: LD (FF00 + n),A case 4: // GB: LD (FF00 + n),A
IntelProcessor::memoryWrite(IoRegisters::BASE + fetchByte(), A()); IntelProcessor::memoryWrite(IoRegisters::BASE + fetchByte(), A());
tick(3);
break; break;
case 5: { // GB: ADD SP,dd case 5: { // GB: ADD SP,dd
const auto before = SP().word; const auto before = SP().word;
const int8_t value = fetchByte(); const int8_t value = fetchByte();
tick(2);
const auto result = before + value; const auto result = before + value;
SP() = result; SP() = result;
const auto carried = before ^ value ^ (result & Mask16); const auto carried = before ^ value ^ (result & Mask16);
@@ -655,15 +612,14 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
F() = setBit(F(), CF, carried & Bit8); F() = setBit(F(), CF, carried & Bit8);
F() = setBit(F(), HC, carried & Bit4); F() = setBit(F(), HC, carried & Bit4);
} }
tick(4);
break; break;
case 6: // GB: LD A,(FF00 + n) case 6: // GB: LD A,(FF00 + n)
A() = IntelProcessor::memoryRead(IoRegisters::BASE + fetchByte()); A() = IntelProcessor::memoryRead(IoRegisters::BASE + fetchByte());
tick(3);
break; break;
case 7: { // GB: LD HL,SP + dd case 7: { // GB: LD HL,SP + dd
const auto before = SP().word; const auto before = SP().word;
const int8_t value = fetchByte(); const int8_t value = fetchByte();
tick();
const auto result = before + value; const auto result = before + value;
HL() = result; HL() = result;
const auto carried = before ^ value ^ (result & Mask16); const auto carried = before ^ value ^ (result & Mask16);
@@ -671,7 +627,6 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
F() = setBit(F(), CF, carried & Bit8); F() = setBit(F(), CF, carried & Bit8);
F() = setBit(F(), HC, carried & Bit4); F() = setBit(F(), HC, carried & Bit4);
} }
tick(3);
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
@@ -681,25 +636,21 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
switch (q) { switch (q) {
case 0: // POP rp2[p] case 0: // POP rp2[p]
RP2(p) = popWord(); RP2(p) = popWord();
tick(3);
break; break;
case 1: case 1:
switch (p) { switch (p) {
case 0: // RET case 0: // RET
ret(); ret();
tick(4);
break; break;
case 1: // GB: RETI case 1: // GB: RETI
reti(); reti();
tick(4);
break; break;
case 2: // JP HL case 2: // JP HL
jump(HL()); jump(HL());
tick();
break; break;
case 3: // LD SP,HL case 3: // LD SP,HL
SP() = HL(); SP() = HL();
tick(2); tick();
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
@@ -719,21 +670,17 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
break; break;
case 4: // GB: LD (FF00 + C),A case 4: // GB: LD (FF00 + C),A
IntelProcessor::memoryWrite(IoRegisters::BASE + C(), A()); IntelProcessor::memoryWrite(IoRegisters::BASE + C(), A());
tick(2);
break; break;
case 5: // GB: LD (nn),A case 5: // GB: LD (nn),A
BUS().ADDRESS() = MEMPTR() = fetchWord(); BUS().ADDRESS() = MEMPTR() = fetchWord();
IntelProcessor::memoryWrite(A()); IntelProcessor::memoryWrite(A());
tick(4);
break; break;
case 6: // GB: LD A,(FF00 + C) case 6: // GB: LD A,(FF00 + C)
A() = IntelProcessor::memoryRead(IoRegisters::BASE + C()); A() = IntelProcessor::memoryRead(IoRegisters::BASE + C());
tick(2);
break; break;
case 7: // GB: LD A,(nn) case 7: // GB: LD A,(nn)
BUS().ADDRESS() = MEMPTR() = fetchWord(); BUS().ADDRESS() = MEMPTR() = fetchWord();
A() = busRead(); A() = memoryRead();
tick(4);
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
@@ -743,7 +690,7 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
switch (y) { switch (y) {
case 0: // JP nn case 0: // JP nn
jump(MEMPTR() = fetchWord()); jump(MEMPTR() = fetchWord());
tick(4); tick();
break; break;
case 1: // CB prefix case 1: // CB prefix
m_prefixCB = true; m_prefixCB = true;
@@ -751,11 +698,9 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
break; break;
case 6: // DI case 6: // DI
di(); di();
tick();
break; break;
case 7: // EI case 7: // EI
ei(); ei();
tick();
break; break;
} }
break; break;
@@ -766,13 +711,11 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
switch (q) { switch (q) {
case 0: // PUSH rp2[p] case 0: // PUSH rp2[p]
pushWord(RP2(p)); pushWord(RP2(p));
tick(4);
break; break;
case 1: case 1:
switch (p) { switch (p) {
case 0: // CALL nn case 0: // CALL nn
call(MEMPTR() = fetchWord()); call(MEMPTR() = fetchWord());
tick(6);
break; break;
} }
break; break;
@@ -809,11 +752,9 @@ void EightBit::GameBoy::LR35902::executeOther(const int x, const int y, const in
default: default:
UNREACHABLE; UNREACHABLE;
} }
tick(2);
break; break;
case 7: // Restart: RST y * 8 case 7: // Restart: RST y * 8
restart(y << 3); restart(y << 3);
tick(4);
break; break;
default: default:
UNREACHABLE; UNREACHABLE;

View File

@@ -132,7 +132,7 @@ namespace EightBit {
// //
void restart(const uint8_t address) { virtual void restart(const uint8_t address) {
call(MEMPTR() = { address, 0 }); call(MEMPTR() = { address, 0 });
} }
@@ -169,7 +169,10 @@ namespace EightBit {
return condition; return condition;
} }
void ret() final; void ret() override {
Processor::ret();
MEMPTR() = PC();
}
private: private:
std::array<opcode_decoded_t, 0x100> m_decodedOpcodes; std::array<opcode_decoded_t, 0x100> m_decodedOpcodes;

View File

@@ -40,8 +40,3 @@ void EightBit::IntelProcessor::setWord(const register16_t value) {
LittleEndianProcessor::setWord(value); LittleEndianProcessor::setWord(value);
MEMPTR() = BUS().ADDRESS(); MEMPTR() = BUS().ADDRESS();
} }
void EightBit::IntelProcessor::ret() {
Processor::ret();
MEMPTR() = PC();
}