1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-10-01 13:58:20 +00:00

Accepts that whether instructions do 8- or 16-bit bus accesses depends on either M or X depending on the operation.

This commit is contained in:
Thomas Harte 2020-10-02 17:08:30 -04:00
parent 36f843bc6e
commit b83d93abc2
4 changed files with 78 additions and 31 deletions

View File

@ -14,7 +14,7 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const {
switch (r) {
case Register::ProgramCounter: return pc_;
case Register::LastOperationAddress: return last_operation_pc_;
case Register::StackPointer: return s_;
case Register::StackPointer: return s_.full;
// case Register::Flags: return get_flags();
case Register::A: return a_.full;
case Register::X: return x_.full;
@ -26,11 +26,11 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const {
void ProcessorBase::set_value_of_register(Register r, uint16_t value) {
switch (r) {
case Register::ProgramCounter: pc_ = value; break;
case Register::StackPointer: s_ = value; break;
case Register::StackPointer: s_.full = value; break;
// case Register::Flags: set_flags(uint8_t(value)); break;
case Register::A: a_ = value; break;
case Register::X: x_ = value; break;
case Register::Y: y_ = value; break;
case Register::A: a_.full = value; break;
case Register::X: x_.full = value; break;
case Register::Y: y_.full = value; break;
default: break;
}
}

View File

@ -24,32 +24,38 @@ template <typename BusHandler> void Processor<BusHandler>::run_for(const Cycles
// Scheduling.
//
case OperationMoveToNextProgram:
case OperationMoveToNextProgram: {
// 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.
next_op_ = &micro_ops_[instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offset];
const auto offset = instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offsets[0];
next_op_ = &micro_ops_[offset];
instruction_buffer_.clear();
data_buffer_.clear();
last_operation_pc_ = pc_;
continue;
} continue;
case OperationDecode:
case OperationDecode: {
// A VERY TEMPORARY piece of logging.
printf("%02x\n", instruction_buffer_.value);
active_instruction_ = &instructions[instruction_buffer_.value];
next_op_ = &micro_ops_[active_instruction_->program_offset];
active_instruction_ = &instructions[instruction_offset_ + instruction_buffer_.value];
const auto size_flag = mx_flags_[active_instruction_->size_field];
next_op_ = &micro_ops_[active_instruction_->program_offsets[size_flag]];
instruction_buffer_.clear();
continue;
} continue;
//
// PC fetches.
//
case CycleFetchIncrementPC:
case CycleFetchOpcode:
bus_address = pc_ | program_bank_;
bus_value = instruction_buffer_.next();
bus_operation = MOS6502Esque::Read; // TODO: indicate ReadOpcode when appropriate.
bus_operation = (operation == CycleFetchOpcode) ? MOS6502Esque::ReadOpcode : MOS6502Esque::Read;
// TODO: split this action when I'm happy that my route to bus accesses is settled, to avoid repeating the conditional
// embedded into the `switch`.
++pc_;
break;
@ -78,14 +84,40 @@ template <typename BusHandler> void Processor<BusHandler>::run_for(const Cycles
case OperationPerform:
switch(active_instruction_->operation) {
//
// Flag manipulation.
//
case CLD:
// TODO.
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_);
break;
case LDX:
// TODO.
LD(x_, data_buffer_.value, x_masks_);
break;
case LDY:
LD(y_, data_buffer_.value, x_masks_);
break;
case TXS:
// TODO: does this transfer in full when in 8-bit index mode?
LD(s_, x_.full, x_masks_);
break;
#undef LD
default:
assert(false);
}

View File

@ -97,11 +97,11 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
}
// Fill in the proper table entries and increment the opcode pointer.
storage_.instructions[opcode].program_offset = uint16_t(micro_op_location_8);
storage_.instructions[opcode].program_offsets[0] = uint16_t(micro_op_location_16);
storage_.instructions[opcode].program_offsets[1] = uint16_t(micro_op_location_8);
storage_.instructions[opcode].operation = operation;
storage_.instructions[opcode + 256].program_offset = uint16_t(micro_op_location_16);
storage_.instructions[opcode + 256].operation = operation;
// TODO: fill in size_field.
++opcode;
}
@ -109,14 +109,15 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
void set_exception_generator(Generator generator) {
const auto key = std::make_pair(AccessType::Read, generator);
const auto map_entry = installed_patterns.find(key);
storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offset = uint16_t(map_entry->second.first);
storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offsets[0] =
storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].program_offsets[1] = uint16_t(map_entry->second.first);
storage_.instructions[size_t(ProcessorStorage::OperationSlot::Exception)].operation = BRK;
}
void install_fetch_decode_execute() {
storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].program_offset = uint16_t(storage_.micro_ops_.size());
storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].operation = NOP;
storage_.micro_ops_.push_back(CycleFetchIncrementPC);
storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].program_offsets[0] =
storage_.instructions[size_t(ProcessorStorage::OperationSlot::FetchDecodeExecute)].program_offsets[1] = uint16_t(storage_.micro_ops_.size());
storage_.micro_ops_.push_back(CycleFetchOpcode);
storage_.micro_ops_.push_back(OperationDecode);
}

View File

@ -11,6 +11,8 @@ enum MicroOp: uint8_t {
CycleFetchIncrementPC,
/// Fetches a byte from the program counter without incrementing it, and throws it away.
CycleFetchPC,
/// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address.
CycleFetchOpcode,
/// Fetches a byte from the data address to the data buffer.
CycleFetchData,
@ -171,30 +173,42 @@ struct ProcessorStorage {
// Frustratingly, there is not quite enough space in 16 bits to store both
// the program offset and the operation as currently defined.
struct Instruction {
uint16_t program_offset;
Operation operation;
/// Pointers into micro_ops_ for: [0] = 16-bit operation; [1] = 8-bit operation.
uint16_t program_offsets[2] = {0xffff, 0xffff};
/// The operation to perform upon an OperationPerform.
Operation operation = NOP;
/// An index into the mx field indicating which of M or X affects whether this is an 8-bit or 16-bit field.
/// So the program to perform is that at @c program_offsets[mx_flags[size_field]]
uint8_t size_field = 0;
};
Instruction instructions[514]; // Arranged as:
// 256 entries: emulation-mode instructions;
// 256 entries: 16-bit instructions;
// the entry for 'exceptions' (i.e. reset, irq, nmi); and
// the entry for fetch-decode-execute.
Instruction instructions[256 + 2]; // Arranged as:
// 256 entries: instructions;
// the entry for 'exceptions' (i.e. reset, irq, nmi); and
// the entry for fetch-decode-execute.
enum class OperationSlot {
Exception = 512,
Exception = 256,
FetchDecodeExecute
};
// Registers.
RegisterPair16 a_;
RegisterPair16 x_, y_;
uint16_t pc_, s_;
RegisterPair16 s_;
uint16_t pc_;
// A helper for testing.
uint16_t last_operation_pc_;
Instruction *active_instruction_;
Cycles cycles_left_to_run_;
// Flags aplenty.
uint8_t carry_flag_, negative_result_, zero_result_, decimal_flag_, overflow_flag_, inverse_interrupt_flag_ = 0;
uint8_t mx_flags_[2] = {1, 1}; // [0] = m; [1] = x. In both cases either `0` or `1`.
uint16_t m_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask.
uint16_t x_masks_[2] = {0xff00, 0x00ff}; // [0] = src mask; [1] = dst mask.
int instruction_offset_ = 0;
// I.e. the offset for direct addressing (outside of emulation mode).
uint16_t direct_ = 0;