2019-03-09 05:00:23 +00:00
|
|
|
//
|
|
|
|
// 68000Storage.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 08/03/2019.
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "../68000.hpp"
|
|
|
|
|
2019-03-13 02:46:31 +00:00
|
|
|
#include <array>
|
|
|
|
|
2019-03-09 05:00:23 +00:00
|
|
|
using namespace CPU::MC68000;
|
|
|
|
|
|
|
|
ProcessorStorage::ProcessorStorage() {
|
2019-03-13 02:46:31 +00:00
|
|
|
// Create the exception programs.
|
|
|
|
const size_t reset_offset = assemble_program("n- n- n- n- n- nn nF nf nV nv np np");
|
2019-03-10 21:27:34 +00:00
|
|
|
|
2019-03-13 02:46:31 +00:00
|
|
|
// Install all necessary access patterns.
|
|
|
|
const BusStepCollection bus_steps = assemble_standard_bus_steps();
|
2019-03-12 02:47:58 +00:00
|
|
|
|
|
|
|
// Install operations.
|
2019-03-13 02:46:31 +00:00
|
|
|
install_instructions(bus_steps);
|
|
|
|
|
|
|
|
// Realise the exception programs as direct pointers.
|
|
|
|
reset_program_ = &all_bus_steps_[reset_offset];
|
2019-03-12 02:47:58 +00:00
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
// Set initial state. Largely TODO.
|
2019-03-13 02:46:31 +00:00
|
|
|
active_step_ = reset_program_;
|
2019-03-10 21:27:34 +00:00
|
|
|
effective_address_ = 0;
|
2019-03-10 21:42:13 +00:00
|
|
|
is_supervisor_ = 1;
|
2019-03-09 05:00:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 02:46:31 +00:00
|
|
|
size_t ProcessorStorage::assemble_program(const char *access_pattern) {
|
|
|
|
const size_t start = all_bus_steps_.size();
|
2019-03-09 05:00:23 +00:00
|
|
|
|
|
|
|
// Parse the access pattern to build microcycles.
|
|
|
|
while(*access_pattern) {
|
2019-03-13 02:46:31 +00:00
|
|
|
BusStep step;
|
2019-03-09 05:00:23 +00:00
|
|
|
|
|
|
|
switch(*access_pattern) {
|
2019-03-10 21:27:34 +00:00
|
|
|
case '\t': case ' ': // White space acts as a no-op; it's for clarity only.
|
|
|
|
++access_pattern;
|
2019-03-09 05:00:23 +00:00
|
|
|
break;
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
case 'n': // This might be a plain NOP cycle, in which some internal calculation occurs,
|
|
|
|
// or it might pair off with something afterwards.
|
|
|
|
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.
|
2019-03-13 02:46:31 +00:00
|
|
|
all_bus_steps_.push_back(step);
|
2019-03-10 21:27:34 +00:00
|
|
|
++access_pattern;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '-': // This is two NOPs in a row.
|
2019-03-13 02:46:31 +00:00
|
|
|
all_bus_steps_.push_back(step);
|
|
|
|
all_bus_steps_.push_back(step);
|
2019-03-10 21:27:34 +00:00
|
|
|
access_pattern += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'F': // Fetch SSP MSW.
|
|
|
|
case 'f': // Fetch SSP LSW.
|
|
|
|
step.microcycle.length = HalfCycles(5);
|
2019-03-10 21:42:13 +00:00
|
|
|
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess.
|
2019-03-10 21:27:34 +00:00
|
|
|
step.microcycle.address = &effective_address_;
|
|
|
|
step.microcycle.value = isupper(access_pattern[1]) ? &stack_pointers_[1].halves.high : &stack_pointers_[1].halves.low;
|
2019-03-13 02:46:31 +00:00
|
|
|
all_bus_steps_.push_back(step);
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
|
|
step.microcycle.length = HalfCycles(3);
|
|
|
|
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
|
2019-03-13 02:46:31 +00:00
|
|
|
step.action = BusStep::Action::IncrementEffectiveAddress;
|
|
|
|
all_bus_steps_.push_back(step);
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
|
|
access_pattern += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'V': // Fetch exception vector low.
|
|
|
|
case 'v': // Fetch exception vector high.
|
|
|
|
step.microcycle.length = HalfCycles(5);
|
2019-03-10 21:42:13 +00:00
|
|
|
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram; // IsProgram is a guess.
|
2019-03-10 21:27:34 +00:00
|
|
|
step.microcycle.address = &effective_address_;
|
|
|
|
step.microcycle.value = isupper(access_pattern[1]) ? &program_counter_.halves.high : &program_counter_.halves.low;
|
2019-03-13 02:46:31 +00:00
|
|
|
all_bus_steps_.push_back(step);
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
|
|
step.microcycle.length = HalfCycles(3);
|
|
|
|
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
|
2019-03-13 02:46:31 +00:00
|
|
|
step.action = BusStep::Action::IncrementEffectiveAddress;
|
|
|
|
all_bus_steps_.push_back(step);
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
|
|
access_pattern += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p': // Fetch from the program counter into the prefetch queue.
|
|
|
|
step.microcycle.length = HalfCycles(5);
|
2019-03-10 21:42:13 +00:00
|
|
|
step.microcycle.operation = Microcycle::Address | Microcycle::ReadWrite | Microcycle::IsProgram;
|
2019-03-10 21:27:34 +00:00
|
|
|
step.microcycle.address = &program_counter_.full;
|
|
|
|
step.microcycle.value = &prefetch_queue_[1];
|
2019-03-13 02:46:31 +00:00
|
|
|
step.action = BusStep::Action::AdvancePrefetch;
|
|
|
|
all_bus_steps_.push_back(step);
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
|
|
step.microcycle.length = HalfCycles(3);
|
|
|
|
step.microcycle.operation |= Microcycle::LowerData | Microcycle::UpperData;
|
2019-03-13 02:46:31 +00:00
|
|
|
step.action = BusStep::Action::IncrementProgramCounter;
|
|
|
|
all_bus_steps_.push_back(step);
|
2019-03-10 21:27:34 +00:00
|
|
|
|
|
|
|
access_pattern += 2;
|
|
|
|
break;
|
|
|
|
}
|
2019-03-09 05:00:23 +00:00
|
|
|
break;
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
default:
|
|
|
|
std::cerr << "MC68000 program builder; Unknown access type " << *access_pattern << std::endl;
|
|
|
|
assert(false);
|
2019-03-09 05:00:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 21:27:34 +00:00
|
|
|
// Add a final 'ScheduleNextProgram' sentinel.
|
2019-03-13 02:46:31 +00:00
|
|
|
BusStep end_program;
|
|
|
|
end_program.action = BusStep::Action::ScheduleNextProgram;
|
|
|
|
all_bus_steps_.push_back(end_program);
|
2019-03-09 05:00:23 +00:00
|
|
|
|
2019-03-13 02:46:31 +00:00
|
|
|
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;
|
2019-03-09 05:00:23 +00:00
|
|
|
}
|
2019-03-12 02:47:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
install_instruction acts, in effect, in the manner of a disassembler. So this class is
|
|
|
|
formulated to run through all potential 65536 instuction encodings and attempt to
|
|
|
|
disassemble each, rather than going in the opposite direction.
|
|
|
|
|
|
|
|
This has two benefits:
|
|
|
|
|
|
|
|
(i) which addressing modes go with which instructions falls out automatically;
|
|
|
|
(ii) it is a lot easier during the manual verification stage of development to work
|
|
|
|
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.
|
|
|
|
*/
|
2019-03-13 02:46:31 +00:00
|
|
|
void ProcessorStorage::install_instructions(const BusStepCollection &bus_step_collection) {
|
|
|
|
enum class Decoder {
|
|
|
|
Decimal,
|
|
|
|
RegOpModeReg,
|
|
|
|
SizeModeRegisterImmediate,
|
|
|
|
DataSizeModeQuick
|
2019-03-12 02:47:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct PatternMapping {
|
|
|
|
uint16_t mask, value;
|
|
|
|
Operation operation;
|
2019-03-13 02:46:31 +00:00
|
|
|
Decoder decoder;
|
2019-03-12 02:47:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2019-03-13 02:46:31 +00:00
|
|
|
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
|
2019-03-12 02:47:58 +00:00
|
|
|
https://www.nxp.com/files-static/archives/doc/ref_manual/M68000PRM.pdf
|
2019-03-13 02:46:31 +00:00
|
|
|
|
|
|
|
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.
|
2019-03-12 02:47:58 +00:00
|
|
|
*/
|
|
|
|
const std::vector<PatternMapping> mappings = {
|
2019-03-13 02:46:31 +00:00
|
|
|
{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)
|
2019-03-12 02:47:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Perform a linear search of the mappings above for this instruction.
|
2019-03-13 02:46:31 +00:00
|
|
|
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;
|
2019-03-12 02:47:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|