1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +00:00

This is where my thinking now resides. Two levels of indirection, and consolidated collections.

This commit is contained in:
Thomas Harte 2019-03-12 22:46:31 -04:00
parent 33b53e7605
commit 57898ed6dd
3 changed files with 183 additions and 111 deletions

View File

@ -9,17 +9,34 @@
template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>::run_for(HalfCycles duration) { template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>::run_for(HalfCycles duration) {
// TODO: obey the 'cycles' count. // TODO: obey the 'cycles' count.
while(true) { while(true) {
// Check whether the program is exhausted. // Check whether the current list of bus steps is exhausted; if so then
if(active_program_->action == Step::Action::ScheduleNextProgram) { // seek out another one from the current program (if any), and if there
std::cerr << "68000 Abilities exhausted" << std::endl; // are no more to do, revert to scheduling something else (after checking
return; // for interrupts).
if(active_step_->action == BusStep::Action::ScheduleNextProgram) {
if(active_micro_op_) {
++active_micro_op_;
switch(active_micro_op_->action) {
case MicroOp::Action::None: break;
case MicroOp::Action::PerformOperation:
std::cerr << "Should do something with program operation " << int(active_program_->operation) << std::endl;
break;
}
active_step_ = active_micro_op_->bus_program;
}
if(!active_step_) {
std::cerr << "68000 Abilities exhausted; should schedule an instruction or something?" << std::endl;
return;
}
} }
// The program is not exhausted, so perform the microcycle. // The bus step list is not exhausted, so perform the microcycle.
// Check for DTack if this isn't being treated implicitly. // Check for DTack if this isn't being treated implicitly.
if(!dtack_is_implicit) { if(!dtack_is_implicit) {
if(active_program_->microcycle.operation & (Microcycle::UpperData | Microcycle::LowerData) && !dtack_) { if(active_step_->microcycle.operation & (Microcycle::UpperData | Microcycle::LowerData) && !dtack_) {
// TODO: perform wait state. // TODO: perform wait state.
continue; continue;
} }
@ -28,26 +45,26 @@ template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
// TODO: synchronous bus. // TODO: synchronous bus.
// Perform the microcycle. // Perform the microcycle.
bus_handler_.perform_bus_operation(active_program_->microcycle, is_supervisor_); bus_handler_.perform_bus_operation(active_step_->microcycle, is_supervisor_);
// Perform the post-hoc action. // Perform the post-hoc action.
switch(active_program_->action) { switch(active_step_->action) {
default: default:
std::cerr << "Unimplemented 68000 action: " << int(active_program_->action) << std::endl; std::cerr << "Unimplemented 68000 bus step action: " << int(active_step_->action) << std::endl;
return; return;
break; break;
case Step::Action::None: break; case BusStep::Action::None: break;
case Step::Action::IncrementEffectiveAddress: effective_address_ += 2; break; case BusStep::Action::IncrementEffectiveAddress: effective_address_ += 2; break;
case Step::Action::IncrementProgramCounter: program_counter_.full += 2; break; case BusStep::Action::IncrementProgramCounter: program_counter_.full += 2; break;
case Step::Action::AdvancePrefetch: case BusStep::Action::AdvancePrefetch:
prefetch_queue_[0] = prefetch_queue_[1]; prefetch_queue_[0] = prefetch_queue_[1];
break; break;
} }
// Move to the next program step. // Move to the next program step.
++active_program_; ++active_step_;
} }
} }

View File

@ -8,32 +8,35 @@
#include "../68000.hpp" #include "../68000.hpp"
#include <array>
using namespace CPU::MC68000; using namespace CPU::MC68000;
ProcessorStorage::ProcessorStorage() { ProcessorStorage::ProcessorStorage() {
// Create the reset program. // Create the exception programs.
reset_program_ = assemble_program("n- n- n- n- n- nn nF nf nV nv np np"); const size_t reset_offset = assemble_program("n- n- n- n- n- nn nF nf nV nv np np");
// TODO: install access patterns. // Install all necessary access patterns.
const BusStepCollection bus_steps = assemble_standard_bus_steps();
// Install operations. // Install operations.
for(int c = 0; c < 65536; ++c) { install_instructions(bus_steps);
install_instruction(c);
} // Realise the exception programs as direct pointers.
reset_program_ = &all_bus_steps_[reset_offset];
// Set initial state. Largely TODO. // Set initial state. Largely TODO.
active_program_ = reset_program_.data(); active_step_ = reset_program_;
effective_address_ = 0; effective_address_ = 0;
is_supervisor_ = 1; is_supervisor_ = 1;
} }
// TODO: allow actions to be specified, of course. size_t ProcessorStorage::assemble_program(const char *access_pattern) {
std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const char *access_pattern) { const size_t start = all_bus_steps_.size();
std::vector<Step> steps;
// Parse the access pattern to build microcycles. // Parse the access pattern to build microcycles.
while(*access_pattern) { while(*access_pattern) {
Step step; BusStep step;
switch(*access_pattern) { switch(*access_pattern) {
case '\t': case ' ': // White space acts as a no-op; it's for clarity only. case '\t': case ' ': // White space acts as a no-op; it's for clarity only.
@ -45,13 +48,13 @@ std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const cha
switch(access_pattern[1]) { switch(access_pattern[1]) {
default: // This is probably a pure NOP; if what comes after this 'n' isn't actually default: // This is probably a pure NOP; if what comes after this 'n' isn't actually
// valid, it should be caught in the outer switch the next time around the loop. // valid, it should be caught in the outer switch the next time around the loop.
steps.push_back(step); all_bus_steps_.push_back(step);
++access_pattern; ++access_pattern;
break; break;
case '-': // This is two NOPs in a row. case '-': // This is two NOPs in a row.
steps.push_back(step); all_bus_steps_.push_back(step);
steps.push_back(step); all_bus_steps_.push_back(step);
access_pattern += 2; access_pattern += 2;
break; break;
@ -61,12 +64,12 @@ std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const cha
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess. step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess.
step.microcycle.address = &effective_address_; step.microcycle.address = &effective_address_;
step.microcycle.value = isupper(access_pattern[1]) ? &stack_pointers_[1].halves.high : &stack_pointers_[1].halves.low; step.microcycle.value = isupper(access_pattern[1]) ? &stack_pointers_[1].halves.high : &stack_pointers_[1].halves.low;
steps.push_back(step); all_bus_steps_.push_back(step);
step.microcycle.length = HalfCycles(3); step.microcycle.length = HalfCycles(3);
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData; step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
step.action = Step::Action::IncrementEffectiveAddress; step.action = BusStep::Action::IncrementEffectiveAddress;
steps.push_back(step); all_bus_steps_.push_back(step);
access_pattern += 2; access_pattern += 2;
break; break;
@ -77,12 +80,12 @@ std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const cha
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess. step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess.
step.microcycle.address = &effective_address_; step.microcycle.address = &effective_address_;
step.microcycle.value = isupper(access_pattern[1]) ? &program_counter_.halves.high : &program_counter_.halves.low; step.microcycle.value = isupper(access_pattern[1]) ? &program_counter_.halves.high : &program_counter_.halves.low;
steps.push_back(step); all_bus_steps_.push_back(step);
step.microcycle.length = HalfCycles(3); step.microcycle.length = HalfCycles(3);
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData; step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
step.action = Step::Action::IncrementEffectiveAddress; step.action = BusStep::Action::IncrementEffectiveAddress;
steps.push_back(step); all_bus_steps_.push_back(step);
access_pattern += 2; access_pattern += 2;
break; break;
@ -92,13 +95,13 @@ std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const cha
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram;
step.microcycle.address = &program_counter_.full; step.microcycle.address = &program_counter_.full;
step.microcycle.value = &prefetch_queue_[1]; step.microcycle.value = &prefetch_queue_[1];
step.action = Step::Action::AdvancePrefetch; step.action = BusStep::Action::AdvancePrefetch;
steps.push_back(step); all_bus_steps_.push_back(step);
step.microcycle.length = HalfCycles(3); step.microcycle.length = HalfCycles(3);
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData; step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
step.action = Step::Action::IncrementProgramCounter; step.action = BusStep::Action::IncrementProgramCounter;
steps.push_back(step); all_bus_steps_.push_back(step);
access_pattern += 2; access_pattern += 2;
break; break;
@ -112,11 +115,20 @@ std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const cha
} }
// Add a final 'ScheduleNextProgram' sentinel. // Add a final 'ScheduleNextProgram' sentinel.
Step end_program; BusStep end_program;
end_program.action = Step::Action::ScheduleNextProgram; end_program.action = BusStep::Action::ScheduleNextProgram;
steps.push_back(end_program); all_bus_steps_.push_back(end_program);
return steps; return start;
}
ProcessorStorage::BusStepCollection ProcessorStorage::assemble_standard_bus_steps() {
ProcessorStorage::BusStepCollection collection;
collection.four_step_Dn = assemble_program("np");
collection.six_step_Dn = assemble_program("np n");
return collection;
} }
/* /*
@ -131,81 +143,75 @@ std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const cha
from known instructions to their disassembly rather than vice versa; especially from known instructions to their disassembly rather than vice versa; especially
(iii) given that there are plentiful disassemblers against which to test work in progress. (iii) given that there are plentiful disassemblers against which to test work in progress.
*/ */
void ProcessorStorage::install_instruction(int instruction) { void ProcessorStorage::install_instructions(const BusStepCollection &bus_step_collection) {
enum class Operation { enum class Decoder {
ABCD, ADD, ADDA, ADDI, ADDQ, ADDX, AND, ANDI, Decimal,
ASL, ASLrmw, ASR, ASRrmw, Bcc, RegOpModeReg,
SizeModeRegisterImmediate,
TODO DataSizeModeQuick
}; };
struct PatternMapping { struct PatternMapping {
uint16_t mask, value; uint16_t mask, value;
Operation operation; Operation operation;
Decoder decoder;
}; };
/* /*
Credit here is due to 'wrm' (https://github.com/wrm-za I assume) for his public Inspired partly by 'wrm' (https://github.com/wrm-za I assume); the following
domain 68000 disassembler, from which the table below was largely sourced. table draws from the M68000 Programmer's Reference Manual, currently available at
Manual legwork has been extended to check this table against the M68000
Programmer's Reference Manual, currently available at
https://www.nxp.com/files-static/archives/doc/ref_manual/M68000PRM.pdf https://www.nxp.com/files-static/archives/doc/ref_manual/M68000PRM.pdf
After each line is the internal page number on which documentation of that
instruction mapping can be found, followed by the page number within the PDF
linked above.
NB: a vector is used to allow easy iteration.
*/ */
const std::vector<PatternMapping> mappings = { const std::vector<PatternMapping> mappings = {
{0xf1f0, 0xc100, Operation::ABCD}, {0xf000, 0xd000, Operation::ADD}, {0xf1f0, 0x8100, Operation::SBCD, Decoder::Decimal}, // 4-171 (p275)
{0xf0c0, 0xd0c0, Operation::ADDA}, {0xff00, 0x0600, Operation::ADDI}, {0xf1f0, 0xc100, Operation::ABCD, Decoder::Decimal}, // 4-3 (p107)
{0xf100, 0x5000, Operation::ADDQ}, {0xf130, 0xd100, Operation::ADDX},
{0xf000, 0xc000, Operation::AND}, {0xff00, 0x0200, Operation::ANDI}, {0xf000, 0x8000, Operation::OR, Decoder::RegOpModeReg}, // 4-150 (p226)
{0xf118, 0xe100, Operation::ASL}, {0xffc0, 0xe1c0, Operation::ASLrmw}, {0xf000, 0x9000, Operation::SUB, Decoder::RegOpModeReg}, // 4-174 (p278)
{0xf118, 0xe000, Operation::ASR}, {0xffc0, 0xe0c0, Operation::ASRrmw}, {0xf000, 0xb000, Operation::EOR, Decoder::RegOpModeReg}, // 4-100 (p204)
{0xf000, 0x6000, Operation::Bcc}, {0xf1c0, 0x0140, Operation::TODO}, {0xf000, 0xc000, Operation::AND, Decoder::RegOpModeReg}, // 4-15 (p119)
{0xffc0, 0x0840, Operation::TODO}, {0xf1c0, 0x0180, Operation::TODO}, {0xf000, 0xd000, Operation::ADD, Decoder::RegOpModeReg}, // 4-4 (p108)
{0xffc0, 0x0880, Operation::TODO}, {0xf1c0, 0x01c0, Operation::TODO},
{0xffc0, 0x08c0, Operation::TODO}, {0xf1c0, 0x0100, Operation::TODO}, {0xff00, 0x0600, Operation::ADD, Decoder::SizeModeRegisterImmediate}, // 4-9 (p113)
{0xffc0, 0x0800, Operation::TODO}, {0xf1c0, 0x4180, Operation::TODO},
{0xff00, 0x4200, Operation::TODO}, {0xf100, 0xb000, Operation::TODO}, {0xff00, 0x0600, Operation::ADD, Decoder::DataSizeModeQuick}, // 4-11 (p115)
{0xf0c0, 0xb0c0, Operation::TODO}, {0xff00, 0x0c00, Operation::TODO},
{0xf138, 0xb108, Operation::TODO}, {0xf0f8, 0x50c8, Operation::TODO},
{0xf1c0, 0x81c0, Operation::TODO}, {0xf1c0, 0x80c0, Operation::TODO},
{0xf100, 0xb100, Operation::TODO}, {0xff00, 0x0a00, Operation::TODO},
{0xf100, 0xc100, Operation::TODO}, {0xffb8, 0x4880, Operation::TODO},
{0xffc0, 0x4ec0, Operation::TODO}, {0xffc0, 0x4e80, Operation::TODO},
{0xf1c0, 0x41c0, Operation::TODO}, {0xfff8, 0x4e50, Operation::TODO},
{0xf118, 0xe108, Operation::TODO}, {0xffc0, 0xe3c0, Operation::TODO},
{0xf118, 0xe008, Operation::TODO}, {0xffc0, 0xe2c0, Operation::TODO},
{0xc000, 0x0000, Operation::TODO}, {0xffc0, 0x44c0, Operation::TODO},
{0xffc0, 0x46c0, Operation::TODO}, {0xffc0, 0x40c0, Operation::TODO},
{0xfff0, 0x4e60, Operation::TODO}, {0xc1c0, 0x0040, Operation::TODO},
{0xfb80, 0x4880, Operation::TODO}, {0xf138, 0x0108, Operation::TODO},
{0xf100, 0x7000, Operation::TODO}, {0xf1c0, 0xc1c0, Operation::TODO},
{0xf1c0, 0xc0c0, Operation::TODO}, {0xffc0, 0x4800, Operation::TODO},
{0xff00, 0x4400, Operation::TODO}, {0xff00, 0x4000, Operation::TODO},
{0xffff, 0x4e71, Operation::TODO}, {0xff00, 0x4600, Operation::TODO},
{0xf000, 0x8000, Operation::TODO}, {0xff00, 0x0000, Operation::TODO},
{0xffc0, 0x4840, Operation::TODO}, {0xffff, 0x4e70, Operation::TODO},
{0xf118, 0xe118, Operation::TODO}, {0xffc0, 0xe7c0, Operation::TODO},
{0xf118, 0xe018, Operation::TODO}, {0xffc0, 0xe6c0, Operation::TODO},
{0xf118, 0xe110, Operation::TODO}, {0xffc0, 0xe5c0, Operation::TODO},
{0xf118, 0xe010, Operation::TODO}, {0xffc0, 0xe4c0, Operation::TODO},
{0xffff, 0x4e73, Operation::TODO}, {0xffff, 0x4e77, Operation::TODO},
{0xffff, 0x4e75, Operation::TODO}, {0xf1f0, 0x8100, Operation::TODO},
{0xf0c0, 0x50c0, Operation::TODO}, {0xffff, 0x4e72, Operation::TODO},
{0xf000, 0x9000, Operation::TODO}, {0xf0c0, 0x90c0, Operation::TODO},
{0xff00, 0x0400, Operation::TODO}, {0xf100, 0x5100, Operation::TODO},
{0xf130, 0x9100, Operation::TODO}, {0xfff8, 0x4840, Operation::TODO},
{0xffc0, 0x4ac0, Operation::TODO}, {0xfff0, 0x4e40, Operation::TODO},
{0xffff, 0x4e76, Operation::TODO}, {0xff00, 0x4a00, Operation::TODO},
{0xfff8, 0x4e58, Operation::TODO}
}; };
// Perform a linear search of the mappings above for this instruction. // Perform a linear search of the mappings above for this instruction.
for(const auto &mapping: mappings) { for(int instruction = 0; instruction < 65536; ++instruction) {
if((instruction & mapping.mask) == mapping.value) { for(const auto &mapping: mappings) {
if(mapping.operation == Operation::TODO) { if((instruction & mapping.mask) == mapping.value) {
std::cout << std::hex << std::setw(4) << std::setfill('0'); switch(mapping.decoder) {
std::cout << "Yet to implement, instruction matching: x & " << mapping.mask << " == " << mapping.value << std::endl; case Decoder::Decimal: {
const int destination = (instruction >> 8) & 7;
const int source = instruction & 7;
if(instruction & 8) {
std::cout << "Address to address (both predecrement) from " << source << " to " << destination << std::endl;
} else {
instructions[instruction].operation = mapping.operation;
instructions[instruction].source = &data_[source];
instructions[instruction].destination = &data_[destination];
// instructions[instruction].destination.micro_operations =
std::cout << "Data register to data register from " << source << " to " << destination << std::endl;
}
} break;
case Decoder::RegOpModeReg: {
} break;
default:
std::cerr << "Unhandled decoder " << int(mapping.decoder) << std::endl;
break;
}
break;
} }
break;
} }
} }
} }

View File

@ -29,10 +29,15 @@ class ProcessorStorage {
uint32_t effective_address_; uint32_t effective_address_;
RegisterPair32 bus_data_; RegisterPair32 bus_data_;
enum class Operation {
ABCD, SBCD,
ADD, AND, EOR, OR, SUB,
};
/*! /*!
A step is a microcycle to perform plus an action to occur afterwards, if any. Bus steps are sequences of things to communicate to the bus.
*/ */
struct Step { struct BusStep {
Microcycle microcycle; Microcycle microcycle;
enum class Action { enum class Action {
None, None,
@ -56,15 +61,53 @@ class ProcessorStorage {
} action = Action::None; } action = Action::None;
}; };
// Special programs. /*!
std::vector<Step> reset_program_; A micro-op is: (i) an action to take; and (ii) a sequence of bus operations
to perform after taking the action.
// Current program pointer. A nullptr bus_program terminates a sequence of micro operations.
Step *active_program_ = nullptr; */
struct MicroOp {
enum class Action {
None,
PerformOperation
} action = Action::None;
BusStep *bus_program = nullptr;
};
/*!
A program represents the implementation of a particular opcode, as a sequence
of micro-ops and, separately, the operation to perform plus whatever other
fields the operation requires.
*/
struct Program {
MicroOp *micro_operations = nullptr;
Operation operation;
RegisterPair32 *source;
RegisterPair32 *destination;
};
// Storage for all the sequences of bus steps and micro-ops used throughout
// the 68000.
std::vector<BusStep> all_bus_steps_;
std::vector<MicroOp> all_micro_ops_;
// A lookup table from instructions to implementations.
Program instructions[65536];
// Special programs, for exception handlers.
BusStep *reset_program_;
// Current bus step pointer, and outer program pointer.
Program *active_program_ = nullptr;
MicroOp *active_micro_op_ = nullptr;
BusStep *active_step_ = nullptr;
private: private:
/*! /*!
Produces a vector of Steps that implement the described program. Installs BusSteps that implement the described program into the relevant
instance storage, returning the offset within @c all_bus_steps_ at which
the generated steps begin.
@param access_pattern A string describing the bus activity that occurs @param access_pattern A string describing the bus activity that occurs
during this program. This should follow the same general pattern as during this program. This should follow the same general pattern as
@ -103,13 +146,19 @@ class ProcessorStorage {
The user should fill in the steps necessary to get data into or extract The user should fill in the steps necessary to get data into or extract
data from those. data from those.
*/ */
std::vector<Step> assemble_program(const char *access_pattern); size_t assemble_program(const char *access_pattern);
struct BusStepCollection {
size_t six_step_Dn;
size_t four_step_Dn;
};
BusStepCollection assemble_standard_bus_steps();
/*! /*!
Disassembles the instruction @c instruction and inserts it into the Disassembles the instruction @c instruction and inserts it into the
appropriate lookup tables. appropriate lookup tables.
*/ */
void install_instruction(int instruction); void install_instructions(const BusStepCollection &);
}; };
#endif /* MC68000Storage_h */ #endif /* MC68000Storage_h */