From 2626b36995b3a2e8432f2e77ad1fcdb2d8b8eb46 Mon Sep 17 00:00:00 2001 From: williamvds Date: Fri, 19 Jun 2020 19:54:27 +0100 Subject: [PATCH] Replace std::bitset flags with constexpr struct Many std::bitset methods aren't marked constexpr, so this requires re-implementing the needed functionality in a new class. --- src/cpu.cpp | 134 ++++++++++++++++++++++++----------------------- src/cpu.hpp | 74 ++++++++++++++++++++------ test/nestest.hpp | 4 +- test/testCPU.cpp | 9 ++-- 4 files changed, 136 insertions(+), 85 deletions(-) diff --git a/src/cpu.cpp b/src/cpu.cpp index 86cdc08..79ebc05 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -9,6 +9,8 @@ constexpr auto u8Max = 0xffU; constexpr auto u8Modulo = 0x100U; constexpr auto u16Upper = 0xff00U; +using F = Flags::Index; + constexpr auto toU8(auto value) { return static_cast(value); } @@ -82,7 +84,7 @@ constexpr void CPU::reset() { memory = Memory{}; pc = initialProgramCounter; stack = initialStackPointer; - flags = initialFlags; + flags.reset(); } void CPU::loadProgram(const std::span program, uint16_t offset) { @@ -256,23 +258,23 @@ constexpr auto CPU::pop2() noexcept -> uint16_t { constexpr void CPU::popFlags() noexcept { auto value = pop(); - value |= (1U << Unused); - value &= ~(1U << Break); + value |= Flags::bitmask(F::Unused); + value &= toU8(~Flags::bitmask(F::Break)); flags = value; } -void CPU::calculateFlag(uint8_t value, FlagIndex flag) noexcept { +void CPU::calculateFlag(uint8_t value, Flags::Index flag) noexcept { bool result = false; - assert(flag == Carry - || flag == Zero - || flag == Negative); + assert(flag == F::Carry + || flag == F::Zero + || flag == F::Negative); switch(flag) { - case Carry: + case F::Carry: result = (value == u8Max); break; - case Zero: + case F::Zero: result = (value == 0); break; - case Negative: + case F::Negative: result = isNegative(value); break; default: return; @@ -288,21 +290,21 @@ void CPU::calculateFlag(uint8_t value, T flag, Args... flags) { } void CPU::compare(size_t a, size_t b) noexcept { - flags.set(Zero, a == b); - flags.set(Carry, a >= b); - flags.set(Negative, isNegative(a - b)); + flags.set(F::Zero, a == b); + flags.set(F::Carry, a >= b); + flags.set(F::Negative, isNegative(a - b)); } void CPU::addWithCarry(uint8_t input) noexcept { // TODO: implement decimal mode - const uint8_t result = accumulator + input + (flags.test(Carry) ? 1 : 0); - calculateFlag(result, Zero, Negative); + const uint8_t result = accumulator + input + (flags.test(F::Carry) ? 1 : 0); + calculateFlag(result, F::Zero, F::Negative); const auto resultSign = sign(result); - flags.set(Overflow, + flags.set(F::Overflow, (sign(accumulator) != resultSign) && (sign(input) != resultSign)); - flags.set(Carry, result < accumulator); + flags.set(F::Carry, result < accumulator); accumulator = result; } @@ -314,86 +316,86 @@ void CPU::oADC(ValueStore address) noexcept { void CPU::oAND(ValueStore address) noexcept { const auto input = address.read(); accumulator &= input; - calculateFlag(accumulator, Zero, Negative); + calculateFlag(accumulator, F::Zero, F::Negative); } void CPU::oASL(ValueStore address) noexcept { const auto input = address.read(); - flags.set(Carry, getBit(7, input)); + flags.set(F::Carry, getBit(7, input)); const auto result = input << 1U; - calculateFlag(result, Zero, Negative); + calculateFlag(result, F::Zero, F::Negative); address.write(result); } void CPU::oBCC(ValueStore target) noexcept { - if (!flags.test(Carry)) + if (!flags.test(F::Carry)) branch(target.value); } void CPU::oBCS(ValueStore target) noexcept { - if (flags.test(Carry)) + if (flags.test(F::Carry)) branch(target.value); } void CPU::oBEQ(ValueStore target) noexcept { - if (flags.test(Zero)) + if (flags.test(F::Zero)) branch(target.value); } void CPU::oBIT(ValueStore address) noexcept { const auto input = address.read(); - flags.set(Zero, !toBool(input & accumulator)); - flags.set(Overflow, getBit(6, input)); - flags.set(Negative, isNegative(input)); + flags.set(F::Zero, !toBool(input & accumulator)); + flags.set(F::Overflow, getBit(6, input)); + flags.set(F::Negative, isNegative(input)); } void CPU::oBMI(ValueStore target) noexcept { - if (flags.test(Negative)) + if (flags.test(F::Negative)) branch(target.value); } void CPU::oBNE(ValueStore target) noexcept { - if (!flags.test(Zero)) + if (!flags.test(F::Zero)) branch(target.value); } void CPU::oBPL(ValueStore target) noexcept { - if (!flags.test(Negative)) + if (!flags.test(F::Negative)) branch(target.value); } void CPU::oBRK(ValueStore) noexcept { - flags.set(InterruptOff, true); + flags.set(F::InterruptOff, true); push2(pc); - push(toU8(flags.to_ulong())); + push(toU8(flags.get())); } void CPU::oBVC(ValueStore target) noexcept { - if (!flags.test(Overflow)) + if (!flags.test(F::Overflow)) branch(target.value); } void CPU::oBVS(ValueStore target) noexcept { - if (flags.test(Overflow)) + if (flags.test(F::Overflow)) branch(target.value); } void CPU::oCLC(ValueStore) noexcept { - flags.set(Carry, false); + flags.set(F::Carry, false); } void CPU::oCLD(ValueStore) noexcept { - flags.set(Decimal, false); + flags.set(F::Decimal, false); } void CPU::oCLI(ValueStore) noexcept { - flags.set(InterruptOff, false); + flags.set(F::InterruptOff, false); } void CPU::oCLV(ValueStore) noexcept { - flags.set(Overflow, false); + flags.set(F::Overflow, false); } void CPU::oCMP(ValueStore address) noexcept { @@ -414,44 +416,44 @@ void CPU::oCPY(ValueStore address) noexcept { void CPU::oDEC(ValueStore address) noexcept { const auto input = address.read(); const auto result = input - 1; - calculateFlag(result, Zero, Negative); + calculateFlag(result, F::Zero, F::Negative); address.write(result); } void CPU::oDEX(ValueStore) noexcept { const auto result = indexX - 1; - calculateFlag(result, Zero, Negative); + calculateFlag(result, F::Zero, F::Negative); indexX = result; } void CPU::oDEY(ValueStore) noexcept { const auto result = indexY - 1; - calculateFlag(result, Zero, Negative); + calculateFlag(result, F::Zero, F::Negative); indexY = result; } void CPU::oEOR(ValueStore address) noexcept { const auto input = address.read(); accumulator = accumulator ^ input; - calculateFlag(accumulator, Zero, Negative); + calculateFlag(accumulator, F::Zero, F::Negative); } void CPU::oINC(ValueStore address) noexcept { const auto input = address.read(); const auto result = input + 1; - calculateFlag(result, Zero, Negative); + calculateFlag(result, F::Zero, F::Negative); address.write(result); } void CPU::oINX(ValueStore) noexcept { const auto result = indexX + 1; - calculateFlag(result, Zero, Negative); + calculateFlag(result, F::Zero, F::Negative); indexX = result; } void CPU::oINY(ValueStore) noexcept { const auto result = indexY + 1; - calculateFlag(result, Zero, Negative); + calculateFlag(result, F::Zero, F::Negative); indexY = result; } @@ -467,26 +469,26 @@ void CPU::oJSR(ValueStore target) noexcept { void CPU::oLDA(ValueStore address) noexcept { const auto input = address.read(); accumulator = input; - calculateFlag(input, Zero, Negative); + calculateFlag(input, F::Zero, F::Negative); } void CPU::oLDX(ValueStore address) noexcept { const auto input = address.read(); indexX = input; - calculateFlag(input, Zero, Negative); + calculateFlag(input, F::Zero, F::Negative); } void CPU::oLDY(ValueStore address) noexcept { const auto input = address.read(); indexY = input; - calculateFlag(input, Zero, Negative); + calculateFlag(input, F::Zero, F::Negative); } void CPU::oLSR(ValueStore address) noexcept { const auto input = address.read(); const auto result = input >> 1U; - calculateFlag(result, Zero, Negative); - flags.set(Carry, getBit(0, input)); + calculateFlag(result, F::Zero, F::Negative); + flags.set(F::Carry, getBit(0, input)); address.write(result); } @@ -496,7 +498,7 @@ void CPU::oNOP(ValueStore) noexcept { void CPU::oORA(ValueStore address) noexcept { const auto input = address.read(); const auto result = accumulator | input; - calculateFlag(result, Zero, Negative); + calculateFlag(result, F::Zero, F::Negative); accumulator = result; } @@ -505,12 +507,12 @@ void CPU::oPHA(ValueStore) noexcept { } void CPU::oPHP(ValueStore) noexcept { - push(toU8(flags.to_ulong() | (1U << Break))); + push(toU8(flags.get() | Flags::bitmask(F::Break))); } void CPU::oPLA(ValueStore) noexcept { accumulator = pop(); - calculateFlag(accumulator, Zero, Negative); + calculateFlag(accumulator, F::Zero, F::Negative); } void CPU::oPLP(ValueStore) noexcept { @@ -519,19 +521,19 @@ void CPU::oPLP(ValueStore) noexcept { void CPU::oROL(ValueStore address) noexcept { const auto input = address.read(); - const auto result = setBit(0, input << 1U, flags.test(Carry)); + const auto result = setBit(0, input << 1U, flags.test(F::Carry)); - flags.set(Carry, getBit(7, input)); - calculateFlag(result, Zero, Negative); + flags.set(F::Carry, getBit(7, input)); + calculateFlag(result, F::Zero, F::Negative); address.write(result); } void CPU::oROR(ValueStore address) noexcept { const auto input = address.read(); - const auto result = setBit(7, input >> 1U, flags.test(Carry)); + const auto result = setBit(7, input >> 1U, flags.test(F::Carry)); - flags.set(Carry, getBit(0, input)); - calculateFlag(result, Zero, Negative); + flags.set(F::Carry, getBit(0, input)); + calculateFlag(result, F::Zero, F::Negative); address.write(result); } @@ -549,15 +551,15 @@ void CPU::oSBC(ValueStore address) noexcept { } void CPU::oSEC(ValueStore) noexcept { - flags.set(Carry, true); + flags.set(F::Carry, true); } void CPU::oSED(ValueStore) noexcept { - flags.set(Decimal, true); + flags.set(F::Decimal, true); } void CPU::oSEI(ValueStore) noexcept { - flags.set(InterruptOff, true); + flags.set(F::InterruptOff, true); } void CPU::oSTA(ValueStore address) noexcept { @@ -574,22 +576,22 @@ void CPU::oSTY(ValueStore address) noexcept { void CPU::oTAX(ValueStore) noexcept { indexX = accumulator; - calculateFlag(indexX, Zero, Negative); + calculateFlag(indexX, F::Zero, F::Negative); } void CPU::oTAY(ValueStore) noexcept { indexY = accumulator; - calculateFlag(indexY, Zero, Negative); + calculateFlag(indexY, F::Zero, F::Negative); } void CPU::oTSX(ValueStore) noexcept { indexX = stack; - calculateFlag(indexX, Zero, Negative); + calculateFlag(indexX, F::Zero, F::Negative); } void CPU::oTXA(ValueStore) noexcept { accumulator = indexX; - calculateFlag(accumulator, Zero, Negative); + calculateFlag(accumulator, F::Zero, F::Negative); } void CPU::oTXS(ValueStore) noexcept { @@ -598,7 +600,7 @@ void CPU::oTXS(ValueStore) noexcept { void CPU::oTYA(ValueStore) noexcept { accumulator = indexY; - calculateFlag(accumulator, Zero, Negative); + calculateFlag(accumulator, F::Zero, F::Negative); } constexpr auto CPU::getInstructions() -> Instructions { diff --git a/src/cpu.hpp b/src/cpu.hpp index 59e2f41..1f95688 100644 --- a/src/cpu.hpp +++ b/src/cpu.hpp @@ -9,17 +9,6 @@ class CPU; -enum FlagIndex { - Carry, - Zero, - InterruptOff, - Decimal, - Break, - Unused, - Overflow, - Negative, -}; - enum class AddressMode { Implicit, Accumulator, A = Accumulator, @@ -36,6 +25,63 @@ enum class AddressMode { ZeropageY, ZpgY = ZeropageY, }; +struct Flags { + enum class Index : uint8_t { + Carry, + Zero, + InterruptOff, + Decimal, + Break, + Unused, + Overflow, + Negative, + }; + + constexpr Flags() = default; + constexpr Flags(uint8_t value) + : value{value} + { + }; + + constexpr static auto bitmask(Index i) noexcept -> uint8_t { + return 1U << static_cast(i); + } + + [[nodiscard]] + constexpr auto get() const noexcept -> uint8_t { + return value; + } + + [[nodiscard]] + constexpr auto test(Index i) const noexcept -> bool { + return (value & bitmask(i)) != 0; + } + + constexpr void set(Index i, bool set) noexcept { + const auto mask = bitmask(i); + if (set) + value |= mask; + else + value &= static_cast(~mask); + } + + constexpr void reset() noexcept { + value = getDefault(); + } + + constexpr auto operator ==(const Flags& rhs) const noexcept -> bool { + return value == rhs.value; + } + +private: + constexpr static auto getDefault() -> uint8_t { + return static_cast( + bitmask(Index::Unused) | bitmask(Index::InterruptOff)); + } + + uint8_t value = getDefault(); +}; + class ValueStore { public: enum class Type { @@ -92,8 +138,7 @@ public: uint8_t stack{initialStackPointer}; uint16_t pc{initialProgramCounter}; - using Flags = std::bitset<8>; - Flags flags{initialFlags}; + Flags flags; constexpr static auto memorySize = 65536U; using Memory = std::array; @@ -106,7 +151,6 @@ private: constexpr static auto stackTop = 0x100; constexpr static auto initialStackPointer = 0xfd; constexpr static auto initialProgramCounter = 0x600; - constexpr static auto initialFlags = (1U << Unused) | (1U << InterruptOff); bool indirectJumpBug = true; @@ -131,7 +175,7 @@ private: template void calculateFlag(uint8_t value, T flag, Args... flags); - void calculateFlag(uint8_t value, FlagIndex flag) noexcept; + void calculateFlag(uint8_t value, Flags::Index flag) noexcept; void compare(size_t a, size_t b) noexcept; void addWithCarry(uint8_t value) noexcept; diff --git a/test/nestest.hpp b/test/nestest.hpp index 8eb86e3..67cd485 100644 --- a/test/nestest.hpp +++ b/test/nestest.hpp @@ -5,13 +5,15 @@ #include #include +#include "cpu.hpp" + struct cpuState { uint16_t pc; const char *dis; uint8_t a; uint8_t x; uint8_t y; - std::bitset<8> p; + Flags p; uint8_t sp; uint16_t ppuX; uint16_t ppuY; diff --git a/test/testCPU.cpp b/test/testCPU.cpp index f9a82e8..f470d5d 100644 --- a/test/testCPU.cpp +++ b/test/testCPU.cpp @@ -8,8 +8,8 @@ namespace Catch { template<> - struct StringMaker { - static auto convert(const CPU::Flags& value) -> std::string { + struct StringMaker { + static auto convert(const Flags& value) -> std::string { constexpr auto flags = std::to_array({ 'C', 'Z', 'I', 'D', 'B', '-', 'V', 'N' }); @@ -18,7 +18,10 @@ namespace Catch { os << '['; for (size_t i = 0; i <= flags.size(); i++) { size_t bit = flags.size() - i; - os << (value[bit] ? flags.at(bit) : ' '); + os << + (value.test(static_cast(bit)) + ? flags.at(bit) + : ' '); } os << ']'; -- 2.45.2