diff --git a/juiceboy/include/juiceboy/instructions.hpp b/juiceboy/include/juiceboy/instructions.hpp index 6a2671c..b92b857 100644 --- a/juiceboy/include/juiceboy/instructions.hpp +++ b/juiceboy/include/juiceboy/instructions.hpp @@ -32,6 +32,11 @@ std::uint8_t LdBU8(Processor &processor); std::uint8_t RLCA(Processor &processor); std::uint8_t LdU16Sp(Processor &processor); std::uint8_t AddHlBc(Processor &processor); +std::uint8_t LdABc(Processor &processor); +std::uint8_t DecBc(Processor &processor); +std::uint8_t IncC(Processor &processor); +std::uint8_t DecC(Processor &processor); +std::uint8_t LdCU8(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 948b712..90cb27d 100644 --- a/juiceboy/include/juiceboy/registers.hpp +++ b/juiceboy/include/juiceboy/registers.hpp @@ -34,6 +34,7 @@ public: [[nodiscard]] std::uint8_t GetA() const; [[nodiscard]] std::uint8_t GetF() const; [[nodiscard]] std::uint8_t GetB() const; + [[nodiscard]] std::uint8_t GetC() const; [[nodiscard]] std::uint16_t GetBC() const; [[nodiscard]] std::uint16_t GetHL() const; @@ -41,6 +42,7 @@ public: void SetA(std::uint8_t value); void SetF(std::uint8_t value); void SetB(std::uint8_t value); + void SetC(std::uint8_t value); void SetBC(std::uint16_t value); void SetHL(std::uint16_t value); diff --git a/juiceboy/src/instructions.cpp b/juiceboy/src/instructions.cpp index 82b0d12..862d81c 100644 --- a/juiceboy/src/instructions.cpp +++ b/juiceboy/src/instructions.cpp @@ -18,11 +18,11 @@ Instructions::Instructions() { 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<0x0A>(m_instructions) = &instructions::LdABc; + std::get<0x0B>(m_instructions) = &instructions::DecBc; + std::get<0x0C>(m_instructions) = &instructions::IncC; + std::get<0x0D>(m_instructions) = &instructions::DecC; + std::get<0x0E>(m_instructions) = &instructions::LdCU8; std::get<0x0F>(m_instructions) = &instructions::NotImplemented; std::get<0x10>(m_instructions) = &instructions::NotImplemented; @@ -385,6 +385,51 @@ std::uint8_t AddHlBc(Processor &processor) { return 8; } +std::uint8_t LdABc(Processor &processor) { + const auto address = processor.GetRegisters().GetBC(); + const auto value = processor.GetMemory().ReadByte(address); + processor.GetRegisters().SetA(value); + return 8; +} + +std::uint8_t DecBc(Processor &processor) { + processor.GetRegisters().SetBC(processor.GetRegisters().GetBC() - 1); + return 8; +} + +std::uint8_t IncC(Processor &processor) { + const auto value = processor.GetRegisters().GetC(); + const std::uint8_t result = value + 1; + + processor.GetRegisters().SetC(result); + + processor.GetRegisters().SetFlag(Registers::Flags::Zero, result == 0); + processor.GetRegisters().SetFlag(Registers::Flags::Subtract, false); + processor.GetRegisters().SetFlag(Registers::Flags::HalfCarry, + (value & 0x0F) == 0x0F); + + return 4; +} + +std::uint8_t DecC(Processor &processor) { + const auto value = processor.GetRegisters().GetC(); + const std::uint8_t result = value - 1; + + processor.GetRegisters().SetC(result); + + processor.GetRegisters().SetFlag(Registers::Flags::Zero, result == 0); + processor.GetRegisters().SetFlag(Registers::Flags::Subtract, true); + processor.GetRegisters().SetFlag(Registers::Flags::HalfCarry, + (value & 0x0F) == 0x0); + + return 4; +} + +std::uint8_t LdCU8(Processor &processor) { + processor.GetRegisters().SetC(processor.FetchByte()); + 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 480f421..0ab3d07 100644 --- a/juiceboy/src/registers.cpp +++ b/juiceboy/src/registers.cpp @@ -32,6 +32,7 @@ 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::uint8_t Registers::GetC() const { return m_regC; } std::uint16_t Registers::GetBC() const { return BytesToWord(m_regB, m_regC); } std::uint16_t Registers::GetHL() const { return BytesToWord(m_regH, m_regL); } @@ -39,6 +40,7 @@ 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; } void Registers::SetB(const std::uint8_t value) { m_regB = value; } +void Registers::SetC(const std::uint8_t value) { m_regC = value; } void Registers::SetBC(const std::uint16_t value) { std::tie(m_regB, m_regC) = WordToBytes(value); diff --git a/juiceboy/test/instructions_test.cpp b/juiceboy/test/instructions_test.cpp index 51ea51e..2afc599 100644 --- a/juiceboy/test/instructions_test.cpp +++ b/juiceboy/test/instructions_test.cpp @@ -249,4 +249,114 @@ TEST_F(InstructionsTest, AddHlBcPreservesZeroFlag) { EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Zero)); } +TEST_F(InstructionsTest, LdABc) { + GetMemory().WriteByte(0x8000, 0x12); + GetRegisters().SetBC(0x8000); + + const auto cycles = instructions::LdABc(GetProcessor()); + + EXPECT_EQ(cycles, 8); + EXPECT_EQ(GetRegisters().GetA(), 0x12); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0); +} + +TEST_F(InstructionsTest, DecBC) { + GetRegisters().SetBC(0x2); + + const auto cycles = instructions::DecBc(GetProcessor()); + + EXPECT_EQ(cycles, 8); + EXPECT_EQ(GetRegisters().GetBC(), 0x1); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0); +} + +TEST_F(InstructionsTest, IncC) { + GetRegisters().SetC(0x1); + + const auto cycles = instructions::IncC(GetProcessor()); + + EXPECT_EQ(cycles, 4); + EXPECT_EQ(GetRegisters().GetC(), 0x2); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Zero)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Subtract)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::HalfCarry)); +} + +TEST_F(InstructionsTest, IncCSetsZeroFlag) { + GetRegisters().SetC(0xFF); + + (void)instructions::IncC(GetProcessor()); + + EXPECT_EQ(GetRegisters().GetC(), 0x00); + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Zero)); +} + +TEST_F(InstructionsTest, IncCSetsHalfCarryFlag) { + GetRegisters().SetC(0x0F); + + (void)instructions::IncC(GetProcessor()); + + EXPECT_EQ(GetRegisters().GetC(), 0x10); + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::HalfCarry)); +} + +TEST_F(InstructionsTest, IncCPreservesCarry) { + GetRegisters().SetFlag(Registers::Flags::Carry, true); + + (void)instructions::IncC(GetProcessor()); + + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Carry)); +} + +TEST_F(InstructionsTest, DecC) { + GetRegisters().SetC(0x2); + + const auto cycles = instructions::DecC(GetProcessor()); + + EXPECT_EQ(cycles, 4); + EXPECT_EQ(GetRegisters().GetC(), 0x1); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::Zero)); + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Subtract)); + EXPECT_FALSE(GetRegisters().GetFlag(Registers::Flags::HalfCarry)); +} + +TEST_F(InstructionsTest, DecCSetsZeroFlag) { + GetRegisters().SetC(0x1); + + (void)instructions::DecC(GetProcessor()); + + EXPECT_EQ(GetRegisters().GetC(), 0x00); + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Zero)); +} + +TEST_F(InstructionsTest, DecCSetsHalfCarryFlag) { + GetRegisters().SetC(0x10); + + (void)instructions::DecC(GetProcessor()); + + EXPECT_EQ(GetRegisters().GetC(), 0x0F); + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::HalfCarry)); +} + +TEST_F(InstructionsTest, DecCPreservesCarry) { + GetRegisters().SetFlag(Registers::Flags::Carry, true); + + (void)instructions::IncC(GetProcessor()); + + EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Carry)); +} + +TEST_F(InstructionsTest, LdCU8) { + GetMemory().WriteByte(0x8000, 0x12); + GetRegisters().SetProgramCounter(0x8000); + + const auto cycles = instructions::LdCU8(GetProcessor()); + + EXPECT_EQ(cycles, 8); + EXPECT_EQ(GetRegisters().GetC(), 0x12); + EXPECT_EQ(GetRegisters().GetProgramCounter(), 0x8001); +} + } // namespace juiceboy::test \ No newline at end of file