1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-10-19 14:25:35 +00:00
CLK/Processors/6502/Implementation/6502Implementation.hpp
2019-03-09 21:16:11 -05:00

795 lines
28 KiB
C++

//
// 6502Implementation.hpp
// Clock Signal
//
// Created by Thomas Harte on 01/09/2017.
// Copyright 2017 Thomas Harte. All rights reserved.
//
/*
Here lies the implementations of those methods declared in the CPU::MOS6502::Processor template, or declared
as inline within CPU::MOS6502::ProcessorBase. So it's stuff that has to be in a header file, visible from
6502.hpp, but it's implementation stuff.
*/
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
static const MicroOp do_branch[] = {
CycleReadFromPC,
CycleAddSignedOperandToPC,
OperationMoveToNextProgram
};
static uint8_t throwaway_target;
static const MicroOp fetch_decode_execute[] = {
CycleFetchOperation,
CycleFetchOperand,
OperationDecodeOperation
};
// These plus program below act to give the compiler permission to update these values
// without touching the class storage (i.e. it explicitly says they need be completely up
// to date in this stack frame only); which saves some complicated addressing
RegisterPair16 nextAddress = next_address_;
BusOperation nextBusOperation = next_bus_operation_;
uint16_t busAddress = bus_address_;
uint8_t *busValue = bus_value_;
#define checkSchedule(op) \
if(!scheduled_program_counter_) {\
if(interrupt_requests_) {\
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
scheduled_program_counter_ = get_reset_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
scheduled_program_counter_ = get_nmi_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
scheduled_program_counter_ = get_irq_program();\
} \
} else {\
scheduled_program_counter_ = fetch_decode_execute;\
}\
op;\
}
#define bus_access() \
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::IRQ) | irq_request_history_; \
irq_request_history_ = irq_line_ & inverse_interrupt_flag_; \
number_of_cycles -= bus_handler_.perform_bus_operation(nextBusOperation, busAddress, busValue); \
nextBusOperation = BusOperation::None; \
if(number_of_cycles <= Cycles(0)) break;
checkSchedule();
Cycles number_of_cycles = cycles + cycles_left_to_run_;
while(number_of_cycles > Cycles(0)) {
// Deal with a potential RDY state, if this 6502 has anything connected to ready.
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
}
// Deal with a potential STP state, if this 6502 implements STP.
while(has_stpwai(personality) && stop_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
if(interrupt_requests_ & InterruptRequestFlags::Reset) {
stop_is_active_ = false;
checkSchedule();
break;
}
}
// Deal with a potential WAI state, if this 6502 implements WAI.
while(has_stpwai(personality) && wait_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
interrupt_requests_ |= (irq_line_ & inverse_interrupt_flag_);
if(interrupt_requests_ & InterruptRequestFlags::NMI || irq_line_) {
wait_is_active_ = false;
checkSchedule();
break;
}
}
if((!uses_ready_line || !ready_is_active_) && (!has_stpwai(personality) || (!wait_is_active_ && !stop_is_active_))) {
if(nextBusOperation != BusOperation::None) {
bus_access();
}
while(1) {
const MicroOp cycle = *scheduled_program_counter_;
scheduled_program_counter_++;
#define read_op(val, addr) nextBusOperation = BusOperation::ReadOpcode; busAddress = addr; busValue = &val; val = 0xff
#define read_mem(val, addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &val; val = 0xff
#define throwaway_read(addr) nextBusOperation = BusOperation::Read; busAddress = addr; busValue = &throwaway_target; throwaway_target = 0xff
#define write_mem(val, addr) nextBusOperation = BusOperation::Write; busAddress = addr; busValue = &val
switch(cycle) {
// MARK: - Fetch/Decode
case CycleFetchOperation: {
last_operation_pc_ = pc_;
pc_.full++;
read_op(operation_, last_operation_pc_.full);
} break;
case CycleFetchOperand:
// This is supposed to produce the 65C02's 1-cycle NOPs; they're
// treated as a special case because they break the rule that
// governs everything else on the 6502: that two bytes will always
// be fetched.
if(
!is_65c02(personality) ||
(operation_&7) != 3 ||
operation_ == 0xcb ||
operation_ == 0xdb
) {
read_mem(operand_, pc_.full);
break;
} else {
continue;
}
break;
case OperationDecodeOperation:
scheduled_program_counter_ = operations_[operation_];
continue;
case OperationMoveToNextProgram:
scheduled_program_counter_ = nullptr;
checkSchedule();
continue;
#define push(v) {\
uint16_t targetAddress = s_ | 0x100; s_--;\
write_mem(v, targetAddress);\
}
case CycleIncPCPushPCH: pc_.full++; // deliberate fallthrough
case CyclePushPCH: push(pc_.halves.high); break;
case CyclePushPCL: push(pc_.halves.low); break;
case CyclePushOperand: push(operand_); break;
case CyclePushA: push(a_); break;
case CyclePushX: push(x_); break;
case CyclePushY: push(y_); break;
case CycleNoWritePush: {
uint16_t targetAddress = s_ | 0x100; s_--;
read_mem(operand_, targetAddress);
}
break;
#undef push
case CycleReadFromS: throwaway_read(s_ | 0x100); break;
case CycleReadFromPC: throwaway_read(pc_.full); break;
case OperationBRKPickVector:
if(is_65c02(personality)) {
nextAddress.full = 0xfffe;
} else {
// NMI can usurp BRK-vector operations on the pre-C 6502s.
nextAddress.full = (interrupt_requests_ & InterruptRequestFlags::NMI) ? 0xfffa : 0xfffe;
interrupt_requests_ &= ~InterruptRequestFlags::NMI;
}
continue;
case OperationNMIPickVector: nextAddress.full = 0xfffa; continue;
case OperationRSTPickVector: nextAddress.full = 0xfffc; continue;
case CycleReadVectorLow: read_mem(pc_.halves.low, nextAddress.full); break;
case CycleReadVectorHigh: read_mem(pc_.halves.high, nextAddress.full+1); break;
case OperationSetIRQFlags:
inverse_interrupt_flag_ = 0;
if(is_65c02(personality)) decimal_flag_ = false;
continue;
case OperationSetNMIRSTFlags:
if(is_65c02(personality)) decimal_flag_ = false;
continue;
case CyclePullPCL: s_++; read_mem(pc_.halves.low, s_ | 0x100); break;
case CyclePullPCH: s_++; read_mem(pc_.halves.high, s_ | 0x100); break;
case CyclePullA: s_++; read_mem(a_, s_ | 0x100); break;
case CyclePullX: s_++; read_mem(x_, s_ | 0x100); break;
case CyclePullY: s_++; read_mem(y_, s_ | 0x100); break;
case CyclePullOperand: s_++; read_mem(operand_, s_ | 0x100); break;
case OperationSetFlagsFromOperand: set_flags(operand_); continue;
case OperationSetOperandFromFlagsWithBRKSet: operand_ = get_flags() | Flag::Break; continue;
case OperationSetOperandFromFlags: operand_ = get_flags(); continue;
case OperationSetFlagsFromA: zero_result_ = negative_result_ = a_; continue;
case OperationSetFlagsFromX: zero_result_ = negative_result_ = x_; continue;
case OperationSetFlagsFromY: zero_result_ = negative_result_ = y_; continue;
case CycleIncrementPCAndReadStack: pc_.full++; throwaway_read(s_ | 0x100); break;
case CycleReadPCLFromAddress: read_mem(pc_.halves.low, address_.full); break;
case CycleReadPCHFromAddressLowInc: address_.halves.low++; read_mem(pc_.halves.high, address_.full); break;
case CycleReadPCHFromAddressFixed: if(!address_.halves.low) address_.halves.high++; read_mem(pc_.halves.high, address_.full); break;
case CycleReadPCHFromAddressInc: address_.full++; read_mem(pc_.halves.high, address_.full); break;
case CycleReadAndIncrementPC: {
uint16_t oldPC = pc_.full;
pc_.full++;
throwaway_read(oldPC);
} break;
// MARK: - JAM, WAI, STP
case OperationScheduleJam: {
is_jammed_ = true;
scheduled_program_counter_ = operations_[CPU::MOS6502::JamOpcode];
} continue;
case OperationScheduleStop:
stop_is_active_ = true;
break;
case OperationScheduleWait:
wait_is_active_ = true;
break;
// MARK: - Bitwise
case OperationORA: a_ |= operand_; negative_result_ = zero_result_ = a_; continue;
case OperationAND: a_ &= operand_; negative_result_ = zero_result_ = a_; continue;
case OperationEOR: a_ ^= operand_; negative_result_ = zero_result_ = a_; continue;
// MARK: - Load and Store
case OperationLDA: a_ = negative_result_ = zero_result_ = operand_; continue;
case OperationLDX: x_ = negative_result_ = zero_result_ = operand_; continue;
case OperationLDY: y_ = negative_result_ = zero_result_ = operand_; continue;
case OperationLAX: a_ = x_ = negative_result_ = zero_result_ = operand_; continue;
case OperationCopyOperandToA: a_ = operand_; continue;
case OperationSTA: operand_ = a_; continue;
case OperationSTX: operand_ = x_; continue;
case OperationSTY: operand_ = y_; continue;
case OperationSTZ: operand_ = 0; continue;
case OperationSAX: operand_ = a_ & x_; continue;
case OperationSHA: operand_ = a_ & x_ & (address_.halves.high+1); continue;
case OperationSHX: operand_ = x_ & (address_.halves.high+1); continue;
case OperationSHY: operand_ = y_ & (address_.halves.high+1); continue;
case OperationSHS: s_ = a_ & x_; operand_ = s_ & (address_.halves.high+1); continue;
case OperationLXA:
a_ = x_ = (a_ | 0xee) & operand_;
negative_result_ = zero_result_ = a_;
continue;
// MARK: - Compare
case OperationCMP: {
const uint16_t temp16 = a_ - operand_;
negative_result_ = zero_result_ = static_cast<uint8_t>(temp16);
carry_flag_ = ((~temp16) >> 8)&1;
} continue;
case OperationCPX: {
const uint16_t temp16 = x_ - operand_;
negative_result_ = zero_result_ = static_cast<uint8_t>(temp16);
carry_flag_ = ((~temp16) >> 8)&1;
} continue;
case OperationCPY: {
const uint16_t temp16 = y_ - operand_;
negative_result_ = zero_result_ = static_cast<uint8_t>(temp16);
carry_flag_ = ((~temp16) >> 8)&1;
} continue;
// MARK: - BIT, TSB, TRB
case OperationBIT:
zero_result_ = operand_ & a_;
negative_result_ = operand_;
overflow_flag_ = operand_&Flag::Overflow;
continue;
case OperationBITNoNV:
zero_result_ = operand_ & a_;
continue;
case OperationTRB:
zero_result_ = operand_ & a_;
operand_ &= ~a_;
continue;
case OperationTSB:
zero_result_ = operand_ & a_;
operand_ |= a_;
continue;
// MARK: - RMB and SMB
case OperationRMB:
operand_ &= ~(1 << (operation_ >> 4));
continue;
case OperationSMB:
operand_ |= 1 << ((operation_ >> 4)&7);
continue;
// MARK: - ADC/SBC (and INS)
case OperationINS:
operand_++; // deliberate fallthrough
case OperationSBC:
if(decimal_flag_ && has_decimal_mode(personality)) {
const uint16_t notCarry = carry_flag_ ^ 0x1;
const uint16_t decimalResult = static_cast<uint16_t>(a_) - static_cast<uint16_t>(operand_) - notCarry;
uint16_t temp16;
temp16 = (a_&0xf) - (operand_&0xf) - notCarry;
if(temp16 > 0xf) temp16 -= 0x6;
temp16 = (temp16&0x0f) | ((temp16 > 0x0f) ? 0xfff0 : 0x00);
temp16 += (a_&0xf0) - (operand_&0xf0);
overflow_flag_ = ( ( (decimalResult^a_)&(~decimalResult^operand_) )&0x80) >> 1;
negative_result_ = static_cast<uint8_t>(temp16);
zero_result_ = static_cast<uint8_t>(decimalResult);
if(temp16 > 0xff) temp16 -= 0x60;
carry_flag_ = (temp16 > 0xff) ? 0 : Flag::Carry;
a_ = static_cast<uint8_t>(temp16);
if(is_65c02(personality)) {
negative_result_ = zero_result_ = a_;
read_mem(operand_, address_.full);
break;
}
continue;
} else {
operand_ = ~operand_;
}
// deliberate fallthrough
case OperationADC:
if(decimal_flag_ && has_decimal_mode(personality)) {
const uint16_t decimalResult = static_cast<uint16_t>(a_) + static_cast<uint16_t>(operand_) + static_cast<uint16_t>(carry_flag_);
uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + carry_flag_;
if(low_nibble >= 0xa) low_nibble = ((low_nibble + 0x6) & 0xf) + 0x10;
uint16_t result = static_cast<uint16_t>(a_ & 0xf0) + static_cast<uint16_t>(operand_ & 0xf0) + static_cast<uint16_t>(low_nibble);
negative_result_ = static_cast<uint8_t>(result);
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
if(result >= 0xa0) result += 0x60;
carry_flag_ = (result >> 8) ? 1 : 0;
a_ = static_cast<uint8_t>(result);
zero_result_ = static_cast<uint8_t>(decimalResult);
if(is_65c02(personality)) {
negative_result_ = zero_result_ = a_;
read_mem(operand_, address_.full);
break;
}
} else {
const uint16_t result = static_cast<uint16_t>(a_) + static_cast<uint16_t>(operand_) + static_cast<uint16_t>(carry_flag_);
overflow_flag_ = (( (result^a_)&(result^operand_) )&0x80) >> 1;
negative_result_ = zero_result_ = a_ = static_cast<uint8_t>(result);
carry_flag_ = (result >> 8)&1;
}
// fix up in case this was INS
if(cycle == OperationINS) operand_ = ~operand_;
continue;
// MARK: - Shifts and Rolls
case OperationASL:
carry_flag_ = operand_ >> 7;
operand_ <<= 1;
negative_result_ = zero_result_ = operand_;
continue;
case OperationASO:
carry_flag_ = operand_ >> 7;
operand_ <<= 1;
a_ |= operand_;
negative_result_ = zero_result_ = a_;
continue;
case OperationROL: {
const uint8_t temp8 = static_cast<uint8_t>((operand_ << 1) | carry_flag_);
carry_flag_ = operand_ >> 7;
operand_ = negative_result_ = zero_result_ = temp8;
} continue;
case OperationRLA: {
const uint8_t temp8 = static_cast<uint8_t>((operand_ << 1) | carry_flag_);
carry_flag_ = operand_ >> 7;
operand_ = temp8;
a_ &= operand_;
negative_result_ = zero_result_ = a_;
} continue;
case OperationLSR:
carry_flag_ = operand_ & 1;
operand_ >>= 1;
negative_result_ = zero_result_ = operand_;
continue;
case OperationLSE:
carry_flag_ = operand_ & 1;
operand_ >>= 1;
a_ ^= operand_;
negative_result_ = zero_result_ = a_;
continue;
case OperationASR:
a_ &= operand_;
carry_flag_ = a_ & 1;
a_ >>= 1;
negative_result_ = zero_result_ = a_;
continue;
case OperationROR: {
const uint8_t temp8 = static_cast<uint8_t>((operand_ >> 1) | (carry_flag_ << 7));
carry_flag_ = operand_ & 1;
operand_ = negative_result_ = zero_result_ = temp8;
} continue;
case OperationRRA: {
const uint8_t temp8 = static_cast<uint8_t>((operand_ >> 1) | (carry_flag_ << 7));
carry_flag_ = operand_ & 1;
operand_ = temp8;
} continue;
case OperationDecrementOperand: operand_--; continue;
case OperationIncrementOperand: operand_++; continue;
case OperationCLC: carry_flag_ = 0; continue;
case OperationCLI: inverse_interrupt_flag_ = Flag::Interrupt; continue;
case OperationCLV: overflow_flag_ = 0; continue;
case OperationCLD: decimal_flag_ = 0; continue;
case OperationSEC: carry_flag_ = Flag::Carry; continue;
case OperationSEI: inverse_interrupt_flag_ = 0; continue;
case OperationSED: decimal_flag_ = Flag::Decimal; continue;
case OperationINC: operand_++; negative_result_ = zero_result_ = operand_; continue;
case OperationDEC: operand_--; negative_result_ = zero_result_ = operand_; continue;
case OperationINA: a_++; negative_result_ = zero_result_ = a_; continue;
case OperationDEA: a_--; negative_result_ = zero_result_ = a_; continue;
case OperationINX: x_++; negative_result_ = zero_result_ = x_; continue;
case OperationDEX: x_--; negative_result_ = zero_result_ = x_; continue;
case OperationINY: y_++; negative_result_ = zero_result_ = y_; continue;
case OperationDEY: y_--; negative_result_ = zero_result_ = y_; continue;
case OperationANE:
a_ = (a_ | 0xee) & operand_ & x_;
negative_result_ = zero_result_ = a_;
continue;
case OperationANC:
a_ &= operand_;
negative_result_ = zero_result_ = a_;
carry_flag_ = a_ >> 7;
continue;
case OperationLAS:
a_ = x_ = s_ = s_ & operand_;
negative_result_ = zero_result_ = a_;
continue;
// MARK: - Addressing Mode Work
#define page_crossing_stall_read() \
if(is_65c02(personality)) { \
throwaway_read(pc_.full - 1); \
} else { \
throwaway_read(address_.full); \
}
case CycleAddXToAddressLow:
nextAddress.full = address_.full + x_;
address_.halves.low = nextAddress.halves.low;
if(address_.halves.high != nextAddress.halves.high) {
page_crossing_stall_read();
break;
}
continue;
case CycleAddXToAddressLowRead:
nextAddress.full = address_.full + x_;
address_.halves.low = nextAddress.halves.low;
page_crossing_stall_read();
break;
case CycleAddYToAddressLow:
nextAddress.full = address_.full + y_;
address_.halves.low = nextAddress.halves.low;
if(address_.halves.high != nextAddress.halves.high) {
page_crossing_stall_read();
break;
}
continue;
case CycleAddYToAddressLowRead:
nextAddress.full = address_.full + y_;
address_.halves.low = nextAddress.halves.low;
page_crossing_stall_read();
break;
#undef page_crossing_stall_read
case OperationCorrectAddressHigh:
address_.full = nextAddress.full;
continue;
case CycleIncrementPCFetchAddressLowFromOperand:
pc_.full++;
read_mem(address_.halves.low, operand_);
break;
case CycleAddXToOperandFetchAddressLow:
operand_ += x_;
read_mem(address_.halves.low, operand_);
break;
case CycleFetchAddressLowFromOperand:
read_mem(address_.halves.low, operand_);
break;
case CycleIncrementOperandFetchAddressHigh:
operand_++;
read_mem(address_.halves.high, operand_);
break;
case CycleIncrementPCReadPCHLoadPCL: // deliberate fallthrough
pc_.full++;
case CycleReadPCHLoadPCL: {
uint16_t oldPC = pc_.full;
pc_.halves.low = operand_;
read_mem(pc_.halves.high, oldPC);
} break;
case CycleReadAddressHLoadAddressL:
address_.halves.low = operand_; pc_.full++;
read_mem(address_.halves.high, pc_.full);
break;
case CycleLoadAddressAbsolute: {
uint16_t nextPC = pc_.full+1;
pc_.full += 2;
address_.halves.low = operand_;
read_mem(address_.halves.high, nextPC);
} break;
case OperationLoadAddressZeroPage:
pc_.full++;
address_.full = operand_;
continue;
case CycleLoadAddessZeroX:
pc_.full++;
address_.full = (operand_ + x_)&0xff;
throwaway_read(operand_);
break;
case CycleLoadAddessZeroY:
pc_.full++;
address_.full = (operand_ + y_)&0xff;
throwaway_read(operand_);
break;
case OperationIncrementPC: pc_.full++; continue;
case CycleFetchOperandFromAddress: read_mem(operand_, address_.full); break;
case CycleWriteOperandToAddress: write_mem(operand_, address_.full); break;
// MARK: - Branching
#define BRA(condition) \
pc_.full++; \
if(condition) { \
scheduled_program_counter_ = do_branch; \
}
case OperationBPL: BRA(!(negative_result_&0x80)); continue;
case OperationBMI: BRA(negative_result_&0x80); continue;
case OperationBVC: BRA(!overflow_flag_); continue;
case OperationBVS: BRA(overflow_flag_); continue;
case OperationBCC: BRA(!carry_flag_); continue;
case OperationBCS: BRA(carry_flag_); continue;
case OperationBNE: BRA(zero_result_); continue;
case OperationBEQ: BRA(!zero_result_); continue;
case OperationBRA: BRA(true); continue;
#undef BRA
case CycleAddSignedOperandToPC:
nextAddress.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
pc_.halves.low = nextAddress.halves.low;
if(nextAddress.halves.high != pc_.halves.high) {
uint16_t halfUpdatedPc = pc_.full;
pc_.full = nextAddress.full;
throwaway_read(halfUpdatedPc);
break;
} else if(is_65c02(personality)) {
// 65C02 modification to all branches: a branch that is taken but requires only a single cycle
// to target its destination skips any pending interrupts.
// Cf. http://forum.6502.org/viewtopic.php?f=4&t=1634
scheduled_program_counter_ = fetch_decode_execute;
}
continue;
case CycleFetchFromHalfUpdatedPC: {
uint16_t halfUpdatedPc = static_cast<uint16_t>(((pc_.halves.low + (int8_t)operand_) & 0xff) | (pc_.halves.high << 8));
throwaway_read(halfUpdatedPc);
} break;
case OperationAddSignedOperandToPC16:
pc_.full = static_cast<uint16_t>(pc_.full + (int8_t)operand_);
continue;
case OperationBBRBBS: {
// To reach here, the 6502 has (i) read the operation; (ii) read the first operand;
// and (iii) read from the corresponding zero page.
const uint8_t mask = static_cast<uint8_t>(1 << ((operation_ >> 4)&7));
if((operand_ & mask) == ((operation_ & 0x80) ? mask : 0)) {
static const MicroOp do_branch[] = {
CycleFetchOperand, // Fetch offset.
OperationIncrementPC,
CycleFetchFromHalfUpdatedPC,
OperationAddSignedOperandToPC16,
OperationMoveToNextProgram
};
scheduled_program_counter_ = do_branch;
} else {
static const MicroOp do_not_branch[] = {
CycleFetchOperand,
OperationIncrementPC,
CycleFetchFromHalfUpdatedPC,
OperationMoveToNextProgram
};
scheduled_program_counter_ = do_not_branch;
}
} break;
// MARK: - Transfers
case OperationTXA: zero_result_ = negative_result_ = a_ = x_; continue;
case OperationTYA: zero_result_ = negative_result_ = a_ = y_; continue;
case OperationTXS: s_ = x_; continue;
case OperationTAY: zero_result_ = negative_result_ = y_ = a_; continue;
case OperationTAX: zero_result_ = negative_result_ = x_ = a_; continue;
case OperationTSX: zero_result_ = negative_result_ = x_ = s_; continue;
case OperationARR:
if(decimal_flag_) {
a_ &= operand_;
uint8_t unshiftedA = a_;
a_ = static_cast<uint8_t>((a_ >> 1) | (carry_flag_ << 7));
zero_result_ = negative_result_ = a_;
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
if((unshiftedA&0xf) + (unshiftedA&0x1) > 5) a_ = ((a_ + 6)&0xf) | (a_ & 0xf0);
carry_flag_ = ((unshiftedA&0xf0) + (unshiftedA&0x10) > 0x50) ? 1 : 0;
if(carry_flag_) a_ += 0x60;
} else {
a_ &= operand_;
a_ = static_cast<uint8_t>((a_ >> 1) | (carry_flag_ << 7));
negative_result_ = zero_result_ = a_;
carry_flag_ = (a_ >> 6)&1;
overflow_flag_ = (a_^(a_ << 1))&Flag::Overflow;
}
continue;
case OperationSBX:
x_ &= a_;
uint16_t difference = x_ - operand_;
x_ = static_cast<uint8_t>(difference);
negative_result_ = zero_result_ = x_;
carry_flag_ = ((difference >> 8)&1)^1;
continue;
}
if(has_stpwai(personality) && (stop_is_active_ || wait_is_active_)) {
break;
}
if(uses_ready_line && ready_line_is_enabled_ && (is_65c02(personality) || isReadOperation(nextBusOperation))) {
ready_is_active_ = true;
break;
}
bus_access();
}
}
}
cycles_left_to_run_ = number_of_cycles;
next_address_ = nextAddress;
next_bus_operation_ = nextBusOperation;
bus_address_ = busAddress;
bus_value_ = busValue;
bus_handler_.flush();
}
template <Personality personality, typename T, bool uses_ready_line> void Processor<personality, T, uses_ready_line>::set_ready_line(bool active) {
assert(uses_ready_line);
if(active) {
ready_line_is_enabled_ = true;
} else {
ready_line_is_enabled_ = false;
ready_is_active_ = false;
}
}
void ProcessorBase::set_reset_line(bool active) {
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::Reset) | (active ? InterruptRequestFlags::Reset : 0);
}
bool ProcessorBase::get_is_resetting() {
return !!(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn));
}
void ProcessorBase::set_power_on(bool active) {
interrupt_requests_ = (interrupt_requests_ & ~InterruptRequestFlags::PowerOn) | (active ? InterruptRequestFlags::PowerOn : 0);
}
void ProcessorBase::set_irq_line(bool active) {
irq_line_ = active ? Flag::Interrupt : 0;
}
void ProcessorBase::set_overflow_line(bool active) {
// a leading edge will set the overflow flag
if(active && !set_overflow_line_is_enabled_)
overflow_flag_ = Flag::Overflow;
set_overflow_line_is_enabled_ = active;
}
void ProcessorBase::set_nmi_line(bool active) {
// NMI is edge triggered, not level
if(active && !nmi_line_is_enabled_)
interrupt_requests_ |= InterruptRequestFlags::NMI;
nmi_line_is_enabled_ = active;
}
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_reset_program() {
static const MicroOp reset[] = {
CycleFetchOperand,
CycleFetchOperand,
CycleNoWritePush,
CycleNoWritePush,
OperationRSTPickVector,
CycleNoWritePush,
OperationSetNMIRSTFlags,
CycleReadVectorLow,
CycleReadVectorHigh,
OperationMoveToNextProgram
};
return reset;
}
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_irq_program() {
static const MicroOp reset[] = {
CycleFetchOperand,
CycleFetchOperand,
CyclePushPCH,
CyclePushPCL,
OperationBRKPickVector,
OperationSetOperandFromFlags,
CyclePushOperand,
OperationSetIRQFlags,
CycleReadVectorLow,
CycleReadVectorHigh,
OperationMoveToNextProgram
};
return reset;
}
inline const ProcessorStorage::MicroOp *ProcessorStorage::get_nmi_program() {
static const MicroOp reset[] = {
CycleFetchOperand,
CycleFetchOperand,
CyclePushPCH,
CyclePushPCL,
OperationNMIPickVector,
OperationSetOperandFromFlags,
CyclePushOperand,
OperationSetNMIRSTFlags,
CycleReadVectorLow,
CycleReadVectorHigh,
OperationMoveToNextProgram
};
return reset;
}
uint8_t ProcessorStorage::get_flags() {
return carry_flag_ | overflow_flag_ | (inverse_interrupt_flag_ ^ Flag::Interrupt) | (negative_result_ & 0x80) | (zero_result_ ? 0 : Flag::Zero) | Flag::Always | decimal_flag_;
}
void ProcessorStorage::set_flags(uint8_t flags) {
carry_flag_ = flags & Flag::Carry;
negative_result_ = flags & Flag::Sign;
zero_result_ = (~flags) & Flag::Zero;
overflow_flag_ = flags & Flag::Overflow;
inverse_interrupt_flag_ = (~flags) & Flag::Interrupt;
decimal_flag_ = flags & Flag::Decimal;
}