M src/cpu.cpp => src/cpu.cpp +165 -193
@@ 11,17 11,11 @@ constexpr auto u8Max = 0xffU;
constexpr auto u8Modulo = 0x100U;
constexpr auto u16Upper = 0xff00U;
-constexpr auto toU8(auto value) {
- return static_cast<uint8_t>(value);
-}
+constexpr auto toU8(auto value) { return static_cast<uint8_t>(value); }
-constexpr auto toU16(auto value) {
- return static_cast<uint16_t>(value);
-}
+constexpr auto toU16(auto value) { return static_cast<uint16_t>(value); }
-constexpr auto toBool(auto value) {
- return static_cast<bool>(value);
-}
+constexpr auto toBool(auto value) { return static_cast<bool>(value); }
constexpr auto setBit(uint8_t index, uint8_t value, bool set) -> uint8_t {
return toU8(value | toU8(toU8(set) << index));
@@ 51,36 45,35 @@ namespace microlator {
using F = Flags::Index;
constexpr void ValueStore::write(uint8_t newValue) noexcept {
- assert(type != Type::Implicit
- && type != Type::Value);
-
- switch(type) {
- case Type::Accumulator: {
- cpu.accumulator = newValue;
- return;
- }
- case Type::Memory: {
- cpu.write(value, newValue);
- return;
- }
- case Type::Implicit:
- case Type::Value:
- break;
+ assert(type != Type::Implicit && type != Type::Value);
+
+ switch (type) {
+ case Type::Accumulator: {
+ cpu.accumulator = newValue;
+ return;
+ }
+ case Type::Memory: {
+ cpu.write(value, newValue);
+ return;
+ }
+ case Type::Implicit:
+ case Type::Value:
+ break;
}
}
constexpr auto ValueStore::read() const noexcept -> uint16_t {
assert(type != Type::Implicit);
- switch(type) {
- case Type::Accumulator:
- return cpu.accumulator;
- case Type::Memory:
- return cpu.read(value);
- case Type::Value:
- return value;
- case Type::Implicit:
- break;
+ switch (type) {
+ case Type::Accumulator:
+ return cpu.accumulator;
+ case Type::Memory:
+ return cpu.read(value);
+ case Type::Value:
+ return value;
+ case Type::Implicit:
+ break;
}
return {};
@@ 88,7 81,7 @@ constexpr auto ValueStore::read() const noexcept -> uint16_t {
constexpr void CPU::reset() {
memory = Memory{};
- pc = initialProgramCounter;
+ pc = initialProgramCounter;
stack = initialStackPointer;
flags.reset();
}
@@ 123,122 116,123 @@ auto CPU::step() noexcept -> bool {
constexpr auto CPU::getTarget(AddressMode mode) noexcept -> ValueStore {
using Mode = AddressMode;
- assert(mode >= Mode::Implicit
- && mode <= Mode::ZeropageY);
+ assert(mode >= Mode::Implicit && mode <= Mode::ZeropageY);
- CPU& self = *this;
+ CPU &self = *this;
switch (mode) {
- // Instruction makes target implicit, e.g. CLC
- case Mode::Implicit: {
- return {self, 0, ValueStore::Type::Implicit};
- }
-
- // Use value of accumulator, e.g. LSL A
- case Mode::Accumulator: {
- return ValueStore(self);
- }
-
- // Use value at next address e.g. LDX #$00
- case Mode::Immediate: {
- return {self, read(pc++), ValueStore::Type::Value};
- }
-
- // Use 16-bit value embedded in instruction, e.g. JMP $1234
- case Mode::Absolute: {
- const auto address = read2(pc);
- pc += 2;
- return {self, address};
- }
-
- // Like Absolute, but add value of register X, e.g. JMP $1234,X
- case Mode::AbsoluteX: {
- return {self, toU16(getTarget(Mode::Absolute).get() + indexX)};
- }
-
- // Like Absolute, but add value of register Y, e.g. JMP $1234,Y
- case Mode::AbsoluteY: {
- return {self, toU16(getTarget(Mode::Absolute).get() + indexY)};
- }
-
- // Use the value at the address embedded in the instruction
- // e.g. JMP ($1234)
- case Mode::Indirect: {
- // indirectJumpBug: a hardware bug results in the increment
- // actually flipping the lower byte from 0xff to 0x00
- const uint16_t lowTarget = getTarget(Mode::Absolute).get(),
- highTarget = indirectJumpBug && (lowTarget & u8Max)
- ? (lowTarget & u16Upper)
- : lowTarget + 1;
-
- return {self, toU16((read(highTarget) << 8U) + read(lowTarget))};
- }
-
- // Like Zeropage, but the X index to the indirect address
- // e.g. LDA ($12,X)
- case Mode::IndirectX: {
- const auto indirectAddr = getTarget(Mode::Zeropage).get() + indexX;
- return {self, read2(indirectAddr, true)};
- }
-
- // Like Indirect, but the Y index to the final address
- // e.g. LDA ($12),Y
- case Mode::IndirectY: {
- const auto indirectAddr = getTarget(Mode::Zeropage).get();
- return {self, toU16(read2(indirectAddr, true) + indexY)};
- }
-
- // Use the value embedded in the instruction as a signed offset
- // from the program counter (after the instruction has been decoded)
- case Mode::Relative: {
- const uint8_t value = getTarget(Mode::Immediate).get(),
- lowerBits = value ^ 0b1000'0000U;
- // Two's complement: when the high bit is set the number is
- // negative, in which case flip the lower bits and add one.
- // If positive the original value is correct
- if (isNegative(value))
- return {self, toU16(pc - (~lowerBits + 1))};
-
- return {self, toU16(pc + value)};
- }
-
- // Use the 4-bit value embedded in the instruction as an offset from the
- // beginning of memory
- case Mode::Zeropage: {
- return {self, read(pc++)};
- }
-
- // Like Zeropage, but add value of register X and wrap within the page
- case Mode::ZeropageX: {
- return {self,
- wrapToByte(getTarget(Mode::Immediate).get() + indexX)};
- }
-
- // Like Zeropage, but add value of register Y and wrap within the page
- case Mode::ZeropageY: {
- return {self,
- wrapToByte(getTarget(Mode::Immediate).get() + indexY)};
- }
+ // Instruction makes target implicit, e.g. CLC
+ case Mode::Implicit: {
+ return {self, 0, ValueStore::Type::Implicit};
+ }
+
+ // Use value of accumulator, e.g. LSL A
+ case Mode::Accumulator: {
+ return ValueStore(self);
+ }
+
+ // Use value at next address e.g. LDX #$00
+ case Mode::Immediate: {
+ return {self, read(pc++), ValueStore::Type::Value};
+ }
+
+ // Use 16-bit value embedded in instruction, e.g. JMP $1234
+ case Mode::Absolute: {
+ const auto address = read2(pc);
+ pc += 2;
+ return {self, address};
+ }
+
+ // Like Absolute, but add value of register X, e.g. JMP $1234,X
+ case Mode::AbsoluteX: {
+ return {self, toU16(getTarget(Mode::Absolute).get() + indexX)};
+ }
+
+ // Like Absolute, but add value of register Y, e.g. JMP $1234,Y
+ case Mode::AbsoluteY: {
+ return {self, toU16(getTarget(Mode::Absolute).get() + indexY)};
+ }
+
+ // Use the value at the address embedded in the instruction
+ // e.g. JMP ($1234)
+ case Mode::Indirect: {
+ // indirectJumpBug: a hardware bug results in the increment
+ // actually flipping the lower byte from 0xff to 0x00
+ const uint16_t lowTarget = getTarget(Mode::Absolute).get(),
+ highTarget =
+ indirectJumpBug && (lowTarget & u8Max)
+ ? (lowTarget & u16Upper)
+ : lowTarget + 1;
+
+ return {self,
+ toU16((read(highTarget) << 8U) + read(lowTarget))};
+ }
+
+ // Like Zeropage, but the X index to the indirect address
+ // e.g. LDA ($12,X)
+ case Mode::IndirectX: {
+ const auto indirectAddr =
+ getTarget(Mode::Zeropage).get() + indexX;
+ return {self, read2(indirectAddr, true)};
+ }
+
+ // Like Indirect, but the Y index to the final address
+ // e.g. LDA ($12),Y
+ case Mode::IndirectY: {
+ const auto indirectAddr = getTarget(Mode::Zeropage).get();
+ return {self, toU16(read2(indirectAddr, true) + indexY)};
+ }
+
+ // Use the value embedded in the instruction as a signed offset
+ // from the program counter (after the instruction has been decoded)
+ case Mode::Relative: {
+ const uint8_t value = getTarget(Mode::Immediate).get(),
+ lowerBits = value ^ 0b1000'0000U;
+ // Two's complement: when the high bit is set the number is
+ // negative, in which case flip the lower bits and add one.
+ // If positive the original value is correct
+ if (isNegative(value))
+ return {self, toU16(pc - (~lowerBits + 1))};
+
+ return {self, toU16(pc + value)};
+ }
+
+ // Use the 4-bit value embedded in the instruction as an offset from the
+ // beginning of memory
+ case Mode::Zeropage: {
+ return {self, read(pc++)};
+ }
+
+ // Like Zeropage, but add value of register X and wrap within the page
+ case Mode::ZeropageX: {
+ return {self,
+ wrapToByte(getTarget(Mode::Immediate).get() + indexX)};
+ }
+
+ // Like Zeropage, but add value of register Y and wrap within the page
+ case Mode::ZeropageY: {
+ return {self,
+ wrapToByte(getTarget(Mode::Immediate).get() + indexY)};
+ }
}
return ValueStore(self);
}
-constexpr void CPU::branch(uint16_t address) noexcept {
- pc = address;
-}
+constexpr void CPU::branch(uint16_t address) noexcept { pc = address; }
constexpr auto CPU::read(uint16_t address) const noexcept -> uint8_t {
return memory[address];
}
constexpr auto CPU::read2(uint16_t address, bool wrapToPage) const noexcept
- -> uint16_t {
+ -> uint16_t {
if (wrapToPage)
address = wrapToByte(address);
const auto highAddress = address + 1;
- return memory[address]
- + (memory[wrapToPage ? wrapToByte(highAddress) : highAddress] << 8U);
+ return memory[address] +
+ (memory[wrapToPage ? wrapToByte(highAddress) : highAddress]
+ << 8U);
}
constexpr void CPU::write(uint16_t address, uint8_t value) noexcept {
@@ 271,45 265,46 @@ constexpr void CPU::popFlags() noexcept {
constexpr void CPU::calculateFlag(uint8_t value, Flags::Index flag) noexcept {
bool result = false;
- assert(flag == F::Carry
- || flag == F::Zero
- || flag == F::Negative);
-
- switch(flag) {
- case F::Carry:
- result = (value == u8Max); break;
- case F::Zero:
- result = (value == 0); break;
- case F::Negative:
- result = isNegative(value); break;
- default:
- return;
+ assert(flag == F::Carry || flag == F::Zero || flag == F::Negative);
+
+ switch (flag) {
+ case F::Carry:
+ result = (value == u8Max);
+ break;
+ case F::Zero:
+ result = (value == 0);
+ break;
+ case F::Negative:
+ result = isNegative(value);
+ break;
+ default:
+ return;
}
flags.set(flag, result);
}
-template<class T, class... Args>
+template <class T, class... Args>
constexpr void CPU::calculateFlag(uint8_t value, T flag, Args... flags) {
calculateFlag(value, flag);
calculateFlag(value, flags...);
}
constexpr void CPU::compare(uint8_t a, uint8_t b) noexcept {
- flags.set(F::Zero, a == b);
- flags.set(F::Carry, a >= b);
+ flags.set(F::Zero, a == b);
+ flags.set(F::Carry, a >= b);
flags.set(F::Negative, isNegative(a - b));
}
constexpr void CPU::addWithCarry(uint8_t value) noexcept {
// TODO: implement decimal mode
- const uint8_t result = accumulator + value + (flags.test(F::Carry) ? 1 : 0);
+ const uint8_t result =
+ accumulator + value + (flags.test(F::Carry) ? 1 : 0);
calculateFlag(result, F::Zero, F::Negative);
const auto resultSign = sign(result);
- flags.set(F::Overflow,
- (sign(accumulator) != resultSign)
- && (sign(value) != resultSign));
+ flags.set(F::Overflow, (sign(accumulator) != resultSign) &&
+ (sign(value) != resultSign));
flags.set(F::Carry, result < accumulator);
accumulator = result;
@@ 373,7 368,7 @@ constexpr void CPU::oBPL(ValueStore target) noexcept {
constexpr void CPU::oBRK(ValueStore) noexcept {
flags.set(F::InterruptOff, true);
-
+
push2(pc);
push(toU8(flags.get()));
}
@@ 388,21 383,15 @@ constexpr void CPU::oBVS(ValueStore target) noexcept {
branch(target.get());
}
-constexpr void CPU::oCLC(ValueStore) noexcept {
- flags.set(F::Carry, false);
-}
+constexpr void CPU::oCLC(ValueStore) noexcept { flags.set(F::Carry, false); }
-constexpr void CPU::oCLD(ValueStore) noexcept {
- flags.set(F::Decimal, false);
-}
+constexpr void CPU::oCLD(ValueStore) noexcept { flags.set(F::Decimal, false); }
constexpr void CPU::oCLI(ValueStore) noexcept {
flags.set(F::InterruptOff, false);
}
-constexpr void CPU::oCLV(ValueStore) noexcept {
- flags.set(F::Overflow, false);
-}
+constexpr void CPU::oCLV(ValueStore) noexcept { flags.set(F::Overflow, false); }
constexpr void CPU::oCMP(ValueStore address) noexcept {
const auto input = address.read();
@@ 463,9 452,7 @@ constexpr void CPU::oINY(ValueStore) noexcept {
indexY = result;
}
-constexpr void CPU::oJMP(ValueStore target) noexcept {
- pc = target.get();
-}
+constexpr void CPU::oJMP(ValueStore target) noexcept { pc = target.get(); }
constexpr void CPU::oJSR(ValueStore target) noexcept {
push2(toU16(pc - 1));
@@ 498,8 485,7 @@ constexpr void CPU::oLSR(ValueStore address) noexcept {
address.write(result);
}
-constexpr void CPU::oNOP(ValueStore) noexcept {
-}
+constexpr void CPU::oNOP(ValueStore) noexcept {}
constexpr void CPU::oORA(ValueStore address) noexcept {
const auto input = address.read();
@@ 508,9 494,7 @@ constexpr void CPU::oORA(ValueStore address) noexcept {
accumulator = result;
}
-constexpr void CPU::oPHA(ValueStore) noexcept {
- push(accumulator);
-}
+constexpr void CPU::oPHA(ValueStore) noexcept { push(accumulator); }
constexpr void CPU::oPHP(ValueStore) noexcept {
push(toU8(flags.get() | Flags::bitmask(F::Break)));
@@ 521,9 505,7 @@ constexpr void CPU::oPLA(ValueStore) noexcept {
calculateFlag(accumulator, F::Zero, F::Negative);
}
-constexpr void CPU::oPLP(ValueStore) noexcept {
- popFlags();
-}
+constexpr void CPU::oPLP(ValueStore) noexcept { popFlags(); }
constexpr void CPU::oROL(ValueStore address) noexcept {
const auto input = address.read();
@@ 545,24 527,18 @@ constexpr void CPU::oROR(ValueStore address) noexcept {
constexpr void CPU::oRTI(ValueStore) noexcept {
popFlags();
- pc = pop2();
+ pc = pop2();
}
-constexpr void CPU::oRTS(ValueStore) noexcept {
- pc = pop2() + 1;
-}
+constexpr void CPU::oRTS(ValueStore) noexcept { pc = pop2() + 1; }
constexpr void CPU::oSBC(ValueStore address) noexcept {
addWithCarry(~address.read());
}
-constexpr void CPU::oSEC(ValueStore) noexcept {
- flags.set(F::Carry, true);
-}
+constexpr void CPU::oSEC(ValueStore) noexcept { flags.set(F::Carry, true); }
-constexpr void CPU::oSED(ValueStore) noexcept {
- flags.set(F::Decimal, true);
-}
+constexpr void CPU::oSED(ValueStore) noexcept { flags.set(F::Decimal, true); }
constexpr void CPU::oSEI(ValueStore) noexcept {
flags.set(F::InterruptOff, true);
@@ 572,13 548,9 @@ constexpr void CPU::oSTA(ValueStore address) noexcept {
address.write(accumulator);
}
-constexpr void CPU::oSTX(ValueStore address) noexcept {
- address.write(indexX);
-}
+constexpr void CPU::oSTX(ValueStore address) noexcept { address.write(indexX); }
-constexpr void CPU::oSTY(ValueStore address) noexcept {
- address.write(indexY);
-}
+constexpr void CPU::oSTY(ValueStore address) noexcept { address.write(indexY); }
constexpr void CPU::oTAX(ValueStore) noexcept {
indexX = accumulator;
@@ 600,9 572,7 @@ constexpr void CPU::oTXA(ValueStore) noexcept {
calculateFlag(accumulator, F::Zero, F::Negative);
}
-constexpr void CPU::oTXS(ValueStore) noexcept {
- stack = indexX;
-}
+constexpr void CPU::oTXS(ValueStore) noexcept { stack = indexX; }
constexpr void CPU::oTYA(ValueStore) noexcept {
accumulator = indexY;
@@ 614,6 584,7 @@ constexpr auto CPU::getInstructions() -> Instructions {
using M = AddressMode;
return {{
+ // clang-format off
{&C::oBRK }, {&C::oORA, M::IndX}, { }, {},
{ }, {&C::oORA, M::Zpg }, {&C::oASL, M::Zpg }, {},
{&C::oPHP }, {&C::oORA, M::Imm }, {&C::oASL, M::A }, {},
@@ 685,6 656,7 @@ constexpr auto CPU::getInstructions() -> Instructions {
{ }, {&C::oSBC, M::ZpgX}, {&C::oINC, M::ZpgX}, {},
{&C::oSED }, {&C::oSBC, M::AbsY}, { }, {},
{ }, {&C::oSBC, M::AbsX}, {&C::oINC, M::AbsX}, {},
+ // clang-format on
}};
}
M src/cpu.hpp => src/cpu.hpp +38 -59
@@ 10,6 10,7 @@ namespace microlator {
class CPU;
enum class AddressMode {
+ // clang-format off
Implicit,
Accumulator, A = Accumulator,
Immediate, Imm = Immediate,
@@ 23,6 24,7 @@ enum class AddressMode {
Zeropage, Zpg = Zeropage,
ZeropageX, ZpgX = ZeropageX,
ZeropageY, ZpgY = ZeropageY,
+ // clang-format on
};
struct Flags {
@@ 41,18 43,16 @@ struct Flags {
constexpr Flags(uint8_t value);
constexpr static auto bitmask(Index i) noexcept -> uint8_t;
- [[nodiscard]]
- constexpr auto get() const noexcept -> uint8_t;
- [[nodiscard]]
- constexpr auto test(Index i) const noexcept -> bool;
+ [[nodiscard]] constexpr auto get() const noexcept -> uint8_t;
+ [[nodiscard]] constexpr auto test(Index i) const noexcept -> bool;
constexpr void set(Index i, bool set) noexcept;
constexpr void reset() noexcept;
- constexpr auto operator ==(const Flags& rhs) const noexcept -> bool;
+ constexpr auto operator==(const Flags &rhs) const noexcept -> bool;
private:
constexpr static auto getDefault() -> uint8_t {
- return static_cast<uint8_t>(
- bitmask(Index::Unused) | bitmask(Index::InterruptOff));
+ return static_cast<uint8_t>(bitmask(Index::Unused) |
+ bitmask(Index::InterruptOff));
}
uint8_t value = getDefault();
@@ 67,24 67,23 @@ public:
Value,
};
- constexpr ValueStore(CPU& cpu, uint16_t value, Type type = Type::Memory);
- constexpr explicit ValueStore(CPU& cpu);
+ constexpr ValueStore(CPU &cpu, uint16_t value,
+ Type type = Type::Memory);
+ constexpr explicit ValueStore(CPU &cpu);
- [[nodiscard]]
- constexpr auto read() const noexcept -> uint16_t;
+ [[nodiscard]] constexpr auto read() const noexcept -> uint16_t;
constexpr void write(uint8_t) noexcept;
- [[nodiscard]]
- constexpr auto get() const noexcept -> uint16_t;
+ [[nodiscard]] constexpr auto get() const noexcept -> uint16_t;
private:
const uint16_t value;
const Type type;
- CPU& cpu;
+ CPU &cpu;
};
struct Instruction {
- using Function = void(CPU::*)(ValueStore);
- Function function = nullptr;
+ using Function = void (CPU::*)(ValueStore);
+ Function function = nullptr;
AddressMode addressMode = AddressMode::Implicit;
};
@@ 98,15 97,15 @@ public:
auto step() noexcept -> bool;
// Registers
- uint8_t accumulator{0};
- uint8_t indexX{0};
- uint8_t indexY{0};
- uint8_t stack{initialStackPointer};
+ uint8_t accumulator{0};
+ uint8_t indexX{0};
+ uint8_t indexY{0};
+ uint8_t stack{initialStackPointer};
uint16_t pc{initialProgramCounter};
Flags flags;
- constexpr static auto memorySize = 65536U;
+ constexpr static auto memorySize = 65536U;
using Memory = std::array<uint8_t, memorySize>;
Memory memory{};
@@ 114,8 113,8 @@ public:
constexpr void push2(uint8_t) = delete;
private:
- constexpr static auto stackTop = 0x100;
- constexpr static auto initialStackPointer = 0xfd;
+ constexpr static auto stackTop = 0x100;
+ constexpr static auto initialStackPointer = 0xfd;
constexpr static auto initialProgramCounter = 0x600;
bool indirectJumpBug = true;
@@ 126,11 125,11 @@ private:
// Instruction helpers
constexpr auto getTarget(AddressMode mode) noexcept -> ValueStore;
- [[nodiscard]]
- constexpr auto read(uint16_t address) const noexcept -> uint8_t;
- [[nodiscard]]
- constexpr auto read2(uint16_t address, bool wrapToPage = false) const
- noexcept -> uint16_t;
+ [[nodiscard]] constexpr auto read(uint16_t address) const noexcept
+ -> uint8_t;
+ [[nodiscard]] constexpr auto
+ read2(uint16_t address, bool wrapToPage = false) const noexcept
+ -> uint16_t;
constexpr void write(uint16_t address, uint8_t value) noexcept;
constexpr void push(uint8_t) noexcept;
constexpr void push2(uint16_t) noexcept;
@@ 139,7 138,7 @@ private:
constexpr void popFlags() noexcept;
constexpr void branch(uint16_t) noexcept;
- template<class T, class... Args>
+ template <class T, class... Args>
constexpr void calculateFlag(uint8_t value, T flag, Args... flags);
constexpr void calculateFlag(uint8_t value, Flags::Index flag) noexcept;
constexpr void compare(uint8_t a, uint8_t b) noexcept;
@@ 206,22 205,17 @@ private:
friend class ValueStore;
};
-constexpr Flags::Flags(uint8_t value)
-: value{value}
-{
-}
+constexpr Flags::Flags(uint8_t value) : value{value} {}
constexpr auto Flags::bitmask(Index i) noexcept -> uint8_t {
return 1U << static_cast<uint8_t>(i);
}
-[[nodiscard]]
-constexpr auto Flags::get() const noexcept -> uint8_t {
+[[nodiscard]] constexpr auto Flags::get() const noexcept -> uint8_t {
return value;
}
-[[nodiscard]]
-constexpr auto Flags::test(Index i) const noexcept -> bool {
+[[nodiscard]] constexpr auto Flags::test(Index i) const noexcept -> bool {
return (value & bitmask(i)) != 0;
}
@@ 233,33 227,18 @@ constexpr void Flags::set(Index i, bool set) noexcept {
value &= static_cast<uint8_t>(~mask);
}
-constexpr void Flags::reset() noexcept {
- value = getDefault();
-}
+constexpr void Flags::reset() noexcept { value = getDefault(); }
-constexpr auto Flags::operator ==(const Flags& rhs) const noexcept -> bool {
+constexpr auto Flags::operator==(const Flags &rhs) const noexcept -> bool {
return value == rhs.value;
}
-constexpr ValueStore::ValueStore(
- CPU& cpu,
- uint16_t value,
- Type type
-) : value{value},
- type{type},
- cpu{cpu}
-{
-}
+constexpr ValueStore::ValueStore(CPU &cpu, uint16_t value, Type type)
+ : value{value}, type{type}, cpu{cpu} {}
-constexpr ValueStore::ValueStore(CPU& cpu)
-: value{0},
- type{Type::Accumulator},
- cpu{cpu}
-{
-}
+constexpr ValueStore::ValueStore(CPU &cpu)
+ : value{0}, type{Type::Accumulator}, cpu{cpu} {}
-constexpr auto ValueStore::get() const noexcept -> uint16_t {
- return value;
-}
+constexpr auto ValueStore::get() const noexcept -> uint16_t { return value; }
} // namespace microlator
M test/nestest.hpp => test/nestest.hpp +4 -0
@@ 23,6 23,7 @@ struct cpuState {
};
constexpr auto nestestProgram = std::to_array<uint8_t>({
+ // clang-format off
0x4c, 0xf5, 0xc5, 0x60, 0x78, 0xd8, 0xa2, 0xff, 0x9a, 0xad, 0x02, 0x20,
0x10, 0xfb, 0xad, 0x02, 0x20, 0x10, 0xfb, 0xa9, 0x00, 0x8d, 0x00, 0x20,
0x8d, 0x01, 0x20, 0x8d, 0x05, 0x20, 0x8d, 0x05, 0x20, 0xad, 0x02, 0x20,
@@ 1389,9 1390,11 @@ constexpr auto nestestProgram = std::to_array<uint8_t>({
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xc5,
0x04, 0xc0, 0xf4, 0xc5,
+ // clang-format on
});
constexpr auto nestestStates = std::to_array<const cpuState>({
+ // clang-format off
{0xC000, "JMP $C5F5", 0x00, 0x00, 0x00, 0x24, 0xFD, 0, 0, 7},
{0xC5F5, "LDX #$00", 0x00, 0x00, 0x00, 0x24, 0xFD, 9, 0, 10},
{0xC5F7, "STX $00 = 00", 0x00, 0x00, 0x00, 0x26, 0xFD, 15, 0, 12},
@@ 10383,4 10386,5 @@ constexpr auto nestestStates = std::to_array<const cpuState>({
{0xC69F, "STA $4007 = FF", 0x00, 0xFF, 0x15, 0x27, 0xFB, 158,233, 26544},
{0xC6A2, "RTS", 0x00, 0xFF, 0x15, 0x27, 0xFB, 170,233, 26548},
{0xC66E, "RTS", 0x00, 0xFF, 0x15, 0x27, 0xFD, 188,233, 26554},
+ // clang-format on
});
M test/testCPU.cpp => test/testCPU.cpp +28 -26
@@ 9,27 9,24 @@
namespace emu = microlator;
namespace Catch {
- template<>
- struct StringMaker<emu::Flags> {
- static auto convert(const emu::Flags& value) -> std::string {
- constexpr auto flags = std::to_array<char>({
- 'C', 'Z', 'I', 'D', 'B', '-', 'V', 'N'
- });
+template <> struct StringMaker<emu::Flags> {
+ static auto convert(const emu::Flags &value) -> std::string {
+ constexpr auto flags = std::to_array<char>(
+ {'C', 'Z', 'I', 'D', 'B', '-', 'V', 'N'});
- std::ostringstream os;
- os << '[';
- for (size_t i = 0; i <= flags.size(); i++) {
- size_t bit = flags.size() - i;
- os <<
- (value.test(static_cast<emu::Flags::Index>(bit))
- ? flags.at(bit)
- : ' ');
- }
- os << ']';
-
- return {os.str()};
+ std::ostringstream os;
+ os << '[';
+ for (size_t i = 0; i <= flags.size(); i++) {
+ size_t bit = flags.size() - i;
+ os << (value.test(static_cast<emu::Flags::Index>(bit))
+ ? flags.at(bit)
+ : ' ');
}
- };
+ os << ']';
+
+ return {os.str()};
+ }
+};
} // namespace Catch
TEST_CASE("CPU can execute", "[cpu]") {
@@ 45,19 42,24 @@ TEST_CASE("CPU passes nestest", "[cpu]") {
cpu.loadProgram(nestestProgram, 0xC000);
const auto *prev = nestestStates.begin();
- for (const auto *it = nestestStates.begin(); it != nestestStates.end(); ++it) {
+ for (const auto *it = nestestStates.begin(); it != nestestStates.end();
+ ++it) {
const auto state = *it;
INFO("Last instruction: " << prev->dis);
INFO("PC: " << std::hex << state.pc);
- REQUIRE(static_cast<unsigned>(cpu.pc) == static_cast<unsigned>(state.pc));
- REQUIRE(static_cast<unsigned>(cpu.accumulator) == static_cast<unsigned>(state.a));
- REQUIRE(static_cast<unsigned>(cpu.indexX) == static_cast<unsigned>(state.x));
- REQUIRE(static_cast<unsigned>(cpu.indexY) == static_cast<unsigned>(state.y));
+ REQUIRE(static_cast<unsigned>(cpu.pc) ==
+ static_cast<unsigned>(state.pc));
+ REQUIRE(static_cast<unsigned>(cpu.accumulator) ==
+ static_cast<unsigned>(state.a));
+ REQUIRE(static_cast<unsigned>(cpu.indexX) ==
+ static_cast<unsigned>(state.x));
+ REQUIRE(static_cast<unsigned>(cpu.indexY) ==
+ static_cast<unsigned>(state.y));
REQUIRE(cpu.flags == state.p);
- REQUIRE(static_cast<unsigned>(cpu.stack)
- == static_cast<unsigned>(state.sp));
+ REQUIRE(static_cast<unsigned>(cpu.stack) ==
+ static_cast<unsigned>(state.sp));
if (!cpu.step())
break;