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