diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index a4c69be..92cf17e 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -425,6 +425,8 @@ namespace ComSquare::CPU int ASL(uint24_t, AddressingMode); // !@brief Logical Shift Right int LSR(uint24_t, AddressingMode); + // !@brief Rotate Left + int ROL(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -467,15 +469,15 @@ namespace ComSquare::CPU {&CPU::AND, 4, "and", AddressingMode::StackRelative, 2}, // 23 {&CPU::BIT, 3, "bit", AddressingMode::DirectPage, 2}, // 24 {&CPU::AND, 3, "and", AddressingMode::DirectPage, 2}, // 25 - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 26 + {&CPU::ROL, 5, "rol", AddressingMode::DirectPage, 2}, // 26 {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectLong, 2}, // 27 {&CPU::PLP, 4, "plp", AddressingMode::Implied, 1}, // 28 {&CPU::AND, 2, "and", AddressingMode::ImmediateForA, 2}, // 29 - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2A + {&CPU::ROL, 2, "rol", AddressingMode::Implied, 1}, // 2A {&CPU::PLD, 5, "pld", AddressingMode::Implied, 1}, // 2B {&CPU::BIT, 4, "bit", AddressingMode::Absolute, 3}, // 2C {&CPU::AND, 4, "and", AddressingMode::Absolute, 3}, // 2D - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 2E + {&CPU::ROL, 6, "rol", AddressingMode::Absolute, 3}, // 2E {&CPU::AND, 5, "and", AddressingMode::AbsoluteLong, 4}, // 2F {&CPU::BMI, 2, "bmi", AddressingMode::Immediate8bits, 2}, // 30 {&CPU::AND, 5, "and", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 31 @@ -483,7 +485,7 @@ namespace ComSquare::CPU {&CPU::AND, 7, "and", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 33 {&CPU::BIT, 4, "bit", AddressingMode::DirectPageIndexedByX, 2}, // 34 {&CPU::AND, 4, "and", AddressingMode::DirectPageIndexedByX, 2}, // 35 - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 36 + {&CPU::ROL, 6, "rol", AddressingMode::DirectPageIndexedByX, 2}, // 36 {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 37 {&CPU::SEC, 2, "sec", AddressingMode::Implied, 1}, // 38 {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByY, 3}, // 39 @@ -491,7 +493,7 @@ namespace ComSquare::CPU {&CPU::TSC, 2, "tsc", AddressingMode::Implied, 1}, // 3B {&CPU::BIT, 4, "bit", AddressingMode::AbsoluteIndexedByX, 3}, // 3C {&CPU::AND, 4, "and", AddressingMode::AbsoluteIndexedByX, 3}, // 3D - {&CPU::BRK, 7, "rol #-#", AddressingMode::Implied, 2}, // 3E + {&CPU::ROL, 7, "rol", AddressingMode::AbsoluteIndexedByX, 3}, // 3E {&CPU::AND, 5, "and", AddressingMode::AbsoluteIndexedByXLong, 4}, // 3F {&CPU::RTI, 6, "rti", AddressingMode::Implied, 1}, // 40 {&CPU::EOR, 6, "eor", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 41 diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 629655e..db67ab0 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -133,4 +133,47 @@ namespace ComSquare::CPU } return cycles; } + + int CPU::ROL(uint24_t valueAddr, AddressingMode mode) + { + unsigned highByte = this->_registers.p.m ? 0x80u : 0x8000u; + bool oldCarry = this->_registers.p.c; + + if (mode == Implied) { + this->_registers.p.c = this->_registers.a & highByte; + this->_registers.a <<= 1u; + this->_registers.a |= oldCarry; + this->_registers.p.n = this->_registers.a & highByte; + this->_registers.p.z = this->_registers.a == 0; + return 0; + } + + uint16_t value = this->_bus->read(valueAddr); + if (!this->_registers.p.m) + value += this->_bus->read(valueAddr + 1) << 8u; + + this->_registers.p.c = value & highByte; + value <<= 1u; + value |= oldCarry; + this->_registers.p.n = value & highByte; + this->_registers.p.z = value == 0; + + this->_bus->write(valueAddr, value); + if (!this->_registers.p.m) + this->_bus->write(valueAddr + 1, value >> 8u); + + int cycles = 2 * !this->_registers.p.m; + switch (mode) { + case DirectPage: + case DirectPageIndexedByX: + cycles += this->_registers.dl != 0; + break; + case AbsoluteIndexedByX: + cycles += this->_hasIndexCrossedPageBoundary; + break; + default: + break; + } + return cycles; + } } \ No newline at end of file diff --git a/tests/CPU/testBits.cpp b/tests/CPU/testBits.cpp index c21757a..ae59ee6 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -190,4 +190,57 @@ Test(LSR, accumulator) cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROL, emulationTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->ROL(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b01100110, "The data in ram should be 0b01100110 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROL, emulationTestWithCarry) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.c = true; + snes.cpu->ROL(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b01100111, "The data in ram should be 0b01100111 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROL, nativeTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.wram->_data[1] = 0b10000011; + snes.cpu->_registers.p.m = false; + snes.cpu->_registers.p.c = true; + snes.cpu->ROL(0x0, ComSquare::CPU::AddressingMode::Absolute); + cr_assert_eq(snes.wram->_data[0], 0b01100111, "The data in ram should be 0b01100111 but it was %x", snes.wram->_data[0]); + cr_assert_eq(snes.wram->_data[1], 0b00000111, "The data in ram should be 0b00000111 but it was %x", snes.wram->_data[1]); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); +} + +Test(ROL, accumulator) +{ + Init() + snes.cpu->_registers.a = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->_registers.p.c = true; + snes.cpu->ROL(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.al, 0b01100111, "The accumulator should be 0b01100111 but it was %x", snes.cpu->_registers.al); + cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.c, true, "The carry flag should be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); } \ No newline at end of file