1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-26 09:29:45 +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) {
// TODO: obey the 'cycles' count.
while(true) {
// Check whether the program is exhausted.
if(active_program_->action == Step::Action::ScheduleNextProgram) {
std::cerr << "68000 Abilities exhausted" << std::endl;
return;
// Check whether the current list of bus steps is exhausted; if so then
// seek out another one from the current program (if any), and if there
// are no more to do, revert to scheduling something else (after checking
// 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.
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.
continue;
}
@ -28,26 +45,26 @@ template <class T, bool dtack_is_implicit> void Processor<T, dtack_is_implicit>:
// TODO: synchronous bus.
// 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.
switch(active_program_->action) {
switch(active_step_->action) {
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;
break;
case Step::Action::None: break;
case BusStep::Action::None: break;
case Step::Action::IncrementEffectiveAddress: effective_address_ += 2; break;
case Step::Action::IncrementProgramCounter: program_counter_.full += 2; break;
case BusStep::Action::IncrementEffectiveAddress: effective_address_ += 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];
break;
}
// Move to the next program step.
++active_program_;
++active_step_;
}
}

View File

@ -8,32 +8,35 @@
#include "../68000.hpp"
#include <array>
using namespace CPU::MC68000;
ProcessorStorage::ProcessorStorage() {
// Create the reset program.
reset_program_ = assemble_program("n- n- n- n- n- nn nF nf nV nv np np");
// Create the exception programs.
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.
for(int c = 0; c < 65536; ++c) {
install_instruction(c);
}
install_instructions(bus_steps);
// Realise the exception programs as direct pointers.
reset_program_ = &all_bus_steps_[reset_offset];
// Set initial state. Largely TODO.
active_program_ = reset_program_.data();
active_step_ = reset_program_;
effective_address_ = 0;
is_supervisor_ = 1;
}
// TODO: allow actions to be specified, of course.
std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const char *access_pattern) {
std::vector<Step> steps;
size_t ProcessorStorage::assemble_program(const char *access_pattern) {
const size_t start = all_bus_steps_.size();
// Parse the access pattern to build microcycles.
while(*access_pattern) {
Step step;
BusStep step;
switch(*access_pattern) {
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]) {
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.
steps.push_back(step);
all_bus_steps_.push_back(step);
++access_pattern;
break;
case '-': // This is two NOPs in a row.
steps.push_back(step);
steps.push_back(step);
all_bus_steps_.push_back(step);
all_bus_steps_.push_back(step);
access_pattern += 2;
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.address = &effective_address_;
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.operation |= Microcycle::LowerData | Microcycle::UpperData;
step.action = Step::Action::IncrementEffectiveAddress;
steps.push_back(step);
step.action = BusStep::Action::IncrementEffectiveAddress;
all_bus_steps_.push_back(step);
access_pattern += 2;
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.address = &effective_address_;
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.operation |= Microcycle::LowerData | Microcycle::UpperData;
step.action = Step::Action::IncrementEffectiveAddress;
steps.push_back(step);
step.action = BusStep::Action::IncrementEffectiveAddress;
all_bus_steps_.push_back(step);
access_pattern += 2;
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.address = &program_counter_.full;
step.microcycle.value = &prefetch_queue_[1];
step.action = Step::Action::AdvancePrefetch;
steps.push_back(step);
step.action = BusStep::Action::AdvancePrefetch;
all_bus_steps_.push_back(step);
step.microcycle.length = HalfCycles(3);
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
step.action = Step::Action::IncrementProgramCounter;
steps.push_back(step);
step.action = BusStep::Action::IncrementProgramCounter;
all_bus_steps_.push_back(step);
access_pattern += 2;
break;
@ -112,11 +115,20 @@ std::vector<ProcessorStorage::Step> ProcessorStorage::assemble_program(const cha
}
// Add a final 'ScheduleNextProgram' sentinel.
Step end_program;
end_program.action = Step::Action::ScheduleNextProgram;
steps.push_back(end_program);
BusStep end_program;
end_program.action = BusStep::Action::ScheduleNextProgram;
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
(iii) given that there are plentiful disassemblers against which to test work in progress.
*/
void ProcessorStorage::install_instruction(int instruction) {
enum class Operation {
ABCD, ADD, ADDA, ADDI, ADDQ, ADDX, AND, ANDI,
ASL, ASLrmw, ASR, ASRrmw, Bcc,
TODO
void ProcessorStorage::install_instructions(const BusStepCollection &bus_step_collection) {
enum class Decoder {
Decimal,
RegOpModeReg,
SizeModeRegisterImmediate,
DataSizeModeQuick
};
struct PatternMapping {
uint16_t mask, value;
Operation operation;
Decoder decoder;
};
/*
Credit here is due to 'wrm' (https://github.com/wrm-za I assume) for his public
domain 68000 disassembler, from which the table below was largely sourced.
Manual legwork has been extended to check this table against the M68000
Programmer's Reference Manual, currently available at
Inspired partly by 'wrm' (https://github.com/wrm-za I assume); the following
table draws from the M68000 Programmer's Reference Manual, currently available at
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 = {
{0xf1f0, 0xc100, Operation::ABCD}, {0xf000, 0xd000, Operation::ADD},
{0xf0c0, 0xd0c0, Operation::ADDA}, {0xff00, 0x0600, Operation::ADDI},
{0xf100, 0x5000, Operation::ADDQ}, {0xf130, 0xd100, Operation::ADDX},
{0xf000, 0xc000, Operation::AND}, {0xff00, 0x0200, Operation::ANDI},
{0xf118, 0xe100, Operation::ASL}, {0xffc0, 0xe1c0, Operation::ASLrmw},
{0xf118, 0xe000, Operation::ASR}, {0xffc0, 0xe0c0, Operation::ASRrmw},
{0xf000, 0x6000, Operation::Bcc}, {0xf1c0, 0x0140, Operation::TODO},
{0xffc0, 0x0840, Operation::TODO}, {0xf1c0, 0x0180, Operation::TODO},
{0xffc0, 0x0880, Operation::TODO}, {0xf1c0, 0x01c0, Operation::TODO},
{0xffc0, 0x08c0, Operation::TODO}, {0xf1c0, 0x0100, Operation::TODO},
{0xffc0, 0x0800, Operation::TODO}, {0xf1c0, 0x4180, Operation::TODO},
{0xff00, 0x4200, Operation::TODO}, {0xf100, 0xb000, Operation::TODO},
{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}
{0xf1f0, 0x8100, Operation::SBCD, Decoder::Decimal}, // 4-171 (p275)
{0xf1f0, 0xc100, Operation::ABCD, Decoder::Decimal}, // 4-3 (p107)
{0xf000, 0x8000, Operation::OR, Decoder::RegOpModeReg}, // 4-150 (p226)
{0xf000, 0x9000, Operation::SUB, Decoder::RegOpModeReg}, // 4-174 (p278)
{0xf000, 0xb000, Operation::EOR, Decoder::RegOpModeReg}, // 4-100 (p204)
{0xf000, 0xc000, Operation::AND, Decoder::RegOpModeReg}, // 4-15 (p119)
{0xf000, 0xd000, Operation::ADD, Decoder::RegOpModeReg}, // 4-4 (p108)
{0xff00, 0x0600, Operation::ADD, Decoder::SizeModeRegisterImmediate}, // 4-9 (p113)
{0xff00, 0x0600, Operation::ADD, Decoder::DataSizeModeQuick}, // 4-11 (p115)
};
// Perform a linear search of the mappings above for this instruction.
for(const auto &mapping: mappings) {
if((instruction & mapping.mask) == mapping.value) {
if(mapping.operation == Operation::TODO) {
std::cout << std::hex << std::setw(4) << std::setfill('0');
std::cout << "Yet to implement, instruction matching: x & " << mapping.mask << " == " << mapping.value << std::endl;
for(int instruction = 0; instruction < 65536; ++instruction) {
for(const auto &mapping: mappings) {
if((instruction & mapping.mask) == mapping.value) {
switch(mapping.decoder) {
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_;
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;
enum class Action {
None,
@ -56,15 +61,53 @@ class ProcessorStorage {
} 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.
Step *active_program_ = nullptr;
A nullptr bus_program terminates a sequence of micro operations.
*/
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:
/*!
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
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
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
appropriate lookup tables.
*/
void install_instruction(int instruction);
void install_instructions(const BusStepCollection &);
};
#endif /* MC68000Storage_h */