diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 1bbe317..a65944c 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -421,6 +421,8 @@ namespace ComSquare::CPU int XBA(uint24_t, AddressingMode); //! @brief Test Memory Bits against Accumulator int BIT(uint24_t, AddressingMode); + //! @brief Arithmetic Shift Left + int ASL(uint24_t, AddressingMode); //! @brief All the instructions of the CPU. //! @info Instructions are indexed by their opcode @@ -431,15 +433,15 @@ namespace ComSquare::CPU {&CPU::ORA, 4, "ora", AddressingMode::StackRelative, 2}, // 03 {&CPU::TSB, 5, "tsb", AddressingMode::DirectPage, 2}, // 04 {&CPU::ORA, 3, "ora", AddressingMode::DirectPage, 2}, // 05 - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 06 + {&CPU::ASL, 5, "asl", AddressingMode::DirectPage, 2}, // 06 {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectLong, 2}, // 07 {&CPU::PHP, 3, "php", AddressingMode::Implied, 3}, // 08 {&CPU::ORA, 2, "ora", AddressingMode::ImmediateForA, 2}, // 09 - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0A + {&CPU::ASL, 2, "asl", AddressingMode::Implied, 1}, // 0A {&CPU::PHD, 4, "phd", AddressingMode::Implied, 1}, // 0B {&CPU::TSB, 6, "tsb", AddressingMode::Absolute, 3}, // 0C {&CPU::ORA, 3, "ora", AddressingMode::Absolute, 4}, // 0D - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 0E + {&CPU::ASL, 6, "asl", AddressingMode::Absolute, 3}, // 0E {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteLong, 5}, // 0F {&CPU::BPL, 7, "bpl", AddressingMode::Immediate8bits, 2}, // 10 {&CPU::ORA, 5, "ora", AddressingMode::DirectPageIndirectIndexedByY, 2}, // 11 @@ -447,7 +449,7 @@ namespace ComSquare::CPU {&CPU::ORA, 7, "ora", AddressingMode::StackRelativeIndirectIndexedByY, 2}, // 13 {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 14 {&CPU::ORA, 4, "ora", AddressingMode::DirectPageIndexedByX, 2}, // 15 - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 16 + {&CPU::ASL, 6, "asl", AddressingMode::DirectPageIndexedByX, 2}, // 16 {&CPU::ORA, 6, "ora", AddressingMode::DirectPageIndirectIndexedByYLong, 2}, // 17 {&CPU::CLC, 2, "clc", AddressingMode::Implied, 1}, // 18 {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByY, 3}, // 19 @@ -455,7 +457,7 @@ namespace ComSquare::CPU {&CPU::TCS, 2, "tcs", AddressingMode::Implied, 1}, // 1B {&CPU::BRK, 7, "trb #-#", AddressingMode::Implied, 2}, // 1C {&CPU::ORA, 4, "ora", AddressingMode::AbsoluteIndexedByX, 3}, // 1D - {&CPU::BRK, 7, "asl #-#", AddressingMode::Implied, 2}, // 1E + {&CPU::ASL, 7, "asl", AddressingMode::AbsoluteIndexedByX, 3}, // 1E {&CPU::ORA, 5, "ora", AddressingMode::AbsoluteIndexedByXLong, 4}, // 1F {&CPU::JSR, 6, "jsr", AddressingMode::Absolute, 3}, // 20 {&CPU::AND, 6, "and", AddressingMode::DirectPageIndirectIndexedByX, 2}, // 21 diff --git a/sources/CPU/Instructions/BitsInstructions.cpp b/sources/CPU/Instructions/BitsInstructions.cpp index 41848f6..7ad9f83 100644 --- a/sources/CPU/Instructions/BitsInstructions.cpp +++ b/sources/CPU/Instructions/BitsInstructions.cpp @@ -55,4 +55,44 @@ namespace ComSquare::CPU } return cycles; } + + int CPU::ASL(uint24_t valueAddr, AddressingMode mode) + { + unsigned highByte = this->_registers.p.m ? 0x80u : 0x8000u; + + if (mode == Implied) { + this->_registers.a <<= 1u; + 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; + this->_bus->write(valueAddr, value); + if (!this->_registers.p.m) + this->_bus->write(valueAddr + 1, value >> 8u); + + this->_registers.p.n = value & highByte; + this->_registers.p.z = value == 0; + + 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 9e8e5dd..1e94a0a 100644 --- a/tests/CPU/testBits.cpp +++ b/tests/CPU/testBits.cpp @@ -114,4 +114,42 @@ Test(BIT, other) cr_assert_eq(snes.cpu->_registers.p.z, false, "The zero flag should not be set."); cr_assert_eq(snes.cpu->_registers.p.v, true, "The overflow flag should be set."); cr_assert_eq(snes.cpu->_registers.p.n, true, "The negative flag should be set."); +} + +Test(ASL, emulationTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->ASL(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(ASL, nativeTest) +{ + Init() + snes.wram->_data[0] = 0b10110011; + snes.wram->_data[1] = 0b10000011; + snes.cpu->_registers.p.m = false; + snes.cpu->ASL(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.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(ASL, accumulator) +{ + Init() + snes.cpu->_registers.a = 0b10110011; + snes.cpu->_registers.p.m = true; + snes.cpu->ASL(0x0, ComSquare::CPU::AddressingMode::Implied); + cr_assert_eq(snes.cpu->_registers.al, 0b01100110, "The accumulator should be 0b01100110 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, false, "The carry flag should not be set."); + cr_assert_eq(snes.cpu->_registers.p.n, false, "The negative flag should not be set."); } \ No newline at end of file