diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bdd37c..18c98a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,7 +97,7 @@ add_executable(unit_tests sources/Exceptions/DebuggableError.hpp tests/CPU/Math/testOthersMath.cpp tests/testRectangleMemory.cpp -) + tests/CPU/Math/testCMP.cpp) # include criterion & coverage target_link_libraries(unit_tests criterion -lgcov) diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index e498095..a0598b6 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -389,6 +389,8 @@ namespace ComSquare::CPU int RTS(uint24_t, AddressingMode); //! @brief Return from subroutine long. int RTL(uint24_t, AddressingMode); + //! @brief Compare Accumulator with Memory. + int CMP(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -586,37 +588,37 @@ namespace ComSquare::CPU {&CPU::LDX, 4, "ldx", AddressingMode::AbsoluteIndexedByY, 3}, // BE {&CPU::LDA, 5, "lda", AddressingMode::AbsoluteIndexedByXLong, 4}, // BF {&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // C0 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C1 + {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectIndexedByX, 2}, // C1 {&CPU::REP, 3, "rep", AddressingMode::Immediate8bits, 2}, // C2 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C3 + {&CPU::CMP, 4, "cmp", AddressingMode::StackRelative, 2}, // C3 {&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // C4 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C5 + {&CPU::CMP, 3, "cmp", AddressingMode::DirectPage, 2}, // C5 {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // C6 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C7 + {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectLong, 2}, // C7 {&CPU::INY, 2, "iny", AddressingMode::Implied, 1}, // C8 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // C9 + {&CPU::CMP, 2, "cmp", AddressingMode::Implied, 2}, // C9 {&CPU::DEX, 2, "dex", AddressingMode::Implied, 1}, // CA {&CPU::BRK, 7, "wai #-#", AddressingMode::Implied, 2}, // CB {&CPU::BRK, 7, "cpy #-#", AddressingMode::Implied, 2}, // CC - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // CD + {&CPU::CMP, 4, "cmp", AddressingMode::Absolute, 3}, // CD {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // CE - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // CF + {&CPU::CMP, 6, "cmp", AddressingMode::AbsoluteLong, 4}, // CF {&CPU::BNE, 2, "bne", AddressingMode::Implied, 2}, // D0 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D1 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D2 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D3 + {&CPU::CMP, 5, "cmp", AddressingMode::DirectPageIndirectIndexedByY, 2}, // D1 + {&CPU::CMP, 5, "cmp", AddressingMode::DirectPageIndirect, 2}, // D2 + {&CPU::CMP, 7, "cmp", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // D3 {&CPU::BRK, 7, "pei #-#", AddressingMode::Implied, 2}, // D4 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D5 + {&CPU::CMP, 4, "cmp", AddressingMode::DirectPageIndexedByX, 2}, // D5 {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // D6 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D7 - {&CPU::CLD, 27, "cld", AddressingMode::Implied, 2}, // D8 - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // D9 + {&CPU::CMP, 6, "cmp", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // D7 + {&CPU::CLD, 2, "cld", AddressingMode::Implied, 2}, // D8 + {&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByY, 3}, // D9 {&CPU::PHX, 3, "phx", AddressingMode::Implied, 1}, // DA {&CPU::BRK, 7, "stp #-#", AddressingMode::Implied, 2}, // DB {&CPU::JML, 7, "jml", AddressingMode::AbsoluteIndirectLong, 2}, // DC - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // DD + {&CPU::CMP, 4, "cmp", AddressingMode::AbsoluteIndexedByX, 3}, // DD {&CPU::BRK, 7, "dec #-#", AddressingMode::Implied, 2}, // DE - {&CPU::BRK, 7, "cmp #-#", AddressingMode::Implied, 2}, // DF + {&CPU::CMP, 5, "cmp", AddressingMode::AbsoluteIndexedByXLong, 4}, // DF {&CPU::BRK, 7, "cpx #-#", AddressingMode::Implied, 2}, // E0 {&CPU::SBC, 6, "sbc", AddressingMode::DirectPageIndirectIndexedByX, 2}, // E1 {&CPU::SEP, 3, "sep", AddressingMode::Immediate8bits, 2}, // E2 diff --git a/sources/CPU/Instructions/MathematicalOperations.cpp b/sources/CPU/Instructions/MathematicalOperations.cpp index 4872cbb..776a810 100644 --- a/sources/CPU/Instructions/MathematicalOperations.cpp +++ b/sources/CPU/Instructions/MathematicalOperations.cpp @@ -147,4 +147,41 @@ namespace ComSquare::CPU this->_registers.p.n = this->_registers.y & negativeMask; return 0; } + + int CPU::CMP(uint24_t valueAddr, AddressingMode mode) + { + unsigned negativeMask = this->_registers.p.m ? 0x80u : 0x8000u; + unsigned value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + unsigned result = this->_registers.a - value; + if (this->_registers.p.m) + result %= 0x100; + + this->_registers.p.n = result & negativeMask; + this->_registers.p.z = result == 0; + this->_registers.p.c = this->_registers.a >= result; + + int cycles = !this->_registers.p.m; + switch (mode) { + case DirectPage: + case DirectPageIndirect: + case DirectPageIndirectLong: + case DirectPageIndexedByX: + case DirectPageIndirectIndexedByX: + case DirectPageIndirectIndexedByYLong: + cycles += this->_registers.dl != 0; + break; + case AbsoluteIndexedByX: + case AbsoluteIndexedByY: + cycles += this->_hasIndexCrossedPageBoundary; + break; + case DirectPageIndirectIndexedByY: + cycles += this->_registers.dl != 0 + this->_hasIndexCrossedPageBoundary; + break; + default: + break; + } + return cycles; + } } \ No newline at end of file diff --git a/tests/CPU/Math/testCMP.cpp b/tests/CPU/Math/testCMP.cpp new file mode 100644 index 0000000..47968d4 --- /dev/null +++ b/tests/CPU/Math/testCMP.cpp @@ -0,0 +1,73 @@ +// +// Created by anonymus-raccoon on 4/6/20. +// + +#include +#include +#include "../../tests.hpp" +#include "../../../sources/SNES.hpp" +using namespace ComSquare; + +Test(CMP, underflow) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0; + snes.wram->_data[0] = 0x1; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, false, "The carry flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(CMP, zero) +{ + Init() + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.a = 0x5; + snes.wram->_data[0] = 0x5; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} + +Test(CMP, nativeModeZero) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x5000; + snes.wram->_data[0] = 0x00; + snes.wram->_data[1] = 0x50; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, true, "The zero flags should be set."); +} + +Test(CMP, nativeModeNothing) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0x8000; + snes.wram->_data[0] = 0x00; + snes.wram->_data[1] = 0x50; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flags should not be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} + +Test(CMP, negative) +{ + Init() + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.a = 0xB000; + snes.wram->_data[0] = 0x00; + snes.wram->_data[1] = 0x10; + snes.cpu->CMP(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flags should be set."); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flags should not be set."); +} +