Compare commits

...
4 Commits
Author SHA1 Message Date
thom 90de24a6ba Added instructions: LdABc, DecBc, IncC, DecC, LdCU8 2026-09-20 22:12:38 +02:00
thom d0f6bb4e76 Added instructions: LdBU8, RLCA, LdU16Sp and AddHlBc 2026-09-19 22:11:46 +02:00
thom 7fafc060f5 IncBC IncB DecB instructions 2026-09-19 16:33:18 +02:00
thom b6e949ac4c Enable bunch of warnings 2026-09-19 16:04:57 +02:00
7 changed files with 775 additions and 24 deletions
+4 -2
View File
@@ -11,7 +11,8 @@
"CMAKE_CXX_STANDARD": "23", "CMAKE_CXX_STANDARD": "23",
"CMAKE_CXX_STANDARD_REQUIRED": "YES", "CMAKE_CXX_STANDARD_REQUIRED": "YES",
"CMAKE_CXX_EXTENSIONS": "NO", "CMAKE_CXX_EXTENSIONS": "NO",
"CMAKE_CXX_VISIBILITY_PRESET": "hidden" "CMAKE_CXX_VISIBILITY_PRESET": "hidden",
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -Wconversion"
} }
}, },
{ {
@@ -20,7 +21,8 @@
"inherits": "base", "inherits": "base",
"generator": "Ninja", "generator": "Ninja",
"cacheVariables": { "cacheVariables": {
"BUILD_TESTING": "ON" "BUILD_TESTING": "ON",
"CMAKE_BUILD_TYPE": "Debug"
} }
} }
], ],
@@ -25,6 +25,18 @@ std::uint8_t NotImplemented(Processor &processor);
std::uint8_t Nop(Processor &processor); std::uint8_t Nop(Processor &processor);
std::uint8_t LdBcD16(Processor &processor); std::uint8_t LdBcD16(Processor &processor);
std::uint8_t LdBcA(Processor &processor); 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);
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 instructions
} // namespace juiceboy } // namespace juiceboy
+19 -2
View File
@@ -6,7 +6,12 @@ namespace juiceboy {
class Registers { class Registers {
public: public:
/// @brief Flags in the flags (af) register. /// @brief Flags in the flags (af) register.
enum class Flags { Zero, Subtract, HalfCarry, Carry }; enum class Flags : std::uint8_t {
Zero = 0x80,
Subtract = 0x40,
HalfCarry = 0x20,
Carry = 0x10
};
/// @brief Get the current program counter. /// @brief Get the current program counter.
/// @return The current value of the program counter. /// @return The current value of the program counter.
@@ -24,17 +29,29 @@ public:
/// @return The current value of the stack pointer. /// @return The current value of the stack pointer.
[[nodiscard]] std::uint16_t GetStackPointer() const; [[nodiscard]] std::uint16_t GetStackPointer() const;
void SetStackPointer(std::uint16_t value);
[[nodiscard]] std::uint8_t GetA() const; [[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 GetBC() const;
[[nodiscard]] std::uint16_t GetHL() const;
void SetA(std::uint8_t value); 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 SetBC(std::uint16_t value);
void SetHL(std::uint16_t value);
/// @brief Get the current value of the given flag. /// @brief Get the current value of the given flag.
/// @return True if the given flag bit is set, false otherwise. /// @return True if the given flag bit is set, false otherwise.
// [[nodiscard]] bool GetFlag(Flags flag) const; [[nodiscard]] bool GetFlag(Flags flag) const;
void SetFlag(Flags flag, bool value);
private: private:
std::uint8_t m_regA{}; std::uint8_t m_regA{};
+394 -5
View File
@@ -8,12 +8,277 @@
namespace juiceboy { namespace juiceboy {
Instructions::Instructions() { Instructions::Instructions() {
// For now, all not implemented instructions should throw. std::get<0x00>(m_instructions) = &instructions::Nop;
m_instructions.fill(&instructions::NotImplemented); 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::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<0>(m_instructions) = &instructions::Nop; std::get<0x10>(m_instructions) = &instructions::NotImplemented;
std::get<1>(m_instructions) = &instructions::LdBcD16; std::get<0x11>(m_instructions) = &instructions::NotImplemented;
std::get<2>(m_instructions) = &instructions::LdBcA; 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, std::uint8_t Instructions::ExecuteInstruction(const std::uint8_t opcode,
@@ -41,6 +306,130 @@ std::uint8_t LdBcA(Processor &processor) {
return 8; return 8;
} }
std::uint8_t IncBc(Processor &processor) {
processor.GetRegisters().SetBC(processor.GetRegisters().GetBC() + 1);
return 8;
}
std::uint8_t IncB(Processor &processor) {
const auto value = processor.GetRegisters().GetB();
const std::uint8_t result = value + 1;
processor.GetRegisters().SetB(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 DecB(Processor &processor) {
const auto value = processor.GetRegisters().GetB();
const std::uint8_t result = value - 1;
processor.GetRegisters().SetB(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 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<std::uint8_t>(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<std::uint16_t>(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;
}
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 instructions
} // namespace juiceboy } // namespace juiceboy
+1 -1
View File
@@ -28,6 +28,6 @@ std::uint8_t Processor::FetchByte() {
std::uint16_t Processor::FetchWord() { std::uint16_t Processor::FetchWord() {
const auto low = static_cast<std::uint16_t>(FetchByte()); const auto low = static_cast<std::uint16_t>(FetchByte());
const auto high = static_cast<std::uint16_t>(FetchByte()) << 8; const auto high = static_cast<std::uint16_t>(FetchByte()) << 8;
return low | high; return static_cast<std::uint16_t>(low | high);
} }
} // namespace juiceboy } // namespace juiceboy
+28 -2
View File
@@ -7,8 +7,8 @@ namespace juiceboy {
namespace { namespace {
std::uint16_t BytesToWord(const std::uint8_t low, const std::uint8_t high) { std::uint16_t BytesToWord(const std::uint8_t low, const std::uint8_t high) {
return static_cast<std::uint16_t>(low) | static_cast<std::uint16_t>(high) return static_cast<std::uint16_t>(low) |
<< 8; static_cast<std::uint16_t>(high << 8);
} }
std::pair<std::uint8_t, std::uint8_t> WordToBytes(const std::uint16_t word) { std::pair<std::uint8_t, std::uint8_t> WordToBytes(const std::uint16_t word) {
@@ -27,13 +27,39 @@ void Registers::IncrementProgramCounter(const std::uint16_t value) {
std::uint16_t Registers::GetStackPointer() const { return m_sp; } 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::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::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::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) { void Registers::SetBC(const std::uint16_t value) {
std::tie(m_regB, m_regC) = WordToBytes(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<std::uint8_t>(flag)) != 0;
}
void Registers::SetFlag(Flags flag, bool value) {
const auto mask = static_cast<std::uint8_t>(flag);
if (value) {
m_regF |= mask;
} else {
m_regF &= static_cast<std::uint8_t>(~mask);
}
}
} // namespace juiceboy } // namespace juiceboy
+317 -12
View File
@@ -1,7 +1,9 @@
#include "juiceboy/instructions.hpp" #include "juiceboy/instructions.hpp"
#include "juiceboy/cartridge.hpp" #include "juiceboy/cartridge.hpp"
#include "juiceboy/memory.hpp"
#include "juiceboy/processor.hpp" #include "juiceboy/processor.hpp"
#include "juiceboy/registers.hpp"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <memory> #include <memory>
@@ -16,42 +18,345 @@ public:
}; };
class InstructionsTest : public testing::Test { class InstructionsTest : public testing::Test {
public: protected:
Processor &GetProcessor() { return processor; } Processor &GetProcessor() { return processor; }
Memory &GetMemory() { return processor.GetMemory(); }
Registers &GetRegisters() { return processor.GetRegisters(); }
private: private:
Processor processor{Memory{std::make_shared<TestCartridge>()}}; Processor processor{Memory{std::make_shared<TestCartridge>()}};
}; };
} // namespace } // namespace
TEST_F(InstructionsTest, Noop) { TEST_F(InstructionsTest, Nop) {
const auto cycles = instructions::Nop(GetProcessor()); const auto cycles = instructions::Nop(GetProcessor());
EXPECT_EQ(cycles, 4); EXPECT_EQ(cycles, 4);
EXPECT_EQ(GetProcessor().GetRegisters().GetProgramCounter(), 0); EXPECT_EQ(GetRegisters().GetProgramCounter(), 0);
} }
TEST_F(InstructionsTest, LdBcD16) { TEST_F(InstructionsTest, LdBcD16) {
GetProcessor().GetMemory().WriteByte(0x8000, 0x34); GetMemory().WriteByte(0x8000, 0x34);
GetProcessor().GetMemory().WriteByte(0x8001, 0x12); GetMemory().WriteByte(0x8001, 0x12);
GetProcessor().GetRegisters().SetProgramCounter(0x8000); GetRegisters().SetProgramCounter(0x8000);
const auto cycles = instructions::LdBcD16(GetProcessor()); const auto cycles = instructions::LdBcD16(GetProcessor());
EXPECT_EQ(cycles, 12); EXPECT_EQ(cycles, 12);
EXPECT_EQ(GetProcessor().GetRegisters().GetBC(), 0x1234); EXPECT_EQ(GetRegisters().GetBC(), 0x1234);
EXPECT_EQ(GetProcessor().GetRegisters().GetProgramCounter(), 0x8002); EXPECT_EQ(GetRegisters().GetProgramCounter(), 0x8002);
} }
TEST_F(InstructionsTest, LdBcA) { TEST_F(InstructionsTest, LdBcA) {
GetProcessor().GetRegisters().SetBC(0x8000); GetRegisters().SetBC(0x8000);
GetProcessor().GetRegisters().SetA(0x12); GetRegisters().SetA(0x12);
const auto cycles = instructions::LdBcA(GetProcessor()); const auto cycles = instructions::LdBcA(GetProcessor());
EXPECT_EQ(cycles, 8); EXPECT_EQ(cycles, 8);
EXPECT_EQ(GetProcessor().GetMemory().ReadByte(0x8000), 0x12); EXPECT_EQ(GetMemory().ReadByte(0x8000), 0x12);
EXPECT_EQ(GetProcessor().GetRegisters().GetProgramCounter(), 0); EXPECT_EQ(GetRegisters().GetProgramCounter(), 0);
}
TEST_F(InstructionsTest, IncBC) {
GetRegisters().SetBC(0x1);
const auto cycles = instructions::IncBc(GetProcessor());
EXPECT_EQ(cycles, 8);
EXPECT_EQ(GetRegisters().GetBC(), 0x2);
EXPECT_EQ(GetRegisters().GetProgramCounter(), 0);
}
TEST_F(InstructionsTest, IncB) {
GetRegisters().SetB(0x1);
const auto cycles = instructions::IncB(GetProcessor());
EXPECT_EQ(cycles, 4);
EXPECT_EQ(GetRegisters().GetB(), 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, IncBSetsZeroFlag) {
GetRegisters().SetB(0xFF);
(void)instructions::IncB(GetProcessor());
EXPECT_EQ(GetRegisters().GetB(), 0x00);
EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Zero));
}
TEST_F(InstructionsTest, IncBSetsHalfCarryFlag) {
GetRegisters().SetB(0x0F);
(void)instructions::IncB(GetProcessor());
EXPECT_EQ(GetRegisters().GetB(), 0x10);
EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::HalfCarry));
}
TEST_F(InstructionsTest, IncBPreservesCarry) {
GetRegisters().SetFlag(Registers::Flags::Carry, true);
(void)instructions::IncB(GetProcessor());
EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Carry));
}
TEST_F(InstructionsTest, DecB) {
GetRegisters().SetB(0x2);
const auto cycles = instructions::DecB(GetProcessor());
EXPECT_EQ(cycles, 4);
EXPECT_EQ(GetRegisters().GetB(), 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, DecBSetsZeroFlag) {
GetRegisters().SetB(0x1);
(void)instructions::DecB(GetProcessor());
EXPECT_EQ(GetRegisters().GetB(), 0x00);
EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::Zero));
}
TEST_F(InstructionsTest, DecBSetsHalfCarryFlag) {
GetRegisters().SetB(0x10);
(void)instructions::DecB(GetProcessor());
EXPECT_EQ(GetRegisters().GetB(), 0x0F);
EXPECT_TRUE(GetRegisters().GetFlag(Registers::Flags::HalfCarry));
}
TEST_F(InstructionsTest, DecBPreservesCarry) {
GetRegisters().SetFlag(Registers::Flags::Carry, true);
(void)instructions::IncB(GetProcessor());
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));
}
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 } // namespace juiceboy::test