mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-19 08:31:11 +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:
parent
36f843bc6e
commit
b83d93abc2
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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_ = µ_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_ = µ_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_ = µ_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_ = µ_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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user