2020-09-28 02:20:58 +00:00
|
|
|
//
|
|
|
|
// 65816Implementation.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 27/09/2020.
|
|
|
|
// Copyright © 2020 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
template <typename BusHandler> void Processor<BusHandler>::run_for(const Cycles cycles) {
|
2020-09-29 22:42:07 +00:00
|
|
|
// Temporary storage for the next bus cycle.
|
|
|
|
uint32_t bus_address = 0;
|
|
|
|
uint8_t *bus_value = nullptr;
|
|
|
|
uint8_t throwaway = 0;
|
|
|
|
BusOperation bus_operation = BusOperation::None;
|
|
|
|
|
2020-10-04 23:12:04 +00:00
|
|
|
#define perform_bus(address, value, operation) \
|
|
|
|
bus_address = address; \
|
|
|
|
bus_value = value; \
|
|
|
|
bus_operation = operation
|
|
|
|
|
|
|
|
#define read(address, value) perform_bus(address, value, MOS6502Esque::Read)
|
|
|
|
#define write(address, value) perform_bus(address, value, MOS6502Esque::Write)
|
|
|
|
|
2020-09-29 22:42:07 +00:00
|
|
|
Cycles number_of_cycles = cycles + cycles_left_to_run_;
|
|
|
|
while(number_of_cycles > Cycles(0)) {
|
2020-09-28 02:20:58 +00:00
|
|
|
const MicroOp operation = *next_op_;
|
|
|
|
++next_op_;
|
|
|
|
|
|
|
|
switch(operation) {
|
2020-09-29 22:42:07 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Scheduling.
|
|
|
|
//
|
|
|
|
|
2020-10-02 21:08:30 +00:00
|
|
|
case OperationMoveToNextProgram: {
|
2020-09-28 02:20:58 +00:00
|
|
|
// The exception program will determine the appropriate way to respond
|
|
|
|
// based on the pending exception if one exists; otherwise just do a
|
|
|
|
// standard fetch-decode-execute.
|
2020-10-02 21:08:30 +00:00
|
|
|
const auto offset = instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offsets[0];
|
|
|
|
next_op_ = µ_ops_[offset];
|
2020-09-29 22:42:07 +00:00
|
|
|
instruction_buffer_.clear();
|
|
|
|
data_buffer_.clear();
|
|
|
|
last_operation_pc_ = pc_;
|
2020-10-02 21:08:30 +00:00
|
|
|
} continue;
|
2020-09-28 22:43:53 +00:00
|
|
|
|
2020-10-02 21:08:30 +00:00
|
|
|
case OperationDecode: {
|
2020-09-29 22:42:07 +00:00
|
|
|
// A VERY TEMPORARY piece of logging.
|
2020-10-04 22:52:46 +00:00
|
|
|
printf("[%04x] %02x\n", pc_ - 1, instruction_buffer_.value);
|
|
|
|
active_instruction_ = &instructions[instruction_buffer_.value];
|
2020-10-02 21:08:30 +00:00
|
|
|
|
|
|
|
const auto size_flag = mx_flags_[active_instruction_->size_field];
|
|
|
|
next_op_ = µ_ops_[active_instruction_->program_offsets[size_flag]];
|
2020-09-29 22:42:07 +00:00
|
|
|
instruction_buffer_.clear();
|
2020-10-02 21:08:30 +00:00
|
|
|
} continue;
|
2020-09-28 02:20:58 +00:00
|
|
|
|
2020-09-29 22:42:07 +00:00
|
|
|
//
|
|
|
|
// PC fetches.
|
|
|
|
//
|
|
|
|
|
|
|
|
case CycleFetchIncrementPC:
|
2020-10-04 23:12:04 +00:00
|
|
|
read(pc_ | program_bank_, instruction_buffer_.next_input());
|
|
|
|
++pc_;
|
|
|
|
break;
|
|
|
|
|
2020-10-02 21:08:30 +00:00
|
|
|
case CycleFetchOpcode:
|
2020-10-04 23:12:04 +00:00
|
|
|
perform_bus(pc_ | program_bank_, instruction_buffer_.next_input(), MOS6502Esque::ReadOpcode);
|
2020-09-29 22:42:07 +00:00
|
|
|
++pc_;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CycleFetchPC:
|
2020-10-04 23:12:04 +00:00
|
|
|
read(pc_ | program_bank_, &throwaway);
|
2020-09-29 22:42:07 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-04 01:30:24 +00:00
|
|
|
//
|
|
|
|
// Data fetches and stores.
|
|
|
|
//
|
|
|
|
|
2020-10-04 22:52:46 +00:00
|
|
|
#define increment_data_address() data_address_ = (data_address_ & 0xff0000) + ((data_address_ + 1) & 0xffff)
|
|
|
|
#define decrement_data_address() data_address_ = (data_address_ & 0xff0000) + ((data_address_ - 1) & 0xffff)
|
|
|
|
|
|
|
|
|
2020-10-04 01:30:24 +00:00
|
|
|
case CycleFetchData:
|
2020-10-04 23:12:04 +00:00
|
|
|
read(data_address_, data_buffer_.next_input());
|
2020-10-04 01:30:24 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-04 23:02:47 +00:00
|
|
|
case CycleFetchIncorrectDataAddress:
|
2020-10-04 23:12:04 +00:00
|
|
|
read(incorrect_data_address_, &throwaway);
|
2020-10-04 23:02:47 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-04 01:30:24 +00:00
|
|
|
case CycleFetchIncrementData:
|
2020-10-04 23:12:04 +00:00
|
|
|
read(data_address_, data_buffer_.next_input());
|
2020-10-04 22:52:46 +00:00
|
|
|
increment_data_address();
|
2020-10-04 01:30:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CycleStoreData:
|
2020-10-04 23:12:04 +00:00
|
|
|
write(data_address_, data_buffer_.next_output());
|
2020-10-04 01:30:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CycleStoreIncrementData:
|
2020-10-04 23:12:04 +00:00
|
|
|
write(data_address_, data_buffer_.next_output());
|
2020-10-04 22:52:46 +00:00
|
|
|
increment_data_address();
|
2020-10-04 01:30:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CycleStoreDecrementData:
|
2020-10-04 23:12:04 +00:00
|
|
|
write(data_address_, data_buffer_.next_output());
|
2020-10-04 22:52:46 +00:00
|
|
|
decrement_data_address();
|
|
|
|
break;
|
|
|
|
|
2020-10-04 23:21:04 +00:00
|
|
|
case CycleFetchBlockX:
|
|
|
|
read(((instruction_buffer_.value & 0xff00) << 8) | (x_.full & x_masks_[1]), data_buffer_.any_byte());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CycleFetchBlockY:
|
|
|
|
read(((instruction_buffer_.value & 0xff00) << 8) | (y_.full & x_masks_[1]), &throwaway);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CycleStoreBlockY:
|
|
|
|
write(((instruction_buffer_.value & 0xff00) << 8) | (y_.full & x_masks_[1]), data_buffer_.any_byte());
|
|
|
|
break;
|
|
|
|
|
2020-10-04 22:52:46 +00:00
|
|
|
#undef increment_data_address
|
|
|
|
#undef decrement_data_address
|
|
|
|
|
|
|
|
//
|
|
|
|
// Stack accesses.
|
|
|
|
//
|
|
|
|
|
|
|
|
#define stack_access(value, operation) \
|
|
|
|
if(emulation_flag_) { \
|
|
|
|
bus_address = s_.halves.low | 0x100; \
|
|
|
|
} else { \
|
|
|
|
bus_address = s_.full; \
|
|
|
|
} \
|
|
|
|
bus_value = value; \
|
|
|
|
bus_operation = operation;
|
|
|
|
|
|
|
|
case CyclePush:
|
|
|
|
stack_access(data_buffer_.next_stack(), MOS6502Esque::Write);
|
|
|
|
--s_.full;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CyclePull:
|
|
|
|
++s_.full;
|
|
|
|
stack_access(data_buffer_.next_input(), MOS6502Esque::Read);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CycleAccessStack:
|
|
|
|
stack_access(&throwaway, MOS6502Esque::Read);
|
2020-10-04 01:30:24 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-04 22:52:46 +00:00
|
|
|
#undef stack_access
|
|
|
|
|
2020-09-29 22:42:07 +00:00
|
|
|
//
|
|
|
|
// Data movement.
|
|
|
|
//
|
|
|
|
|
|
|
|
case OperationCopyPCToData:
|
|
|
|
data_buffer_.size = 2;
|
|
|
|
data_buffer_.value = pc_;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OperationCopyInstructionToData:
|
|
|
|
data_buffer_ = instruction_buffer_;
|
|
|
|
break;
|
|
|
|
|
2020-10-04 01:30:24 +00:00
|
|
|
//
|
|
|
|
// Address construction.
|
|
|
|
//
|
|
|
|
|
|
|
|
case OperationConstructAbsolute:
|
|
|
|
data_address_ = instruction_buffer_.value | data_bank_;
|
|
|
|
break;
|
|
|
|
|
2020-10-04 22:52:46 +00:00
|
|
|
case OperationConstructAbsoluteIndexedIndirect:
|
|
|
|
data_address_ = (instruction_buffer_.value + (x_.full & x_masks_[1])) & 0xffff;
|
|
|
|
break;
|
|
|
|
|
2020-10-04 23:02:47 +00:00
|
|
|
case OperationConstructAbsoluteLongX:
|
|
|
|
data_address_ = instruction_buffer_.value + (x_.full & x_masks_[1]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OperationConstructAbsoluteXRead:
|
|
|
|
case OperationConstructAbsoluteX:
|
|
|
|
data_address_ = ((instruction_buffer_.value + (x_.full & x_masks_[1])) & 0xffff) | data_bank_;
|
|
|
|
incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) | data_bank_;
|
|
|
|
|
|
|
|
// If the incorrect address isn't actually incorrect, skip its usage.
|
|
|
|
if(operation == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) {
|
|
|
|
++next_op_;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2020-10-04 23:12:04 +00:00
|
|
|
case OperationConstructAbsoluteYRead:
|
|
|
|
case OperationConstructAbsoluteY:
|
|
|
|
data_address_ = ((instruction_buffer_.value + (y_.full & x_masks_[1])) & 0xffff) | data_bank_;
|
|
|
|
incorrect_data_address_ = (data_address_ & 0xff) | (instruction_buffer_.value & 0xff00) | data_bank_;
|
|
|
|
|
|
|
|
// If the incorrect address isn't actually incorrect, skip its usage.
|
|
|
|
if(operation == OperationConstructAbsoluteYRead && data_address_ == incorrect_data_address_) {
|
|
|
|
++next_op_;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2020-09-29 22:42:07 +00:00
|
|
|
//
|
|
|
|
// Performance.
|
|
|
|
//
|
|
|
|
|
|
|
|
case OperationPerform:
|
|
|
|
switch(active_instruction_->operation) {
|
2020-10-02 21:08:30 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Flag manipulation.
|
|
|
|
//
|
|
|
|
|
2020-09-29 22:42:07 +00:00
|
|
|
case CLD:
|
2020-10-02 21:08:30 +00:00
|
|
|
decimal_flag_ = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Loads, stores and transfers
|
|
|
|
//
|
|
|
|
|
|
|
|
#define LD(dest, src, masks) dest.full = (dest.full & masks[0]) | (src & masks[1])
|
|
|
|
|
|
|
|
case LDA:
|
|
|
|
LD(a_, data_buffer_.value, m_masks_);
|
2020-09-29 22:42:07 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LDX:
|
2020-10-02 21:08:30 +00:00
|
|
|
LD(x_, data_buffer_.value, x_masks_);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LDY:
|
|
|
|
LD(y_, data_buffer_.value, x_masks_);
|
2020-09-29 22:42:07 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-02 21:08:30 +00:00
|
|
|
case TXS:
|
|
|
|
// TODO: does this transfer in full when in 8-bit index mode?
|
|
|
|
LD(s_, x_.full, x_masks_);
|
|
|
|
break;
|
|
|
|
|
|
|
|
#undef LD
|
|
|
|
|
2020-10-04 01:30:24 +00:00
|
|
|
case STA:
|
|
|
|
data_buffer_.value = a_.full & m_masks_[1];
|
|
|
|
data_buffer_.size = 2 - mx_flags_[0];
|
|
|
|
break;
|
|
|
|
|
2020-10-04 22:52:46 +00:00
|
|
|
//
|
|
|
|
// Jumps.
|
|
|
|
//
|
|
|
|
|
|
|
|
case JML:
|
|
|
|
program_bank_ = instruction_buffer_.value & 0xff0000;
|
|
|
|
pc_ = instruction_buffer_.value & 0xffff;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSL:
|
|
|
|
program_bank_ = instruction_buffer_.value & 0xff0000;
|
|
|
|
instruction_buffer_.size = 2;
|
|
|
|
[[fallthrough]];
|
|
|
|
|
|
|
|
case JSR: {
|
|
|
|
const uint16_t old_pc = pc_;
|
|
|
|
pc_ = instruction_buffer_.value;
|
|
|
|
instruction_buffer_.value = old_pc;
|
|
|
|
} break;
|
|
|
|
|
2020-10-04 23:21:04 +00:00
|
|
|
//
|
|
|
|
// Block moves.
|
|
|
|
//
|
2020-10-04 22:52:46 +00:00
|
|
|
|
2020-10-04 23:21:04 +00:00
|
|
|
case MVP:
|
|
|
|
data_bank_ = (instruction_buffer_.value & 0xff) << 16;
|
|
|
|
--x_.full;
|
|
|
|
--y_.full;
|
|
|
|
--a_.full;
|
|
|
|
if(a_.full) pc_ -= 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MVN:
|
|
|
|
data_bank_ = (instruction_buffer_.value & 0xff) << 16;
|
|
|
|
++x_.full;
|
|
|
|
++y_.full;
|
|
|
|
--a_.full;
|
|
|
|
if(a_.full) pc_ -= 3;
|
|
|
|
break;
|
2020-10-04 22:52:46 +00:00
|
|
|
|
2020-09-29 22:42:07 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2020-09-28 02:20:58 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
2020-09-29 22:42:07 +00:00
|
|
|
|
|
|
|
number_of_cycles -= bus_handler_.perform_bus_operation(bus_operation, bus_address, bus_value);
|
|
|
|
}
|
|
|
|
|
2020-10-04 23:12:04 +00:00
|
|
|
#undef read
|
|
|
|
#undef write
|
|
|
|
#undef bus_operation
|
|
|
|
|
2020-09-29 22:42:07 +00:00
|
|
|
cycles_left_to_run_ = number_of_cycles;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessorBase::set_power_on(bool active) {
|
|
|
|
if(active) {
|
|
|
|
pending_exceptions_ |= PowerOn;
|
|
|
|
} else {
|
|
|
|
pending_exceptions_ &= ~PowerOn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessorBase::set_irq_line(bool active) {
|
|
|
|
if(active) {
|
|
|
|
pending_exceptions_ |= IRQ;
|
|
|
|
} else {
|
|
|
|
pending_exceptions_ &= ~IRQ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessorBase::set_reset_line(bool active) {
|
|
|
|
if(active) {
|
|
|
|
pending_exceptions_ |= Reset;
|
|
|
|
} else {
|
|
|
|
pending_exceptions_ &= ~Reset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessorBase::set_nmi_line(bool active) {
|
|
|
|
// This is edge triggered.
|
|
|
|
if(active) {
|
|
|
|
pending_exceptions_ |= NMI;
|
2020-09-28 02:20:58 +00:00
|
|
|
}
|
|
|
|
}
|
2020-09-29 01:35:46 +00:00
|
|
|
|
2020-09-29 22:42:07 +00:00
|
|
|
// The 65816 can't jam.
|
2020-09-29 01:35:46 +00:00
|
|
|
bool ProcessorBase::is_jammed() const { return false; }
|