diff --git a/CMakePresets.json b/CMakePresets.json index fa326b0..e9242e8 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -21,7 +21,8 @@ "inherits": "base", "generator": "Ninja", "cacheVariables": { - "BUILD_TESTING": "ON" + "BUILD_TESTING": "ON", + "CMAKE_BUILD_TYPE": "Debug" } } ], diff --git a/juiceboy/include/juiceboy/instructions.hpp b/juiceboy/include/juiceboy/instructions.hpp index 73e5b86..6a2671c 100644 --- a/juiceboy/include/juiceboy/instructions.hpp +++ b/juiceboy/include/juiceboy/instructions.hpp @@ -28,6 +28,10 @@ std::uint8_t LdBcA(Processor &processor); std::uint8_t IncBc(Processor &processor); std::uint8_t IncB(Processor &processor); std::uint8_t DecB(Processor &processor); +std::uint8_t LdBU8(Processor &processor); +std::uint8_t RLCA(Processor &processor); +std::uint8_t LdU16Sp(Processor &processor); +std::uint8_t AddHlBc(Processor &processor); } // namespace instructions } // namespace juiceboy \ No newline at end of file diff --git a/juiceboy/include/juiceboy/registers.hpp b/juiceboy/include/juiceboy/registers.hpp index 0d3e988..948b712 100644 --- a/juiceboy/include/juiceboy/registers.hpp +++ b/juiceboy/include/juiceboy/registers.hpp @@ -29,17 +29,21 @@ public: /// @return The current value of the stack pointer. [[nodiscard]] std::uint16_t GetStackPointer() const; + void SetStackPointer(std::uint16_t value); + [[nodiscard]] std::uint8_t GetA() const; [[nodiscard]] std::uint8_t GetF() const; [[nodiscard]] std::uint8_t GetB() const; [[nodiscard]] std::uint16_t GetBC() const; + [[nodiscard]] std::uint16_t GetHL() const; void SetA(std::uint8_t value); void SetF(std::uint8_t value); void SetB(std::uint8_t value); void SetBC(std::uint16_t value); + void SetHL(std::uint16_t value); /// @brief Get the current value of the given flag. /// @return True if the given flag bit is set, false otherwise. diff --git a/juiceboy/src/instructions.cpp b/juiceboy/src/instructions.cpp index bdceb31..82b0d12 100644 --- a/juiceboy/src/instructions.cpp +++ b/juiceboy/src/instructions.cpp @@ -8,15 +8,277 @@ namespace juiceboy { Instructions::Instructions() { - // For now, all not implemented instructions should throw. - m_instructions.fill(&instructions::NotImplemented); + std::get<0x00>(m_instructions) = &instructions::Nop; + std::get<0x01>(m_instructions) = &instructions::LdBcD16; + std::get<0x02>(m_instructions) = &instructions::LdBcA; + std::get<0x03>(m_instructions) = &instructions::IncBc; + std::get<0x04>(m_instructions) = &instructions::IncB; + std::get<0x05>(m_instructions) = &instructions::DecB; + std::get<0x06>(m_instructions) = &instructions::LdBU8; + std::get<0x07>(m_instructions) = &instructions::RLCA; + std::get<0x08>(m_instructions) = &instructions::LdU16Sp; + std::get<0x09>(m_instructions) = &instructions::AddHlBc; + std::get<0x0A>(m_instructions) = &instructions::NotImplemented; + std::get<0x0B>(m_instructions) = &instructions::NotImplemented; + std::get<0x0C>(m_instructions) = &instructions::NotImplemented; + std::get<0x0D>(m_instructions) = &instructions::NotImplemented; + std::get<0x0E>(m_instructions) = &instructions::NotImplemented; + std::get<0x0F>(m_instructions) = &instructions::NotImplemented; - std::get<0x0>(m_instructions) = &instructions::Nop; - std::get<0x1>(m_instructions) = &instructions::LdBcD16; - std::get<0x2>(m_instructions) = &instructions::LdBcA; - std::get<0x3>(m_instructions) = &instructions::IncBc; - std::get<0x4>(m_instructions) = &instructions::IncB; - std::get<0x5>(m_instructions) = &instructions::DecB; + std::get<0x10>(m_instructions) = &instructions::NotImplemented; + std::get<0x11>(m_instructions) = &instructions::NotImplemented; + std::get<0x12>(m_instructions) = &instructions::NotImplemented; + std::get<0x13>(m_instructions) = &instructions::NotImplemented; + std::get<0x14>(m_instructions) = &instructions::NotImplemented; + std::get<0x15>(m_instructions) = &instructions::NotImplemented; + std::get<0x16>(m_instructions) = &instructions::NotImplemented; + std::get<0x17>(m_instructions) = &instructions::NotImplemented; + std::get<0x18>(m_instructions) = &instructions::NotImplemented; + std::get<0x19>(m_instructions) = &instructions::NotImplemented; + std::get<0x1A>(m_instructions) = &instructions::NotImplemented; + std::get<0x1B>(m_instructions) = &instructions::NotImplemented; + std::get<0x1C>(m_instructions) = &instructions::NotImplemented; + std::get<0x1D>(m_instructions) = &instructions::NotImplemented; + std::get<0x1E>(m_instructions) = &instructions::NotImplemented; + std::get<0x1F>(m_instructions) = &instructions::NotImplemented; + + std::get<0x20>(m_instructions) = &instructions::NotImplemented; + std::get<0x21>(m_instructions) = &instructions::NotImplemented; + std::get<0x22>(m_instructions) = &instructions::NotImplemented; + std::get<0x23>(m_instructions) = &instructions::NotImplemented; + std::get<0x24>(m_instructions) = &instructions::NotImplemented; + std::get<0x25>(m_instructions) = &instructions::NotImplemented; + std::get<0x26>(m_instructions) = &instructions::NotImplemented; + std::get<0x27>(m_instructions) = &instructions::NotImplemented; + std::get<0x28>(m_instructions) = &instructions::NotImplemented; + std::get<0x29>(m_instructions) = &instructions::NotImplemented; + std::get<0x2A>(m_instructions) = &instructions::NotImplemented; + std::get<0x2B>(m_instructions) = &instructions::NotImplemented; + std::get<0x2C>(m_instructions) = &instructions::NotImplemented; + std::get<0x2D>(m_instructions) = &instructions::NotImplemented; + std::get<0x2E>(m_instructions) = &instructions::NotImplemented; + std::get<0x2F>(m_instructions) = &instructions::NotImplemented; + + std::get<0x30>(m_instructions) = &instructions::NotImplemented; + std::get<0x31>(m_instructions) = &instructions::NotImplemented; + std::get<0x32>(m_instructions) = &instructions::NotImplemented; + std::get<0x33>(m_instructions) = &instructions::NotImplemented; + std::get<0x34>(m_instructions) = &instructions::NotImplemented; + std::get<0x35>(m_instructions) = &instructions::NotImplemented; + std::get<0x36>(m_instructions) = &instructions::NotImplemented; + std::get<0x37>(m_instructions) = &instructions::NotImplemented; + std::get<0x38>(m_instructions) = &instructions::NotImplemented; + std::get<0x39>(m_instructions) = &instructions::NotImplemented; + std::get<0x3A>(m_instructions) = &instructions::NotImplemented; + std::get<0x3B>(m_instructions) = &instructions::NotImplemented; + std::get<0x3C>(m_instructions) = &instructions::NotImplemented; + std::get<0x3D>(m_instructions) = &instructions::NotImplemented; + std::get<0x3E>(m_instructions) = &instructions::NotImplemented; + std::get<0x3F>(m_instructions) = &instructions::NotImplemented; + + std::get<0x40>(m_instructions) = &instructions::NotImplemented; + std::get<0x41>(m_instructions) = &instructions::NotImplemented; + std::get<0x42>(m_instructions) = &instructions::NotImplemented; + std::get<0x43>(m_instructions) = &instructions::NotImplemented; + std::get<0x44>(m_instructions) = &instructions::NotImplemented; + std::get<0x45>(m_instructions) = &instructions::NotImplemented; + std::get<0x46>(m_instructions) = &instructions::NotImplemented; + std::get<0x47>(m_instructions) = &instructions::NotImplemented; + std::get<0x48>(m_instructions) = &instructions::NotImplemented; + std::get<0x49>(m_instructions) = &instructions::NotImplemented; + std::get<0x4A>(m_instructions) = &instructions::NotImplemented; + std::get<0x4B>(m_instructions) = &instructions::NotImplemented; + std::get<0x4C>(m_instructions) = &instructions::NotImplemented; + std::get<0x4D>(m_instructions) = &instructions::NotImplemented; + std::get<0x4E>(m_instructions) = &instructions::NotImplemented; + std::get<0x4F>(m_instructions) = &instructions::NotImplemented; + + std::get<0x50>(m_instructions) = &instructions::NotImplemented; + std::get<0x51>(m_instructions) = &instructions::NotImplemented; + std::get<0x52>(m_instructions) = &instructions::NotImplemented; + std::get<0x53>(m_instructions) = &instructions::NotImplemented; + std::get<0x54>(m_instructions) = &instructions::NotImplemented; + std::get<0x55>(m_instructions) = &instructions::NotImplemented; + std::get<0x56>(m_instructions) = &instructions::NotImplemented; + std::get<0x57>(m_instructions) = &instructions::NotImplemented; + std::get<0x58>(m_instructions) = &instructions::NotImplemented; + std::get<0x59>(m_instructions) = &instructions::NotImplemented; + std::get<0x5A>(m_instructions) = &instructions::NotImplemented; + std::get<0x5B>(m_instructions) = &instructions::NotImplemented; + std::get<0x5C>(m_instructions) = &instructions::NotImplemented; + std::get<0x5D>(m_instructions) = &instructions::NotImplemented; + std::get<0x5E>(m_instructions) = &instructions::NotImplemented; + std::get<0x5F>(m_instructions) = &instructions::NotImplemented; + + std::get<0x60>(m_instructions) = &instructions::NotImplemented; + std::get<0x61>(m_instructions) = &instructions::NotImplemented; + std::get<0x62>(m_instructions) = &instructions::NotImplemented; + std::get<0x63>(m_instructions) = &instructions::NotImplemented; + std::get<0x64>(m_instructions) = &instructions::NotImplemented; + std::get<0x65>(m_instructions) = &instructions::NotImplemented; + std::get<0x66>(m_instructions) = &instructions::NotImplemented; + std::get<0x67>(m_instructions) = &instructions::NotImplemented; + std::get<0x68>(m_instructions) = &instructions::NotImplemented; + std::get<0x69>(m_instructions) = &instructions::NotImplemented; + std::get<0x6A>(m_instructions) = &instructions::NotImplemented; + std::get<0x6B>(m_instructions) = &instructions::NotImplemented; + std::get<0x6C>(m_instructions) = &instructions::NotImplemented; + std::get<0x6D>(m_instructions) = &instructions::NotImplemented; + std::get<0x6E>(m_instructions) = &instructions::NotImplemented; + std::get<0x6F>(m_instructions) = &instructions::NotImplemented; + + std::get<0x70>(m_instructions) = &instructions::NotImplemented; + std::get<0x71>(m_instructions) = &instructions::NotImplemented; + std::get<0x72>(m_instructions) = &instructions::NotImplemented; + std::get<0x73>(m_instructions) = &instructions::NotImplemented; + std::get<0x74>(m_instructions) = &instructions::NotImplemented; + std::get<0x75>(m_instructions) = &instructions::NotImplemented; + std::get<0x76>(m_instructions) = &instructions::NotImplemented; + std::get<0x77>(m_instructions) = &instructions::NotImplemented; + std::get<0x78>(m_instructions) = &instructions::NotImplemented; + std::get<0x79>(m_instructions) = &instructions::NotImplemented; + std::get<0x7A>(m_instructions) = &instructions::NotImplemented; + std::get<0x7B>(m_instructions) = &instructions::NotImplemented; + std::get<0x7C>(m_instructions) = &instructions::NotImplemented; + std::get<0x7D>(m_instructions) = &instructions::NotImplemented; + std::get<0x7E>(m_instructions) = &instructions::NotImplemented; + std::get<0x7F>(m_instructions) = &instructions::NotImplemented; + + std::get<0x80>(m_instructions) = &instructions::NotImplemented; + std::get<0x81>(m_instructions) = &instructions::NotImplemented; + std::get<0x82>(m_instructions) = &instructions::NotImplemented; + std::get<0x83>(m_instructions) = &instructions::NotImplemented; + std::get<0x84>(m_instructions) = &instructions::NotImplemented; + std::get<0x85>(m_instructions) = &instructions::NotImplemented; + std::get<0x86>(m_instructions) = &instructions::NotImplemented; + std::get<0x87>(m_instructions) = &instructions::NotImplemented; + std::get<0x88>(m_instructions) = &instructions::NotImplemented; + std::get<0x89>(m_instructions) = &instructions::NotImplemented; + std::get<0x8A>(m_instructions) = &instructions::NotImplemented; + std::get<0x8B>(m_instructions) = &instructions::NotImplemented; + std::get<0x8C>(m_instructions) = &instructions::NotImplemented; + std::get<0x8D>(m_instructions) = &instructions::NotImplemented; + std::get<0x8E>(m_instructions) = &instructions::NotImplemented; + std::get<0x8F>(m_instructions) = &instructions::NotImplemented; + + std::get<0x90>(m_instructions) = &instructions::NotImplemented; + std::get<0x91>(m_instructions) = &instructions::NotImplemented; + std::get<0x92>(m_instructions) = &instructions::NotImplemented; + std::get<0x93>(m_instructions) = &instructions::NotImplemented; + std::get<0x94>(m_instructions) = &instructions::NotImplemented; + std::get<0x95>(m_instructions) = &instructions::NotImplemented; + std::get<0x96>(m_instructions) = &instructions::NotImplemented; + std::get<0x97>(m_instructions) = &instructions::NotImplemented; + std::get<0x98>(m_instructions) = &instructions::NotImplemented; + std::get<0x99>(m_instructions) = &instructions::NotImplemented; + std::get<0x9A>(m_instructions) = &instructions::NotImplemented; + std::get<0x9B>(m_instructions) = &instructions::NotImplemented; + std::get<0x9C>(m_instructions) = &instructions::NotImplemented; + std::get<0x9D>(m_instructions) = &instructions::NotImplemented; + std::get<0x9E>(m_instructions) = &instructions::NotImplemented; + std::get<0x9F>(m_instructions) = &instructions::NotImplemented; + + std::get<0xA0>(m_instructions) = &instructions::NotImplemented; + std::get<0xA1>(m_instructions) = &instructions::NotImplemented; + std::get<0xA2>(m_instructions) = &instructions::NotImplemented; + std::get<0xA3>(m_instructions) = &instructions::NotImplemented; + std::get<0xA4>(m_instructions) = &instructions::NotImplemented; + std::get<0xA5>(m_instructions) = &instructions::NotImplemented; + std::get<0xA6>(m_instructions) = &instructions::NotImplemented; + std::get<0xA7>(m_instructions) = &instructions::NotImplemented; + std::get<0xA8>(m_instructions) = &instructions::NotImplemented; + std::get<0xA9>(m_instructions) = &instructions::NotImplemented; + std::get<0xAA>(m_instructions) = &instructions::NotImplemented; + std::get<0xAB>(m_instructions) = &instructions::NotImplemented; + std::get<0xAC>(m_instructions) = &instructions::NotImplemented; + std::get<0xAD>(m_instructions) = &instructions::NotImplemented; + std::get<0xAE>(m_instructions) = &instructions::NotImplemented; + std::get<0xAF>(m_instructions) = &instructions::NotImplemented; + + std::get<0xB0>(m_instructions) = &instructions::NotImplemented; + std::get<0xB1>(m_instructions) = &instructions::NotImplemented; + std::get<0xB2>(m_instructions) = &instructions::NotImplemented; + std::get<0xB3>(m_instructions) = &instructions::NotImplemented; + std::get<0xB4>(m_instructions) = &instructions::NotImplemented; + std::get<0xB5>(m_instructions) = &instructions::NotImplemented; + std::get<0xB6>(m_instructions) = &instructions::NotImplemented; + std::get<0xB7>(m_instructions) = &instructions::NotImplemented; + std::get<0xB8>(m_instructions) = &instructions::NotImplemented; + std::get<0xB9>(m_instructions) = &instructions::NotImplemented; + std::get<0xBA>(m_instructions) = &instructions::NotImplemented; + std::get<0xBB>(m_instructions) = &instructions::NotImplemented; + std::get<0xBC>(m_instructions) = &instructions::NotImplemented; + std::get<0xBD>(m_instructions) = &instructions::NotImplemented; + std::get<0xBE>(m_instructions) = &instructions::NotImplemented; + std::get<0xBF>(m_instructions) = &instructions::NotImplemented; + + std::get<0xC0>(m_instructions) = &instructions::NotImplemented; + std::get<0xC1>(m_instructions) = &instructions::NotImplemented; + std::get<0xC2>(m_instructions) = &instructions::NotImplemented; + std::get<0xC3>(m_instructions) = &instructions::NotImplemented; + std::get<0xC4>(m_instructions) = &instructions::NotImplemented; + std::get<0xC5>(m_instructions) = &instructions::NotImplemented; + std::get<0xC6>(m_instructions) = &instructions::NotImplemented; + std::get<0xC7>(m_instructions) = &instructions::NotImplemented; + std::get<0xC8>(m_instructions) = &instructions::NotImplemented; + std::get<0xC9>(m_instructions) = &instructions::NotImplemented; + std::get<0xCA>(m_instructions) = &instructions::NotImplemented; + std::get<0xCB>(m_instructions) = &instructions::NotImplemented; + std::get<0xCC>(m_instructions) = &instructions::NotImplemented; + std::get<0xCD>(m_instructions) = &instructions::NotImplemented; + std::get<0xCE>(m_instructions) = &instructions::NotImplemented; + std::get<0xCF>(m_instructions) = &instructions::NotImplemented; + + std::get<0xD0>(m_instructions) = &instructions::NotImplemented; + std::get<0xD1>(m_instructions) = &instructions::NotImplemented; + std::get<0xD2>(m_instructions) = &instructions::NotImplemented; + std::get<0xD3>(m_instructions) = &instructions::NotImplemented; + std::get<0xD4>(m_instructions) = &instructions::NotImplemented; + std::get<0xD5>(m_instructions) = &instructions::NotImplemented; + std::get<0xD6>(m_instructions) = &instructions::NotImplemented; + std::get<0xD7>(m_instructions) = &instructions::NotImplemented; + std::get<0xD8>(m_instructions) = &instructions::NotImplemented; + std::get<0xD9>(m_instructions) = &instructions::NotImplemented; + std::get<0xDA>(m_instructions) = &instructions::NotImplemented; + std::get<0xDB>(m_instructions) = &instructions::NotImplemented; + std::get<0xDC>(m_instructions) = &instructions::NotImplemented; + std::get<0xDD>(m_instructions) = &instructions::NotImplemented; + std::get<0xDE>(m_instructions) = &instructions::NotImplemented; + std::get<0xDF>(m_instructions) = &instructions::NotImplemented; + + std::get<0xE0>(m_instructions) = &instructions::NotImplemented; + std::get<0xE1>(m_instructions) = &instructions::NotImplemented; + std::get<0xE2>(m_instructions) = &instructions::NotImplemented; + std::get<0xE3>(m_instructions) = &instructions::NotImplemented; + std::get<0xE4>(m_instructions) = &instructions::NotImplemented; + std::get<0xE5>(m_instructions) = &instructions::NotImplemented; + std::get<0xE6>(m_instructions) = &instructions::NotImplemented; + std::get<0xE7>(m_instructions) = &instructions::NotImplemented; + std::get<0xE8>(m_instructions) = &instructions::NotImplemented; + std::get<0xE9>(m_instructions) = &instructions::NotImplemented; + std::get<0xEA>(m_instructions) = &instructions::NotImplemented; + std::get<0xEB>(m_instructions) = &instructions::NotImplemented; + std::get<0xEC>(m_instructions) = &instructions::NotImplemented; + std::get<0xED>(m_instructions) = &instructions::NotImplemented; + std::get<0xEE>(m_instructions) = &instructions::NotImplemented; + std::get<0xEF>(m_instructions) = &instructions::NotImplemented; + + std::get<0xF0>(m_instructions) = &instructions::NotImplemented; + std::get<0xF1>(m_instructions) = &instructions::NotImplemented; + std::get<0xF2>(m_instructions) = &instructions::NotImplemented; + std::get<0xF3>(m_instructions) = &instructions::NotImplemented; + std::get<0xF4>(m_instructions) = &instructions::NotImplemented; + std::get<0xF5>(m_instructions) = &instructions::NotImplemented; + std::get<0xF6>(m_instructions) = &instructions::NotImplemented; + std::get<0xF7>(m_instructions) = &instructions::NotImplemented; + std::get<0xF8>(m_instructions) = &instructions::NotImplemented; + std::get<0xF9>(m_instructions) = &instructions::NotImplemented; + std::get<0xFA>(m_instructions) = &instructions::NotImplemented; + std::get<0xFB>(m_instructions) = &instructions::NotImplemented; + std::get<0xFC>(m_instructions) = &instructions::NotImplemented; + std::get<0xFD>(m_instructions) = &instructions::NotImplemented; + std::get<0xFE>(m_instructions) = &instructions::NotImplemented; + std::get<0xFF>(m_instructions) = &instructions::NotImplemented; } std::uint8_t Instructions::ExecuteInstruction(const std::uint8_t opcode, @@ -77,6 +339,52 @@ std::uint8_t DecB(Processor &processor) { return 4; } +std::uint8_t LdBU8(Processor &processor) { + processor.GetRegisters().SetB(processor.FetchByte()); + return 4; +} + +std::uint8_t RLCA(Processor &processor) { + auto value = processor.GetRegisters().GetA(); + const auto carry = (value & 0x80) != 0; + + value = static_cast(value << 1); + value |= carry ? 1 : 0; + + processor.GetRegisters().SetA(value); + + processor.GetRegisters().SetFlag(Registers::Flags::Zero, false); + processor.GetRegisters().SetFlag(Registers::Flags::Subtract, false); + processor.GetRegisters().SetFlag(Registers::Flags::HalfCarry, false); + processor.GetRegisters().SetFlag(Registers::Flags::Carry, carry); + return 4; +} + +std::uint8_t LdU16Sp(Processor &processor) { + const auto address = processor.FetchWord(); + const auto value = processor.GetRegisters().GetStackPointer(); + + processor.GetMemory().WriteByte(address + 0, value & 0xFF); + processor.GetMemory().WriteByte(address + 1, value >> 8); + + return 20; +} + +std::uint8_t AddHlBc(Processor &processor) { + + const auto hl = processor.GetRegisters().GetHL(); + const auto bc = processor.GetRegisters().GetBC(); + const auto result = static_cast(hl + bc); + + processor.GetRegisters().SetHL(result); + + processor.GetRegisters().SetFlag(Registers::Flags::Subtract, false); + processor.GetRegisters().SetFlag(Registers::Flags::HalfCarry, + ((hl & 0x0FFF) + (bc & 0x0FFF)) > 0x0FFF); + processor.GetRegisters().SetFlag(Registers::Flags::Carry, hl + bc > 0xFFFF); + return 8; +} + } // namespace instructions } // namespace juiceboy \ No newline at end of file diff --git a/juiceboy/src/registers.cpp b/juiceboy/src/registers.cpp index c572806..480f421 100644 --- a/juiceboy/src/registers.cpp +++ b/juiceboy/src/registers.cpp @@ -27,11 +27,14 @@ void Registers::IncrementProgramCounter(const std::uint16_t value) { std::uint16_t Registers::GetStackPointer() const { return m_sp; } +void Registers::SetStackPointer(const std::uint16_t value) { m_sp = value; } + std::uint8_t Registers::GetA() const { return m_regA; } std::uint8_t Registers::GetF() const { return m_regF; } std::uint8_t Registers::GetB() const { return m_regB; } std::uint16_t Registers::GetBC() const { return BytesToWord(m_regB, m_regC); } +std::uint16_t Registers::GetHL() const { return BytesToWord(m_regH, m_regL); } void Registers::SetA(const std::uint8_t value) { m_regA = value; } void Registers::SetF(const std::uint8_t value) { m_regF = value; } @@ -40,6 +43,9 @@ void Registers::SetB(const std::uint8_t value) { m_regB = value; } void Registers::SetBC(const std::uint16_t value) { std::tie(m_regB, m_regC) = WordToBytes(value); } +void Registers::SetHL(const std::uint16_t value) { + std::tie(m_regH, m_regL) = WordToBytes(value); +} bool Registers::GetFlag(Flags flag) const { return (m_regF & static_cast(flag)) != 0; diff --git a/juiceboy/test/instructions_test.cpp b/juiceboy/test/instructions_test.cpp index 98d7dad..51ea51e 100644 --- a/juiceboy/test/instructions_test.cpp +++ b/juiceboy/test/instructions_test.cpp @@ -146,4 +146,107 @@ TEST_F(InstructionsTest, DecBPreservesCarry) { EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Carry)); } +TEST_F(InstructionsTest, LdBU8) { + GetMemory().WriteByte(0x8000, 0x1); + GetRegisters().SetProgramCounter(0x8000); + + const auto cycles = instructions::LdBU8(GetProcessor()); + + EXPECT_EQ(cycles, 4); + EXPECT_EQ(GetRegisters().GetB(), 0x1); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0x8001); +} + +TEST_F(InstructionsTest, RCLA) { + GetRegisters().SetA(0b11110000); + + const auto cycles = instructions::RLCA(GetProcessor()); + + EXPECT_EQ(cycles, 4); + EXPECT_EQ(GetRegisters().GetA(), 0b11100001); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0); + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Carry)); +} + +TEST_F(InstructionsTest, RCLAClearsFlags) { + GetRegisters().SetA(0b01111111); + GetRegisters().SetFlag(Registers::Flags::Zero, true); + GetRegisters().SetFlag(Registers::Flags::Subtract, true); + GetRegisters().SetFlag(Registers::Flags::HalfCarry, true); + + (void)instructions::RLCA(GetProcessor()); + + EXPECT_EQ(GetRegisters().GetA(), 0b11111110); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Carry)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Zero)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Subtract)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::HalfCarry)); +} + +TEST_F(InstructionsTest, LdU16Sp) { + GetRegisters().SetStackPointer(0x1234); + GetRegisters().SetProgramCounter(0x8000); + GetMemory().WriteByte(0x8000, 0x02); + GetMemory().WriteByte(0x8001, 0x80); + + const auto cycles = instructions::LdU16Sp(GetProcessor()); + + EXPECT_EQ(cycles, 20); + EXPECT_EQ(GetMemory().ReadByte(0x8002), 0x34); + EXPECT_EQ(GetMemory().ReadByte(0x8003), 0x12); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0x8002); +} + +TEST_F(InstructionsTest, AddHlBc) { + GetRegisters().SetBC(0x1234); + GetRegisters().SetHL(0x4321); + + const auto cycles = instructions::AddHlBc(GetProcessor()); + + EXPECT_EQ(cycles, 8); + EXPECT_EQ(GetRegisters().GetBC(), 0x1234); + EXPECT_EQ(GetRegisters().GetHL(), 0x5555); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Subtract)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Zero)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Carry)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::HalfCarry)); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0); +} + +TEST_F(InstructionsTest, AddHlBcSetsCarryFlag) { + GetRegisters().SetBC(0xFFFF); + GetRegisters().SetHL(0x1); + + (void)instructions::AddHlBc(GetProcessor()); + + EXPECT_EQ(GetRegisters().GetHL(), 0x0); + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Carry)); +} + +TEST_F(InstructionsTest, AddHlBcSetsHalfCarryFlag) { + GetRegisters().SetBC(0x0FFF); + GetRegisters().SetHL(0x1); + + (void)instructions::AddHlBc(GetProcessor()); + + EXPECT_EQ(GetRegisters().GetHL(), 0x1000); + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::HalfCarry)); +} + +TEST_F(InstructionsTest, AddHlBcClearsSubtractFlag) { + GetRegisters().SetFlag(Registers::Flags::Subtract, true); + + (void)instructions::AddHlBc(GetProcessor()); + + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Subtract)); +} + +TEST_F(InstructionsTest, AddHlBcPreservesZeroFlag) { + GetRegisters().SetFlag(Registers::Flags::Zero, true); + + (void)instructions::AddHlBc(GetProcessor()); + + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Zero)); +} + } // namespace juiceboy::test \ No newline at end of file