diff --git a/OSBindings/Mac/Clock SignalTests/6502Mk2Tests.mm b/OSBindings/Mac/Clock SignalTests/6502Mk2Tests.mm index acbc0004d..67c0deaa9 100644 --- a/OSBindings/Mac/Clock SignalTests/6502Mk2Tests.mm +++ b/OSBindings/Mac/Clock SignalTests/6502Mk2Tests.mm @@ -120,7 +120,7 @@ void testExecution(NSDictionary *test, BusHandler &handler) { instruction.operation == CPU::MOS6502Mk2::Operation::SBC ) && instruction.mode == CPU::MOS6502Mk2::AddressingMode::Immediate && - processor.registers().flags.decimal; + processor.registers().flags.template get(); ignore_addresses[3] = instruction.mode == CPU::MOS6502Mk2::AddressingMode::JMPAbsoluteIndexedIndirect; } @@ -169,7 +169,7 @@ void testExecution(NSDictionary *test, BusHandler &handler) { auto repeat_processor = make_processor(test, handler); const bool should_interrupt = instruction.operation != CPU::MOS6502Mk2::Operation::BRK && - repeat_processor.registers().flags.inverse_interrupt; + !repeat_processor.registers().flags.template get(); try { repeat_processor.run_for(Cycles(last_length - 1)); diff --git a/Processors/6502Mk2/Implementation/6502.hpp b/Processors/6502Mk2/Implementation/6502.hpp index 19e35053e..96bc3bd6e 100644 --- a/Processors/6502Mk2/Implementation/6502.hpp +++ b/Processors/6502Mk2/Implementation/6502.hpp @@ -42,11 +42,12 @@ void Processor::run_for(const Cycles cycles) { }; const auto check_interrupt = [&] { Storage::captured_interrupt_requests_ = - Storage::inputs_.interrupt_requests & - (Storage::registers_.flags.inverse_interrupt | ~InterruptRequest::IRQ); + Storage::inputs_.interrupt_requests & Storage::registers_.flags.interrupt_mask(); }; - #define restore_point() (__COUNTER__ + int(ResumePoint::Max) + int(AddressingMode::Max)) + // This is a header file, so exclusive use of the counter can't be guaranteed; adjust for that via FirstCounter. + static constexpr auto FirstCounter = __COUNTER__; + #define restore_point() (__COUNTER__ - FirstCounter + int(ResumePoint::Max) + int(AddressingMode::Max)) #define join(a, b) a##b #define attach(a, b) join(a, b) @@ -109,12 +110,12 @@ void Processor::run_for(const Cycles cycles) { ( Storage::decoded_.operation == Operation::ADC || Storage::decoded_.operation == Operation::SBC - ) && registers.flags.decimal; + ) && registers.flags.template get(); }; const auto set_interrupt_flag = [&] { - registers.flags.inverse_interrupt = 0; + registers.flags.template set_per(0); if constexpr (is_65c02(model)) { - registers.flags.decimal = 0; + registers.flags.template set_per(0); } }; diff --git a/Processors/6502Mk2/Perform.hpp b/Processors/6502Mk2/Perform.hpp index f1349608e..686d49d63 100644 --- a/Processors/6502Mk2/Perform.hpp +++ b/Processors/6502Mk2/Perform.hpp @@ -21,29 +21,30 @@ namespace Operations { template void ane(RegistersT ®isters, const uint8_t operand) { registers.a = (registers.a | 0xee) & operand & registers.x; - registers.flags.set_nz(registers.a); + registers.flags.template set_per(registers.a); } template void anc(RegistersT ®isters, const uint8_t operand) { registers.a &= operand; - registers.flags.set_nz(registers.a); - registers.flags.carry = registers.a >> 7; + registers.flags.template set_per(registers.a); + registers.flags.template set_per(registers.a >> 7); } template void adc(RegistersT ®isters, const uint8_t operand) { - uint8_t result = registers.a + operand + registers.flags.carry; - registers.flags.carry = result < registers.a + registers.flags.carry; + uint8_t result = registers.a + operand + registers.flags.carry_value(); + uint8_t carry = result < registers.a + registers.flags.carry_value(); - if(!has_decimal_mode(model) || !registers.flags.decimal) { - registers.flags.set_v(result, registers.a, operand); - registers.flags.set_nz(registers.a = result); + if(!has_decimal_mode(model) || !registers.flags.template get()) { + registers.flags.set_overflow(result, registers.a, operand); + registers.flags.template set_per(registers.a = result); + registers.flags.template set_per(carry); return; } if constexpr (!is_65c02(model)) { - registers.flags.zero_result = result; + registers.flags.template set_per(result); } // General ADC logic: @@ -60,46 +61,47 @@ void adc(RegistersT ®isters, const uint8_t operand) { if(Numeric::carried_in<4>(registers.a, operand, result)) { result = (result & 0xf0) | ((result + 0x06) & 0x0f); } else if((result & 0xf) > 0x9) { - registers.flags.carry |= result >= 0x100 - 0x6; + carry |= result >= 0x100 - 0x6; result += 0x06; } // 6502 quirk: N and V are set before the full result is computed but // after the low nibble has been corrected. if constexpr (!is_65c02(model)) { - registers.flags.negative_result = result; + registers.flags.template set_per(result); } - registers.flags.set_v(result, registers.a, operand); + registers.flags.set_overflow(result, registers.a, operand); // i.e. fix high nibble if there was carry out of bit 7 already, or if the // top nibble is too large (in which case there will be carry after the fix-up). - registers.flags.carry |= result >= 0xa0; - if(registers.flags.carry) { + carry |= result >= 0xa0; + if(carry) { result += 0x60; } registers.a = result; + registers.flags.template set_per(carry); if constexpr (is_65c02(model)) { - registers.flags.set_nz(registers.a); + registers.flags.template set_per(registers.a); } } template void sbc(RegistersT ®isters, const uint8_t operand) { - if(!has_decimal_mode(model) || !registers.flags.decimal) { + if(!has_decimal_mode(model) || !registers.flags.template get()) { adc(registers, ~operand); // Lie about the model to carry forward the fact of not-decimal. return; } const uint8_t operand_complement = ~operand; - uint8_t result = registers.a + operand_complement + registers.flags.carry; + uint8_t result = registers.a + operand_complement + registers.flags.carry_value(); // All flags are set based only on the decimal result. - registers.flags.carry = result < registers.a + registers.flags.carry; + uint8_t carry = result < registers.a + registers.flags.carry_value(); if constexpr (!is_65c02(model)) { - registers.flags.set_nz(result); + registers.flags.template set_per(result); } - registers.flags.set_v(result, registers.a, operand_complement); + registers.flags.set_overflow(result, registers.a, operand_complement); // General SBC logic: // @@ -122,13 +124,14 @@ void sbc(RegistersT ®isters, const uint8_t operand) { } // The top nibble is adjusted only if there was borrow out of the whole byte. - if(!registers.flags.carry) { + if(!carry) { result += 0xa0; } registers.a = result; + registers.flags.template set_per(carry); if constexpr (is_65c02(model)) { - registers.flags.set_nz(registers.a); + registers.flags.template set_per(registers.a); } } @@ -136,123 +139,124 @@ template void arr(RegistersT ®isters, const uint8_t operand) { registers.a &= operand; const uint8_t unshifted_a = registers.a; - registers.a = uint8_t((registers.a >> 1) | (registers.flags.carry << 7)); - registers.flags.set_nz(registers.a); - registers.flags.overflow = (registers.a^(registers.a << 1))&Flag::Overflow; + registers.a = uint8_t((registers.a >> 1) | (registers.flags.carry_value() << 7)); + registers.flags.template set_per(registers.a); + registers.flags.template set_per(uint8_t(registers.a ^ (registers.a << 1))); - if(registers.flags.decimal && has_decimal_mode(model)) { + if(registers.flags.template get() && has_decimal_mode(model)) { if((unshifted_a&0xf) + (unshifted_a&0x1) > 5) registers.a = ((registers.a + 6)&0xf) | (registers.a & 0xf0); - registers.flags.carry = ((unshifted_a&0xf0) + (unshifted_a&0x10) > 0x50) ? 1 : 0; - if(registers.flags.carry) registers.a += 0x60; + const uint8_t carry = ((unshifted_a&0xf0) + (unshifted_a&0x10) > 0x50) ? 1 : 0; + if(carry) registers.a += 0x60; + registers.flags.template set_per(carry); } else { - registers.flags.carry = (registers.a >> 6)&1; + registers.flags.template set_per((registers.a >> 6)&1); } } template void sbx(RegistersT ®isters, const uint8_t operand) { registers.x &= registers.a; - registers.flags.carry = operand <= registers.x; + registers.flags.template set_per(operand <= registers.x); registers.x -= operand; - registers.flags.set_nz(registers.x); + registers.flags.template set_per(registers.x); } template void asl(RegistersT ®isters, uint8_t &operand) { - registers.flags.carry = operand >> 7; + registers.flags.template set_per(operand >> 7); operand <<= 1; - registers.flags.set_nz(operand); + registers.flags.template set_per(operand); } template void aso(RegistersT ®isters, uint8_t &operand) { - registers.flags.carry = operand >> 7; + registers.flags.template set_per(operand >> 7); operand <<= 1; registers.a |= operand; - registers.flags.set_nz(registers.a); + registers.flags.template set_per(registers.a); } template void rol(RegistersT ®isters, uint8_t &operand) { - const uint8_t temp8 = uint8_t((operand << 1) | registers.flags.carry); - registers.flags.carry = operand >> 7; - registers.flags.set_nz(operand = temp8); + const uint8_t temp8 = uint8_t((operand << 1) | registers.flags.carry_value()); + registers.flags.template set_per(operand >> 7); + registers.flags.template set_per(operand = temp8); } template void rla(RegistersT ®isters, uint8_t &operand) { - const uint8_t temp8 = uint8_t((operand << 1) | registers.flags.carry); - registers.flags.carry = operand >> 7; + const uint8_t temp8 = uint8_t((operand << 1) | registers.flags.carry_value()); + registers.flags.template set_per(operand >> 7); operand = temp8; registers.a &= operand; - registers.flags.set_nz(registers.a); + registers.flags.template set_per(registers.a); } template void lsr(RegistersT ®isters, uint8_t &operand) { - registers.flags.carry = operand & 1; + registers.flags.template set_per(operand & 1); operand >>= 1; - registers.flags.set_nz(operand); + registers.flags.template set_per(operand); } template void lse(RegistersT ®isters, uint8_t &operand) { - registers.flags.carry = operand & 1; + registers.flags.template set_per(operand & 1); operand >>= 1; registers.a ^= operand; - registers.flags.set_nz(registers.a); + registers.flags.template set_per(registers.a); } template void asr(RegistersT ®isters, uint8_t &operand) { registers.a &= operand; - registers.flags.carry = registers.a & 1; + registers.flags.template set_per(registers.a & 1); registers.a >>= 1; - registers.flags.set_nz(registers.a); + registers.flags.template set_per(registers.a); } template void ror(RegistersT ®isters, uint8_t &operand) { - const uint8_t temp8 = uint8_t((operand >> 1) | (registers.flags.carry << 7)); - registers.flags.carry = operand & 1; - registers.flags.set_nz(operand = temp8); + const uint8_t temp8 = uint8_t((operand >> 1) | (registers.flags.carry_value() << 7)); + registers.flags.template set_per(operand & 1); + registers.flags.template set_per(operand = temp8); } template void rra(RegistersT ®isters, uint8_t &operand) { - const uint8_t temp8 = uint8_t((operand >> 1) | (registers.flags.carry << 7)); - registers.flags.carry = operand & 1; + const uint8_t temp8 = uint8_t((operand >> 1) | (registers.flags.carry_value() << 7)); + registers.flags.template set_per(operand & 1); Operations::adc(registers, temp8); operand = temp8; } template void compare(RegistersT ®isters, const uint8_t lhs, const uint8_t rhs) { - registers.flags.carry = rhs <= lhs; - registers.flags.set_nz(lhs - rhs); + registers.flags.template set_per(rhs <= lhs); + registers.flags.template set_per(lhs - rhs); } template void bit(RegistersT ®isters, const uint8_t operand) { - registers.flags.zero_result = operand & registers.a; - registers.flags.negative_result = operand; - registers.flags.overflow = operand & Flag::Overflow; + registers.flags.template set_per(operand & registers.a); + registers.flags.template set_per(operand); + registers.flags.template set_per(operand); } template void bit_no_nv(RegistersT ®isters, const uint8_t operand) { - registers.flags.zero_result = operand & registers.a; + registers.flags.template set_per(operand & registers.a); } template void trb(RegistersT ®isters, uint8_t &operand) { - registers.flags.zero_result = operand & registers.a; + registers.flags.template set_per(operand & registers.a); operand &= ~registers.a; } template void tsb(RegistersT ®isters, uint8_t &operand) { - registers.flags.zero_result = operand & registers.a; + registers.flags.template set_per(operand & registers.a); operand |= registers.a; } @@ -309,14 +313,14 @@ bool test(const Operation operation, RegistersT ®isters) { default: __builtin_unreachable(); - case Operation::BPL: return !(registers.flags.negative_result & 0x80); - case Operation::BMI: return registers.flags.negative_result & 0x80; - case Operation::BVC: return !registers.flags.overflow; - case Operation::BVS: return registers.flags.overflow; - case Operation::BCC: return !registers.flags.carry; - case Operation::BCS: return registers.flags.carry; - case Operation::BNE: return registers.flags.zero_result; - case Operation::BEQ: return !registers.flags.zero_result; + case Operation::BPL: return !registers.flags.template get(); + case Operation::BMI: return registers.flags.template get(); + case Operation::BVC: return !registers.flags.template get(); + case Operation::BVS: return registers.flags.template get(); + case Operation::BCC: return !registers.flags.template get(); + case Operation::BCS: return registers.flags.template get(); + case Operation::BNE: return !registers.flags.template get(); + case Operation::BEQ: return registers.flags.template get(); case Operation::BRA: return true; } } @@ -335,112 +339,116 @@ void perform( const uint8_t opcode ) { switch(operation) { + using enum Operation; + default: __builtin_unreachable(); - case Operation::NOP: break; + case NOP: break; // MARK: - Bitwise logic. - case Operation::ORA: registers.flags.set_nz(registers.a |= operand); break; - case Operation::AND: registers.flags.set_nz(registers.a &= operand); break; - case Operation::EOR: registers.flags.set_nz(registers.a ^= operand); break; + case ORA: registers.flags.template set_per(registers.a |= operand); break; + case AND: registers.flags.template set_per(registers.a &= operand); break; + case EOR: registers.flags.template set_per(registers.a ^= operand); break; // MARK: - Loads and stores. - case Operation::LDA: registers.flags.set_nz(registers.a = operand); break; - case Operation::LDX: registers.flags.set_nz(registers.x = operand); break; - case Operation::LDY: registers.flags.set_nz(registers.y = operand); break; - case Operation::LAX: registers.flags.set_nz(registers.a = registers.x = operand); break; - case Operation::LXA: - registers.a = registers.x = (registers.a | 0xee) & operand; - registers.flags.set_nz(registers.a); + case LDA: registers.flags.template set_per(registers.a = operand); break; + case LDX: registers.flags.template set_per(registers.x = operand); break; + case LDY: registers.flags.template set_per(registers.y = operand); break; + case LAX: + registers.flags.template set_per(registers.a = registers.x = operand); break; - case Operation::PLP: registers.flags = Flags(operand); break; + case LXA: + registers.a = registers.x = (registers.a | 0xee) & operand; + registers.flags.template set_per(registers.a); + break; + case PLP: registers.flags = Flags(operand); break; - case Operation::STA: operand = registers.a; break; - case Operation::STX: operand = registers.x; break; - case Operation::STY: operand = registers.y; break; - case Operation::STZ: operand = 0; break; - case Operation::SAX: operand = registers.a & registers.x; break; - case Operation::PHP: operand = static_cast(registers.flags) | Flag::Break; break; + case STA: operand = registers.a; break; + case STX: operand = registers.x; break; + case STY: operand = registers.y; break; + case STZ: operand = 0; break; + case SAX: operand = registers.a & registers.x; break; + case PHP: operand = static_cast(registers.flags) | Flag::Break; break; - case Operation::CLC: registers.flags.carry = 0; break; - case Operation::CLI: registers.flags.inverse_interrupt = Flag::Interrupt; break; - case Operation::CLV: registers.flags.overflow = 0; break; - case Operation::CLD: registers.flags.decimal = 0; break; - case Operation::SEC: registers.flags.carry = Flag::Carry; break; - case Operation::SEI: registers.flags.inverse_interrupt = 0; break; - case Operation::SED: registers.flags.decimal = Flag::Decimal; break; + case CLC: registers.flags.template set_per(0); break; + case CLI: registers.flags.template set_per(0); break; + case CLV: registers.flags.template set_per(0); break; + case CLD: registers.flags.template set_per(0); break; + case SEC: registers.flags.template set_per(Flag::Carry); break; + case SEI: registers.flags.template set_per(Flag::Interrupt); break; + case SED: registers.flags.template set_per(Flag::Decimal); break; - case Operation::ANE: Operations::ane(registers, operand); break; - case Operation::ANC: Operations::anc(registers, operand); break; - case Operation::LAS: + case ANE: Operations::ane(registers, operand); break; + case ANC: Operations::anc(registers, operand); break; + case LAS: registers.a = registers.x = registers.s = registers.s & operand; - registers.flags.set_nz(registers.a); + registers.flags.template set_per(registers.a); break; // MARK: - Transfers. - case Operation::TXA: registers.flags.set_nz(registers.a = registers.x); break; - case Operation::TYA: registers.flags.set_nz(registers.a = registers.y); break; - case Operation::TXS: registers.s = registers.x; break; - case Operation::TAY: registers.flags.set_nz(registers.y = registers.a); break; - case Operation::TAX: registers.flags.set_nz(registers.x = registers.a); break; - case Operation::TSX: registers.flags.set_nz(registers.x = registers.s); break; + case TXA: registers.flags.template set_per(registers.a = registers.x); break; + case TYA: registers.flags.template set_per(registers.a = registers.y); break; + case TXS: registers.s = registers.x; break; + case TAY: registers.flags.template set_per(registers.y = registers.a); break; + case TAX: registers.flags.template set_per(registers.x = registers.a); break; + case TSX: registers.flags.template set_per(registers.x = registers.s); break; // MARK: - Increments and decrements. - case Operation::INC: registers.flags.set_nz(++operand); break; - case Operation::DEC: registers.flags.set_nz(--operand); break; - case Operation::INA: registers.flags.set_nz(++registers.a); break; - case Operation::DEA: registers.flags.set_nz(--registers.a); break; - case Operation::INX: registers.flags.set_nz(++registers.x); break; - case Operation::DEX: registers.flags.set_nz(--registers.x); break; - case Operation::INY: registers.flags.set_nz(++registers.y); break; - case Operation::DEY: registers.flags.set_nz(--registers.y); break; + case INC: registers.flags.template set_per(++operand); break; + case DEC: registers.flags.template set_per(--operand); break; + case INA: registers.flags.template set_per(++registers.a); break; + case DEA: registers.flags.template set_per(--registers.a); break; + case INX: registers.flags.template set_per(++registers.x); break; + case DEX: registers.flags.template set_per(--registers.x); break; + case INY: registers.flags.template set_per(++registers.y); break; + case DEY: registers.flags.template set_per(--registers.y); break; // MARK: - Shifts and rolls. - case Operation::ASL: Operations::asl(registers, operand); break; - case Operation::ASO: Operations::aso(registers, operand); break; - case Operation::ROL: Operations::rol(registers, operand); break; - case Operation::RLA: Operations::rla(registers, operand); break; - case Operation::LSR: Operations::lsr(registers, operand); break; - case Operation::LSE: Operations::lse(registers, operand); break; - case Operation::ASR: Operations::asr(registers, operand); break; - case Operation::ROR: Operations::ror(registers, operand); break; - case Operation::RRA: Operations::rra(registers, operand); break; + case ASL: Operations::asl(registers, operand); break; + case ASO: Operations::aso(registers, operand); break; + case ROL: Operations::rol(registers, operand); break; + case RLA: Operations::rla(registers, operand); break; + case LSR: Operations::lsr(registers, operand); break; + case LSE: Operations::lse(registers, operand); break; + case ASR: Operations::asr(registers, operand); break; + case ROR: Operations::ror(registers, operand); break; + case RRA: Operations::rra(registers, operand); break; // MARK: - Bit logic. - case Operation::BIT: Operations::bit(registers, operand); break; - case Operation::BITNoNV: Operations::bit_no_nv(registers, operand); break; - case Operation::TRB: Operations::trb(registers, operand); break; - case Operation::TSB: Operations::tsb(registers, operand); break; - case Operation::RMB: Operations::rmb(operand, opcode); break; - case Operation::SMB: Operations::smb(operand, opcode); break; + case BIT: Operations::bit(registers, operand); break; + case BITNoNV: Operations::bit_no_nv(registers, operand); break; + case TRB: Operations::trb(registers, operand); break; + case TSB: Operations::tsb(registers, operand); break; + case RMB: Operations::rmb(operand, opcode); break; + case SMB: Operations::smb(operand, opcode); break; // MARK: - Compare - case Operation::DCP: + case DCP: --operand; Operations::compare(registers, registers.a, operand); break; - case Operation::CMP: Operations::compare(registers, registers.a, operand); break; - case Operation::CPX: Operations::compare(registers, registers.x, operand); break; - case Operation::CPY: Operations::compare(registers, registers.y, operand); break; + case CMP: Operations::compare(registers, registers.a, operand); break; + case CPX: Operations::compare(registers, registers.x, operand); break; + case CPY: Operations::compare(registers, registers.y, operand); break; // MARK: - Arithmetic. - case Operation::INS: + case INS: ++operand; Operations::sbc(registers, operand); break; - case Operation::SBC: Operations::sbc(registers, operand); break; - case Operation::ADC: Operations::adc(registers, operand); break; - case Operation::ARR: Operations::arr(registers, operand); break; - case Operation::SBX: Operations::sbx(registers, operand); break; + case SBC: Operations::sbc(registers, operand); break; + case ADC: Operations::adc(registers, operand); break; + case ARR: Operations::arr(registers, operand); break; + case SBX: Operations::sbx(registers, operand); break; } } diff --git a/Processors/6502Mk2/Registers.hpp b/Processors/6502Mk2/Registers.hpp index 58c02e6a6..c48ba3987 100644 --- a/Processors/6502Mk2/Registers.hpp +++ b/Processors/6502Mk2/Registers.hpp @@ -8,6 +8,7 @@ #include "Numeric/RegisterSizes.hpp" +#include #include #pragma once @@ -37,7 +38,7 @@ enum class Register { the corresponding set. */ enum Flag: uint8_t { - Sign = 0b1000'0000, + Negative = 0b1000'0000, Overflow = 0b0100'0000, Always = 0b0010'0000, Break = 0b0001'0000, @@ -46,6 +47,12 @@ enum Flag: uint8_t { Zero = 0b0000'0010, Carry = 0b0000'0001, + // + // Psuedo-flags, for convenience setting and getting. + // + NegativeZero = 0b00, + InverseInterrupt = 0b11, + // // 65816 only. // @@ -53,36 +60,89 @@ enum Flag: uint8_t { IndexSize = Break, }; +constexpr bool is_stored(const Flag flag) { + switch(flag) { + case Flag::Negative: + case Flag::Overflow: + case Flag::Decimal: + case Flag::Interrupt: + case Flag::InverseInterrupt: + case Flag::Zero: + case Flag::Carry: + return true; + + default: + return false; + } +} + +constexpr bool is_settable(const Flag flag) { + switch(flag) { + case Flag::NegativeZero: return true; + default: return is_stored(flag); + } +} + struct Flags { - /// Sets N and Z flags per the 8-bit value @c value. - void set_nz(const uint8_t value) { - zero_result = negative_result = value; + template + requires(is_stored(flag)) + bool get() const { + switch(flag) { + default: __builtin_unreachable(); + case Flag::Negative: return negative_result & Flag::Negative; + case Flag::Overflow: return overflow; + case Flag::Decimal: return decimal; + case Flag::Interrupt: return !(inverse_interrupt & Flag::Interrupt); + case Flag::InverseInterrupt: return inverse_interrupt & Flag::Interrupt; + case Flag::Zero: return !zero_result; + case Flag::Carry: return carry & Flag::Carry; + } + return false; } - /// Sets N and Z flags per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value. - void set_nz(const uint16_t value, const int shift) { - negative_result = uint8_t(value >> shift); - zero_result = uint8_t(value | (value >> shift)); + /// Sets a flag based on an 8-bit ALU result. + template + requires(is_settable(flag)) + void set_per(const uint8_t result) { + switch(flag) { + default: __builtin_unreachable(); + + case Flag::Negative: negative_result = result; break; + case Flag::Overflow: overflow = result & Flag::Overflow; break; + case Flag::Decimal: decimal = result & Flag::Decimal; break; + case Flag::Interrupt: inverse_interrupt = uint8_t(~Flag::Interrupt) | uint8_t(~result); break; + case Flag::InverseInterrupt: inverse_interrupt = uint8_t(~Flag::Interrupt) | result; break; + case Flag::Zero: zero_result = result; break; + case Flag::Carry: + assert(!(result & ~Flag::Carry)); + carry = result; + break; + case Flag::NegativeZero: zero_result = negative_result = result; break; + } } - /// Sets the Z flag per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value. - void set_z(const uint16_t value, const int shift) { - zero_result = uint8_t(value | (value >> shift)); + template + requires(is_settable(flag)) + void set(const bool result) { + set_per(result ? flag : 0x00); } - /// Sets the N flag per the 8- or 16-bit value @c value; @c shift should be 0 to indicate an 8-bit value or 8 to indicate a 16-bit value. - void set_n(const uint16_t value, const int shift) { - negative_result = uint8_t(value >> shift); + uint8_t carry_value() const { + return carry; } - void set_v(const uint8_t result, const uint8_t lhs, const uint8_t rhs) { + uint8_t interrupt_mask() const { + return inverse_interrupt; + } + + void set_overflow(const uint8_t result, const uint8_t lhs, const uint8_t rhs) { // TODO: can this be done lazily? overflow = (( (result^lhs) & (result^rhs) ) & 0x80) >> 1; } explicit operator uint8_t() const { return - carry | overflow | (inverse_interrupt ^ Flag::Interrupt) | (negative_result & 0x80) | + carry | overflow | ((~inverse_interrupt) & Flag::Interrupt) | (negative_result & 0x80) | (zero_result ? 0 : Flag::Zero) | Flag::Always | Flag::Break | decimal; } @@ -95,23 +155,26 @@ struct Flags { } Flags(const uint8_t flags) { - carry = flags & Flag::Carry; - negative_result = flags & Flag::Sign; - zero_result = (~flags) & Flag::Zero; - overflow = flags & Flag::Overflow; - inverse_interrupt = (~flags) & Flag::Interrupt; - decimal = flags & Flag::Decimal; + set_per(flags & Flag::Carry); + set_per(flags); + set_per((~flags) & Flag::Zero); + set_per(flags); + set_per(flags); + set_per(flags); + + assert((flags | Flag::Always | Flag::Break) == static_cast(*this)); } auto operator <=> (const Flags &rhs) const { return static_cast(*this) <=> static_cast(rhs); } +private: + uint8_t overflow = 0; /// Contains Flag::Overflow. + uint8_t carry = 0; /// Contains Flag::Carry. uint8_t negative_result = 0; /// Bit 7 = the negative flag. uint8_t zero_result = 0; /// Non-zero if the zero flag is clear, zero if it is set. - uint8_t carry = 0; /// Contains Flag::Carry. uint8_t decimal = 0; /// Contains Flag::Decimal. - uint8_t overflow = 0; /// Contains Flag::Overflow. uint8_t inverse_interrupt = 0; /// Contains Flag::Interrupt, complemented. };