From d1561f1be37d6066b1bffc8cfaf6b8c2e9c417c6 Mon Sep 17 00:00:00 2001
From: AnonymusRaccoon
Date: Fri, 28 Feb 2020 19:20:59 +0100
Subject: [PATCH] Adding branch instructions
---
sources/CPU/CPU.cpp | 13 +-
sources/CPU/CPU.hpp | 33 ++-
.../CPU/Instructions/InternalInstruction.cpp | 71 +++++
sources/Debugger/CPUDebug.cpp | 21 ++
sources/Debugger/CPUDebug.hpp | 2 +
tests/CPU/testInternal.cpp | 279 ++++++++++++++++++
6 files changed, 417 insertions(+), 2 deletions(-)
diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp
index 36ef7fb..ca1a0b3 100644
--- a/sources/CPU/CPU.cpp
+++ b/sources/CPU/CPU.cpp
@@ -328,7 +328,7 @@ namespace ComSquare::CPU
case Instructions::XCE: this->XCE(); return 2;
- case Instructions::SBC_IM: this->SBC(this->_getImmediateAddrForA()); return 2 + !this->_registers.p.m;
+ case Instructions::SBC_IM: this->SBC(this->_getImmediateAddrForA()); return 2 + !this->_registers.p.m;
case Instructions::SBC_ABS: this->SBC(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::SBC_ABSl: this->SBC(this->_getAbsoluteLongAddr()); return 5 + !this->_registers.p.m;
case Instructions::SBC_DP: this->SBC(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
@@ -359,6 +359,17 @@ namespace ComSquare::CPU
case Instructions::CPY_ABS: this->CPY(this->_getAbsoluteAddr()); return 4 + !this->_registers.p.m;
case Instructions::CPY_DP: this->CPY(this->_getDirectAddr()); return 3 + !this->_registers.p.m + this->_registers.dl != 0;
+ case Instructions::BCC: return this->BCC(this->_registers.pc++) + 2 + this->_isEmulationMode;
+ case Instructions::BCS: return this->BCS(this->_registers.pc++) + 2 + this->_isEmulationMode;
+ case Instructions::BEQ: return this->BEQ(this->_registers.pc++) + 2 + this->_isEmulationMode;
+ case Instructions::BNE: return this->BNE(this->_registers.pc++) + 2 + this->_isEmulationMode;
+ case Instructions::BMI: return this->BMI(this->_registers.pc++) + 2 + this->_isEmulationMode;
+ case Instructions::BPL: return this->BPL(this->_registers.pc++) + 2 + this->_isEmulationMode;
+ case Instructions::BVC: return this->BVC(this->_registers.pc++) + 2 + this->_isEmulationMode;
+ case Instructions::BVS: return this->BVS(this->_registers.pc++) + 2 + this->_isEmulationMode;
+ case Instructions::BRA: this->BRA(this->_registers.pc++); return 3 + this->_isEmulationMode;
+ case Instructions::BRL: this->BRL(this->_registers.pc); this->_registers.pc += 2; return 4;
+
default:
throw InvalidOpcode("CPU", opcode);
}
diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp
index ecbadff..ab2d913 100644
--- a/sources/CPU/CPU.hpp
+++ b/sources/CPU/CPU.hpp
@@ -341,7 +341,18 @@ namespace ComSquare::CPU
CPY_IM = 0xC0,
CPY_ABS = 0xCC,
- CPY_DP = 0xC4
+ CPY_DP = 0xC4,
+
+ BCC = 0x90,
+ BCS = 0xB0,
+ BEQ = 0xF0,
+ BNE = 0xD0,
+ BMI = 0x30,
+ BPL = 0x10,
+ BVC = 0x50,
+ BVS = 0x70,
+ BRA = 0x80,
+ BRL = 0x82
};
//! @brief The main CPU
@@ -509,6 +520,26 @@ namespace ComSquare::CPU
void CPX(uint24_t valueAddr);
//! @brief Compare the Y register with the memory
void CPY(uint24_t valueAddr);
+ //! @brief Branch if carry clear
+ bool BCC(uint24_t valueAddr);
+ //! @brief Branch if carry set
+ bool BCS(uint24_t valueAddr);
+ //! @brief Branch if equal
+ bool BEQ(uint24_t valueAddr);
+ //! @brief Branch if not equal
+ bool BNE(uint24_t valueAddr);
+ //! @brief Branch if minus
+ bool BMI(uint24_t valueAddr);
+ //! @brief Branch if plus
+ bool BPL(uint24_t valueAddr);
+ //! @brief Branch if Overflow Clear
+ bool BVC(uint24_t valueAddr);
+ //! @brief Branch if Overflow Set
+ bool BVS(uint24_t valueAddr);
+ //! @brief Branch always
+ bool BRA(uint24_t valueAddr);
+ //! @brief Branch always long
+ bool BRL(uint24_t valueAddr);
public:
explicit CPU(std::shared_ptr bus, Cartridge::Header &cartridgeHeader);
CPU(const CPU &) = default;
diff --git a/sources/CPU/Instructions/InternalInstruction.cpp b/sources/CPU/Instructions/InternalInstruction.cpp
index 890ad93..abc1d6b 100644
--- a/sources/CPU/Instructions/InternalInstruction.cpp
+++ b/sources/CPU/Instructions/InternalInstruction.cpp
@@ -226,4 +226,75 @@ namespace ComSquare::CPU
this->_registers.p.n = y & 0x8000u;
}
}
+
+ bool CPU::BCC(uint24_t valueAddr)
+ {
+ if (!this->_registers.p.c)
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return !this->_registers.p.c;
+ }
+
+ bool CPU::BCS(uint24_t valueAddr)
+ {
+ if (this->_registers.p.c)
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return this->_registers.p.c;
+ }
+
+ bool CPU::BEQ(uint24_t valueAddr)
+ {
+ if (this->_registers.p.z)
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return this->_registers.p.z;
+ }
+
+ bool CPU::BNE(uint24_t valueAddr)
+ {
+ if (!this->_registers.p.z)
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return !this->_registers.p.z;
+ }
+
+ bool CPU::BMI(uint24_t valueAddr)
+ {
+ if (this->_registers.p.n)
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return this->_registers.p.n;
+ }
+
+ bool CPU::BPL(uint24_t valueAddr)
+ {
+ if (!this->_registers.p.n)
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return !this->_registers.p.n;
+ }
+
+ bool CPU::BRA(uint24_t valueAddr)
+ {
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return true;
+ }
+
+ bool CPU::BRL(uint24_t valueAddr)
+ {
+ unsigned value = this->_bus->read(valueAddr);
+ value += this->_bus->read(valueAddr + 1) << 8u;
+
+ this->_registers.pac += static_cast(value);
+ return true;
+ }
+
+ bool CPU::BVC(uint24_t valueAddr)
+ {
+ if (!this->_registers.p.v)
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return !this->_registers.p.v;
+ }
+
+ bool CPU::BVS(uint24_t valueAddr)
+ {
+ if (this->_registers.p.v)
+ this->_registers.pac += static_cast(this->_bus->read(valueAddr));
+ return this->_registers.p.v;
+ }
}
\ No newline at end of file
diff --git a/sources/Debugger/CPUDebug.cpp b/sources/Debugger/CPUDebug.cpp
index e924a14..613e0df 100644
--- a/sources/Debugger/CPUDebug.cpp
+++ b/sources/Debugger/CPUDebug.cpp
@@ -146,6 +146,16 @@ namespace ComSquare::Debugger
return ss.str();
}
+ std::string CPUDebug::_getImmediateValue16Bits(uint24_t pc)
+ {
+ unsigned value = this->_bus->read(pc);
+ value += this->_bus->read(pc + 1) << 8u;
+
+ std::stringstream ss;
+ ss << "#$" << std::hex << value;
+ return ss.str();
+ }
+
std::string CPUDebug::_getDirectValue(uint24_t pc)
{
std::stringstream ss;
@@ -345,6 +355,17 @@ namespace ComSquare::Debugger
case Instructions::CPY_ABS: return "CPY " + this->_getAbsoluteValue(pc);
case Instructions::CPY_DP: return "CPY";
+ case Instructions::BCC: return "BCC " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BCS: return "BCS " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BEQ: return "BEQ " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BNE: return "BNE " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BMI: return "BMI " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BPL: return "BPL " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BVC: return "BVC " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BVS: return "BVS " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BRA: return "BRA " + this->_getImmediateValue8Bits(pc);
+ case Instructions::BRL: return "BRL " + this->_getImmediateValue16Bits(pc);
+
default: return "Unknown";
}
}
diff --git a/sources/Debugger/CPUDebug.hpp b/sources/Debugger/CPUDebug.hpp
index 6489a71..e40f361 100644
--- a/sources/Debugger/CPUDebug.hpp
+++ b/sources/Debugger/CPUDebug.hpp
@@ -38,6 +38,8 @@ namespace ComSquare::Debugger
std::string _getImmediateValueForX(uint24_t pc);
//! @brief Return a printable string corresponding to the value of a 8bits immediate addressing mode (used only with SEP and REP).
std::string _getImmediateValue8Bits(uint24_t pc);
+ //! @brief Return a printable string corresponding to the value of a 16bits immediate addressing mode.
+ std::string _getImmediateValue16Bits(uint24_t pc);
//! @brief Return a printable string corresponding to the value of a direct addressing mode.
std::string _getDirectValue(uint24_t pc);
//! @brief Return a printable string corresponding to the value of an absolute addressing mode.
diff --git a/tests/CPU/testInternal.cpp b/tests/CPU/testInternal.cpp
index 061ba62..b704172 100644
--- a/tests/CPU/testInternal.cpp
+++ b/tests/CPU/testInternal.cpp
@@ -604,4 +604,283 @@ Test(CPY, negative)
cr_assert_eq(pair.second.cpu->_registers.p.z, false, "The zero flag should not be set");
cr_assert_eq(pair.second.cpu->_registers.p.n, true, "The negative flag should be set");
cr_assert_eq(pair.second.cpu->_registers.p.c, false, "The carry flag should not be set");
+}
+
+Test(BCC, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.c = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BCC(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BCC, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.c = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BCC(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BCC, noJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.c = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x90;
+ pair.second.cpu->BCC(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BCS, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.c = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BCS(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BCS, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.c = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BCS(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BCS, noJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.c = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x90;
+ pair.second.cpu->BCS(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BEQ, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.z = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BEQ(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BEQ, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.z = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BEQ(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BEQ, noJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.z = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x90;
+ pair.second.cpu->BEQ(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BNE, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.z = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BNE(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BNE, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.z = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BNE(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BNE, noJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.z = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x90;
+ pair.second.cpu->BNE(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BMI, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.n = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BMI(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BMI, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.n = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BMI(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BMI, noJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.n = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x90;
+ pair.second.cpu->BMI(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BPL, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.n = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BPL(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BPL, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.n = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BPL(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BPL, noJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.n = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x90;
+ pair.second.cpu->BPL(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BRA, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BRA(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BRA, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BRA(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BRL, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.pc = 0x8080;
+ pair.second.wram->_data[0] = 0x00;
+ pair.second.wram->_data[1] = 0x10;
+ pair.second.cpu->BRL(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x9080, "The program counter should be equal to 0x9080 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BRL, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.pc = 0x8080;
+ pair.second.wram->_data[0] = 0x00;
+ pair.second.wram->_data[1] = 0xF0;
+ pair.second.cpu->BRL(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x7080, "The program counter should be equal to 0x7080 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BVC, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.v = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BVC(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BVC, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.v = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BVC(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BVC, noJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.v = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x90;
+ pair.second.cpu->BVC(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+
+Test(BVS, basic)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.v = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x50;
+ pair.second.cpu->BVS(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0xD0, "The program counter should be equal to 0xD0 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BVS, negativeJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.v = true;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0xF0;
+ pair.second.cpu->BVS(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x70, "The program counter should be equal to 0x70 but it was 0x%x.", pair.second.cpu->_registers.pc);
+}
+
+Test(BVS, noJump)
+{
+ auto pair = Init();
+ pair.second.cpu->_registers.p.v = false;
+ pair.second.cpu->_registers.pc = 0x80;
+ pair.second.wram->_data[0] = 0x90;
+ pair.second.cpu->BVS(0x0);
+ cr_assert_eq(pair.second.cpu->_registers.pc, 0x80, "The program counter should be equal to 0x80 but it was 0x%x.", pair.second.cpu->_registers.pc);
}
\ No newline at end of file