@@ 1,4 1,5 @@
#include <algorithm>
+#include <cassert>
#include <functional>
#include <stdexcept>
@@ 41,7 42,10 @@ constexpr auto wrapToByte(size_t value) -> uint8_t {
return toU8(value % u8Modulo);
}
-constexpr void ValueStore::write(uint8_t newValue) {
+constexpr void ValueStore::write(uint8_t newValue) noexcept {
+ assert(type != Type::Implicit
+ && type != Type::Value);
+
switch(type) {
case Type::Accumulator: {
cpu.accumulator = newValue;
@@ 55,11 59,11 @@ constexpr void ValueStore::write(uint8_t newValue) {
case Type::Value:
break;
}
-
- throw std::logic_error{"Attempt to write to a raw value"};
}
-constexpr auto ValueStore::read() const -> uint16_t {
+constexpr auto ValueStore::read() const noexcept -> uint16_t {
+ assert(type != Type::Implicit);
+
switch(type) {
case Type::Accumulator:
return cpu.accumulator;
@@ 71,7 75,7 @@ constexpr auto ValueStore::read() const -> uint16_t {
break;
}
- throw std::logic_error{"Unhandled ValueStore::read()"};
+ return {};
}
constexpr void CPU::reset() {
@@ 93,7 97,7 @@ void CPU::loadProgram(const std::span<const uint8_t> program) {
loadProgram(program, initialProgramCounter);
}
-auto CPU::step() -> bool {
+auto CPU::step() noexcept -> bool {
const auto opcode = read(pc++);
static auto instructions = CPU::getInstructions();
@@ 108,10 112,13 @@ auto CPU::step() -> bool {
}
// Get the target address depending on the addressing mode
-constexpr auto CPU::getTarget(AddressMode mode) -> ValueStore {
+constexpr auto CPU::getTarget(AddressMode mode) noexcept -> ValueStore {
using Mode = AddressMode;
- CPU& self = *this;
+ assert(mode >= Mode::Implicit
+ && mode <= Mode::ZeropageY);
+
+ CPU& self = *this;
switch (mode) {
// Instruction makes target implicit, e.g. CLC
case Mode::Implicit: {
@@ 205,18 212,19 @@ constexpr auto CPU::getTarget(AddressMode mode) -> ValueStore {
}
}
- throw std::logic_error{"Invalid address mode specified"};
+ return ValueStore(self);
}
-constexpr void CPU::branch(uint16_t address) {
+constexpr void CPU::branch(uint16_t address) noexcept {
pc = address;
}
-constexpr auto CPU::read(size_t address) const -> uint8_t {
+constexpr auto CPU::read(size_t address) const noexcept -> uint8_t {
return memory[address];
}
-constexpr auto CPU::read2(size_t address, bool wrapToPage) const -> uint16_t {
+constexpr auto CPU::read2(size_t address, bool wrapToPage) const noexcept
+ -> uint16_t {
if (wrapToPage)
address = wrapToByte(address);
@@ 225,36 233,39 @@ constexpr auto CPU::read2(size_t address, bool wrapToPage) const -> uint16_t {
+ (memory[wrapToPage ? wrapToByte(highAddress) : highAddress] << 8U);
}
-constexpr void CPU::write(size_t address, uint8_t value) {
+constexpr void CPU::write(size_t address, uint8_t value) noexcept {
memory[address] = value;
}
-constexpr void CPU::push(uint8_t value) {
+constexpr void CPU::push(uint8_t value) noexcept {
memory[stackTop + stack--] = value;
}
-constexpr void CPU::push2(uint16_t value) {
+constexpr void CPU::push2(uint16_t value) noexcept {
push(toU8(value >> 8U));
push(toU8(value & u8Max));
}
-constexpr auto CPU::pop() -> uint8_t {
+constexpr auto CPU::pop() noexcept -> uint8_t {
return memory[stackTop + ++stack];
}
-constexpr auto CPU::pop2() -> uint16_t {
+constexpr auto CPU::pop2() noexcept -> uint16_t {
return pop() + (pop() << 8U);
}
-constexpr void CPU::popFlags() {
+constexpr void CPU::popFlags() noexcept {
auto value = pop();
value |= (1U << Unused);
value &= ~(1U << Break);
flags = value;
}
-void CPU::calculateFlag(uint8_t value, FlagIndex flag) {
+void CPU::calculateFlag(uint8_t value, FlagIndex flag) noexcept {
bool result = false;
+ assert(flag == Carry
+ || flag == Zero
+ || flag == Negative);
switch(flag) {
case Carry:
@@ 264,7 275,7 @@ void CPU::calculateFlag(uint8_t value, FlagIndex flag) {
case Negative:
result = isNegative(value); break;
default:
- throw std::logic_error{"Can't calculate this flag from a value"};
+ return;
}
flags.set(flag, result);
@@ 276,13 287,13 @@ void CPU::calculateFlag(uint8_t value, T flag, Args... flags) {
calculateFlag(value, flags...);
}
-void CPU::compare(size_t a, size_t b) {
+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));
}
-void CPU::addWithCarry(uint8_t input) {
+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);
@@ 296,17 307,17 @@ void CPU::addWithCarry(uint8_t input) {
accumulator = result;
}
-void CPU::oADC(ValueStore address) {
+void CPU::oADC(ValueStore address) noexcept {
addWithCarry(address.read());
}
-void CPU::oAND(ValueStore address) {
+void CPU::oAND(ValueStore address) noexcept {
const auto input = address.read();
accumulator &= input;
calculateFlag(accumulator, Zero, Negative);
}
-void CPU::oASL(ValueStore address) {
+void CPU::oASL(ValueStore address) noexcept {
const auto input = address.read();
flags.set(Carry, getBit(7, input));
@@ 315,163 326,163 @@ void CPU::oASL(ValueStore address) {
address.write(result);
}
-void CPU::oBCC(ValueStore target) {
+void CPU::oBCC(ValueStore target) noexcept {
if (!flags.test(Carry))
branch(target.value);
}
-void CPU::oBCS(ValueStore target) {
+void CPU::oBCS(ValueStore target) noexcept {
if (flags.test(Carry))
branch(target.value);
}
-void CPU::oBEQ(ValueStore target) {
+void CPU::oBEQ(ValueStore target) noexcept {
if (flags.test(Zero))
branch(target.value);
}
-void CPU::oBIT(ValueStore address) {
+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));
}
-void CPU::oBMI(ValueStore target) {
+void CPU::oBMI(ValueStore target) noexcept {
if (flags.test(Negative))
branch(target.value);
}
-void CPU::oBNE(ValueStore target) {
+void CPU::oBNE(ValueStore target) noexcept {
if (!flags.test(Zero))
branch(target.value);
}
-void CPU::oBPL(ValueStore target) {
+void CPU::oBPL(ValueStore target) noexcept {
if (!flags.test(Negative))
branch(target.value);
}
-void CPU::oBRK(ValueStore) {
+void CPU::oBRK(ValueStore) noexcept {
flags.set(InterruptOff, true);
push2(pc);
push(toU8(flags.to_ulong()));
}
-void CPU::oBVC(ValueStore target) {
+void CPU::oBVC(ValueStore target) noexcept {
if (!flags.test(Overflow))
branch(target.value);
}
-void CPU::oBVS(ValueStore target) {
+void CPU::oBVS(ValueStore target) noexcept {
if (flags.test(Overflow))
branch(target.value);
}
-void CPU::oCLC(ValueStore) {
+void CPU::oCLC(ValueStore) noexcept {
flags.set(Carry, false);
}
-void CPU::oCLD(ValueStore) {
+void CPU::oCLD(ValueStore) noexcept {
flags.set(Decimal, false);
}
-void CPU::oCLI(ValueStore) {
+void CPU::oCLI(ValueStore) noexcept {
flags.set(InterruptOff, false);
}
-void CPU::oCLV(ValueStore) {
+void CPU::oCLV(ValueStore) noexcept {
flags.set(Overflow, false);
}
-void CPU::oCMP(ValueStore address) {
+void CPU::oCMP(ValueStore address) noexcept {
const auto input = address.read();
compare(accumulator, input);
}
-void CPU::oCPX(ValueStore address) {
+void CPU::oCPX(ValueStore address) noexcept {
const auto input = address.read();
compare(indexX, input);
}
-void CPU::oCPY(ValueStore address) {
+void CPU::oCPY(ValueStore address) noexcept {
const auto input = address.read();
compare(indexY, input);
}
-void CPU::oDEC(ValueStore address) {
+void CPU::oDEC(ValueStore address) noexcept {
const auto input = address.read();
const auto result = input - 1;
calculateFlag(result, Zero, Negative);
address.write(result);
}
-void CPU::oDEX(ValueStore) {
+void CPU::oDEX(ValueStore) noexcept {
const auto result = indexX - 1;
calculateFlag(result, Zero, Negative);
indexX = result;
}
-void CPU::oDEY(ValueStore) {
+void CPU::oDEY(ValueStore) noexcept {
const auto result = indexY - 1;
calculateFlag(result, Zero, Negative);
indexY = result;
}
-void CPU::oEOR(ValueStore address) {
+void CPU::oEOR(ValueStore address) noexcept {
const auto input = address.read();
accumulator = accumulator ^ input;
calculateFlag(accumulator, Zero, Negative);
}
-void CPU::oINC(ValueStore address) {
+void CPU::oINC(ValueStore address) noexcept {
const auto input = address.read();
const auto result = input + 1;
calculateFlag(result, Zero, Negative);
address.write(result);
}
-void CPU::oINX(ValueStore) {
+void CPU::oINX(ValueStore) noexcept {
const auto result = indexX + 1;
calculateFlag(result, Zero, Negative);
indexX = result;
}
-void CPU::oINY(ValueStore) {
+void CPU::oINY(ValueStore) noexcept {
const auto result = indexY + 1;
calculateFlag(result, Zero, Negative);
indexY = result;
}
-void CPU::oJMP(ValueStore target) {
+void CPU::oJMP(ValueStore target) noexcept {
pc = target.value;
}
-void CPU::oJSR(ValueStore target) {
+void CPU::oJSR(ValueStore target) noexcept {
push2(toU16(pc - 1));
pc = target.value;
}
-void CPU::oLDA(ValueStore address) {
+void CPU::oLDA(ValueStore address) noexcept {
const auto input = address.read();
accumulator = input;
calculateFlag(input, Zero, Negative);
}
-void CPU::oLDX(ValueStore address) {
+void CPU::oLDX(ValueStore address) noexcept {
const auto input = address.read();
indexX = input;
calculateFlag(input, Zero, Negative);
}
-void CPU::oLDY(ValueStore address) {
+void CPU::oLDY(ValueStore address) noexcept {
const auto input = address.read();
indexY = input;
calculateFlag(input, Zero, Negative);
}
-void CPU::oLSR(ValueStore address) {
+void CPU::oLSR(ValueStore address) noexcept {
const auto input = address.read();
const auto result = input >> 1U;
calculateFlag(result, Zero, Negative);
@@ 479,34 490,34 @@ void CPU::oLSR(ValueStore address) {
address.write(result);
}
-void CPU::oNOP(ValueStore) {
+void CPU::oNOP(ValueStore) noexcept {
}
-void CPU::oORA(ValueStore address) {
+void CPU::oORA(ValueStore address) noexcept {
const auto input = address.read();
const auto result = accumulator | input;
calculateFlag(result, Zero, Negative);
accumulator = result;
}
-void CPU::oPHA(ValueStore) {
+void CPU::oPHA(ValueStore) noexcept {
push(accumulator);
}
-void CPU::oPHP(ValueStore) {
+void CPU::oPHP(ValueStore) noexcept {
push(toU8(flags.to_ulong() | (1U << Break)));
}
-void CPU::oPLA(ValueStore) {
+void CPU::oPLA(ValueStore) noexcept {
accumulator = pop();
calculateFlag(accumulator, Zero, Negative);
}
-void CPU::oPLP(ValueStore) {
+void CPU::oPLP(ValueStore) noexcept {
popFlags();
}
-void CPU::oROL(ValueStore address) {
+void CPU::oROL(ValueStore address) noexcept {
const auto input = address.read();
const auto result = setBit(0, input << 1U, flags.test(Carry));
@@ 515,7 526,7 @@ void CPU::oROL(ValueStore address) {
address.write(result);
}
-void CPU::oROR(ValueStore address) {
+void CPU::oROR(ValueStore address) noexcept {
const auto input = address.read();
const auto result = setBit(7, input >> 1U, flags.test(Carry));
@@ 524,68 535,68 @@ void CPU::oROR(ValueStore address) {
address.write(result);
}
-void CPU::oRTI(ValueStore) {
+void CPU::oRTI(ValueStore) noexcept {
popFlags();
pc = pop2();
}
-void CPU::oRTS(ValueStore) {
+void CPU::oRTS(ValueStore) noexcept {
pc = pop2() + 1;
}
-void CPU::oSBC(ValueStore address) {
+void CPU::oSBC(ValueStore address) noexcept {
addWithCarry(~address.read());
}
-void CPU::oSEC(ValueStore) {
+void CPU::oSEC(ValueStore) noexcept {
flags.set(Carry, true);
}
-void CPU::oSED(ValueStore) {
+void CPU::oSED(ValueStore) noexcept {
flags.set(Decimal, true);
}
-void CPU::oSEI(ValueStore) {
+void CPU::oSEI(ValueStore) noexcept {
flags.set(InterruptOff, true);
}
-void CPU::oSTA(ValueStore address) {
+void CPU::oSTA(ValueStore address) noexcept {
address.write(accumulator);
}
-void CPU::oSTX(ValueStore address) {
+void CPU::oSTX(ValueStore address) noexcept {
address.write(indexX);
}
-void CPU::oSTY(ValueStore address) {
+void CPU::oSTY(ValueStore address) noexcept {
address.write(indexY);
}
-void CPU::oTAX(ValueStore) {
+void CPU::oTAX(ValueStore) noexcept {
indexX = accumulator;
calculateFlag(indexX, Zero, Negative);
}
-void CPU::oTAY(ValueStore) {
+void CPU::oTAY(ValueStore) noexcept {
indexY = accumulator;
calculateFlag(indexY, Zero, Negative);
}
-void CPU::oTSX(ValueStore) {
+void CPU::oTSX(ValueStore) noexcept {
indexX = stack;
calculateFlag(indexX, Zero, Negative);
}
-void CPU::oTXA(ValueStore) {
+void CPU::oTXA(ValueStore) noexcept {
accumulator = indexX;
calculateFlag(accumulator, Zero, Negative);
}
-void CPU::oTXS(ValueStore) {
+void CPU::oTXS(ValueStore) noexcept {
stack = indexX;
}
-void CPU::oTYA(ValueStore) {
+void CPU::oTYA(ValueStore) noexcept {
accumulator = indexY;
calculateFlag(accumulator, Zero, Negative);
}
@@ 60,8 60,8 @@ public:
};
[[nodiscard]]
- constexpr auto read() const -> uint16_t;
- constexpr void write(uint8_t);
+ constexpr auto read() const noexcept -> uint16_t;
+ constexpr void write(uint8_t) noexcept;
const uint16_t value;
@@ 83,7 83,7 @@ public:
// [...] used but never defined" if it is declared constexpr
void loadProgram(std::span<const uint8_t> program, uint16_t offset);
void loadProgram(std::span<const uint8_t> program);
- auto step() -> bool;
+ auto step() noexcept -> bool;
// Registers
uint8_t accumulator{0};
@@ 115,83 115,83 @@ private:
constexpr static auto getInstructions() -> Instructions;
// Instruction helpers
- constexpr auto getTarget(AddressMode mode) -> ValueStore;
+ constexpr auto getTarget(AddressMode mode) noexcept -> ValueStore;
[[nodiscard]]
- constexpr auto read(size_t address) const -> uint8_t;
+ constexpr auto read(size_t address) const noexcept -> uint8_t;
[[nodiscard]]
constexpr auto read2(size_t address, bool wrapToPage = false) const
- -> uint16_t;
- constexpr void write(size_t address, uint8_t value);
- constexpr void push(uint8_t);
- constexpr void push2(uint16_t);
- constexpr auto pop() -> uint8_t;
- constexpr auto pop2() -> uint16_t;
- constexpr void popFlags();
- constexpr void branch(uint16_t);
+ noexcept -> uint16_t;
+ constexpr void write(size_t address, uint8_t value) noexcept;
+ constexpr void push(uint8_t) noexcept;
+ constexpr void push2(uint16_t) noexcept;
+ constexpr auto pop() noexcept -> uint8_t;
+ constexpr auto pop2() noexcept -> uint16_t;
+ constexpr void popFlags() noexcept;
+ constexpr void branch(uint16_t) noexcept;
template<class T, class... Args>
void calculateFlag(uint8_t value, T flag, Args... flags);
- void calculateFlag(uint8_t value, FlagIndex flag);
- void compare(size_t a, size_t b);
- void addWithCarry(uint8_t value);
+ void calculateFlag(uint8_t value, FlagIndex flag) noexcept;
+ void compare(size_t a, size_t b) noexcept;
+ void addWithCarry(uint8_t value) noexcept;
// Instructions
- void oADC(ValueStore);
- void oAND(ValueStore);
- void oASL(ValueStore);
- void oBCC(ValueStore);
- void oBCS(ValueStore);
- void oBEQ(ValueStore);
- void oBIT(ValueStore);
- void oBMI(ValueStore);
- void oBNE(ValueStore);
- void oBPL(ValueStore);
- void oBRK(ValueStore);
- void oBVC(ValueStore);
- void oBVS(ValueStore);
- void oCLC(ValueStore);
- void oCLD(ValueStore);
- void oCLI(ValueStore);
- void oCLV(ValueStore);
- void oCMP(ValueStore);
- void oCPX(ValueStore);
- void oCPY(ValueStore);
- void oDEC(ValueStore);
- void oDEX(ValueStore);
- void oDEY(ValueStore);
- void oEOR(ValueStore);
- void oINC(ValueStore);
- void oINX(ValueStore);
- void oINY(ValueStore);
- void oJMP(ValueStore);
- void oJSR(ValueStore);
- void oLDA(ValueStore);
- void oLDX(ValueStore);
- void oLDY(ValueStore);
- void oLSR(ValueStore);
- void oNOP(ValueStore);
- void oORA(ValueStore);
- void oPHA(ValueStore);
- void oPHP(ValueStore);
- void oPLA(ValueStore);
- void oPLP(ValueStore);
- void oROL(ValueStore);
- void oROR(ValueStore);
- void oRTI(ValueStore);
- void oRTS(ValueStore);
- void oSBC(ValueStore);
- void oSEC(ValueStore);
- void oSED(ValueStore);
- void oSEI(ValueStore);
- void oSTA(ValueStore);
- void oSTX(ValueStore);
- void oSTY(ValueStore);
- void oTAX(ValueStore);
- void oTAY(ValueStore);
- void oTSX(ValueStore);
- void oTXA(ValueStore);
- void oTXS(ValueStore);
- void oTYA(ValueStore);
+ void oADC(ValueStore) noexcept;
+ void oAND(ValueStore) noexcept;
+ void oASL(ValueStore) noexcept;
+ void oBCC(ValueStore) noexcept;
+ void oBCS(ValueStore) noexcept;
+ void oBEQ(ValueStore) noexcept;
+ void oBIT(ValueStore) noexcept;
+ void oBMI(ValueStore) noexcept;
+ void oBNE(ValueStore) noexcept;
+ void oBPL(ValueStore) noexcept;
+ void oBRK(ValueStore) noexcept;
+ void oBVC(ValueStore) noexcept;
+ void oBVS(ValueStore) noexcept;
+ void oCLC(ValueStore) noexcept;
+ void oCLD(ValueStore) noexcept;
+ void oCLI(ValueStore) noexcept;
+ void oCLV(ValueStore) noexcept;
+ void oCMP(ValueStore) noexcept;
+ void oCPX(ValueStore) noexcept;
+ void oCPY(ValueStore) noexcept;
+ void oDEC(ValueStore) noexcept;
+ void oDEX(ValueStore) noexcept;
+ void oDEY(ValueStore) noexcept;
+ void oEOR(ValueStore) noexcept;
+ void oINC(ValueStore) noexcept;
+ void oINX(ValueStore) noexcept;
+ void oINY(ValueStore) noexcept;
+ void oJMP(ValueStore) noexcept;
+ void oJSR(ValueStore) noexcept;
+ void oLDA(ValueStore) noexcept;
+ void oLDX(ValueStore) noexcept;
+ void oLDY(ValueStore) noexcept;
+ void oLSR(ValueStore) noexcept;
+ void oNOP(ValueStore) noexcept;
+ void oORA(ValueStore) noexcept;
+ void oPHA(ValueStore) noexcept;
+ void oPHP(ValueStore) noexcept;
+ void oPLA(ValueStore) noexcept;
+ void oPLP(ValueStore) noexcept;
+ void oROL(ValueStore) noexcept;
+ void oROR(ValueStore) noexcept;
+ void oRTI(ValueStore) noexcept;
+ void oRTS(ValueStore) noexcept;
+ void oSBC(ValueStore) noexcept;
+ void oSEC(ValueStore) noexcept;
+ void oSED(ValueStore) noexcept;
+ void oSEI(ValueStore) noexcept;
+ void oSTA(ValueStore) noexcept;
+ void oSTX(ValueStore) noexcept;
+ void oSTY(ValueStore) noexcept;
+ void oTAX(ValueStore) noexcept;
+ void oTAY(ValueStore) noexcept;
+ void oTSX(ValueStore) noexcept;
+ void oTXA(ValueStore) noexcept;
+ void oTXS(ValueStore) noexcept;
+ void oTYA(ValueStore) noexcept;
friend class ValueStore;
};