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