1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 03:32:32 +00:00

Reduces 68000 startup costs a little further.

This commit is contained in:
Thomas Harte 2019-06-25 17:41:13 -04:00
parent d7883d18d4
commit 31edb15369

View File

@ -168,35 +168,66 @@ struct ProcessorStorageConstructor {
then the corresponding effective address will be incremented or decremented then the corresponding effective address will be incremented or decremented
by two after the cycle has completed. by two after the cycle has completed.
*/ */
size_t assemble_program(std::string access_pattern, const std::vector<uint32_t *> &addresses = {}, bool read_full_words = true) { size_t assemble_program(const char *access_pattern, const std::vector<uint32_t *> &addresses = {}, bool read_full_words = true) {
auto address_iterator = addresses.begin(); auto address_iterator = addresses.begin();
using Action = BusStep::Action; using Action = BusStep::Action;
std::vector<BusStep> steps; std::vector<BusStep> steps;
std::stringstream stream(access_pattern);
// Tokenise the access pattern by splitting on spaces. // Tokenise the access pattern by splitting on spaces.
std::string token; const char *next_access_pattern = access_pattern;
while(stream >> token) { while(true) {
/*
Ugly C-style string parsing here:
next_access_pattern is the end of the previous access pattern, i.e.
it is where parsing should begin to find the next one.
end_of_pattern will be where the current pattern ends, after any
modifier suffixes have been removed.
access_pattern is the beginning of the current pattern.
Obiter: this replaces a std::stringstream >> std::string implementation,
that was a lot cleaner but implied a lot of std::string constructions
that made this section of code measureable slower. Which, inter alia,
had a bit impact on the rate at which unit tests would run.
So this ugliness is a net project improvement, I promise!
*/
while(*next_access_pattern == ' ') ++next_access_pattern;
access_pattern = next_access_pattern;
while(*next_access_pattern != ' ' && *next_access_pattern != '\0') ++next_access_pattern;
if(next_access_pattern == access_pattern) break;
const char *end_of_pattern = next_access_pattern;
ProcessorBase::BusStep step; ProcessorBase::BusStep step;
// Check for a plus-or-minus suffix. // Check for a plus-or-minus suffix.
int post_adjustment = 0; int post_adjustment = 0;
if(token.back() == '-' || token.back() == '+') { if(end_of_pattern[-1] == '-' || end_of_pattern[-1] == '+') {
if(token.back() == '-') { if(end_of_pattern[-1] == '-') {
post_adjustment = -1; post_adjustment = -1;
} }
if(token.back() == '+') { if(end_of_pattern[-1] == '+') {
post_adjustment = 1; post_adjustment = 1;
} }
token.pop_back(); --end_of_pattern;
} }
const auto token_length = end_of_pattern - access_pattern;
// Do nothing (possibly twice). // Do nothing (possibly twice).
if(token == "n" || token == "nn") { if(
if(token.size() == 2) { access_pattern[0] == 'n' &&
(
token_length == 1 ||
(token_length == 2 && access_pattern[1] == 'n')
)
) {
if(token_length == 2) {
step.microcycle.length = HalfCycles(8); step.microcycle.length = HalfCycles(8);
} }
steps.push_back(step); steps.push_back(step);
@ -204,7 +235,10 @@ struct ProcessorStorageConstructor {
} }
// Do nothing, but with a length that definitely won't map it to the other do-nothings. // Do nothing, but with a length that definitely won't map it to the other do-nothings.
if(token == "r") { if(
access_pattern[0] == 'r' &&
token_length == 1
) {
#ifndef NDEBUG #ifndef NDEBUG
// If this is a debug build, not where the resizeable microcycle is // If this is a debug build, not where the resizeable microcycle is
// (and double check that there's only the one). // (and double check that there's only the one).
@ -215,51 +249,86 @@ struct ProcessorStorageConstructor {
continue; continue;
} }
// Fetch SSP. if(
if(token == "nF" || token == "nf") { token_length == 2 &&
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; // IsProgram is a guess. access_pattern[0] == 'n'
step.microcycle.address = &storage_.effective_address_[0].full; ) {
step.microcycle.value = isupper(token[1]) ? &storage_.address_[7].halves.high : &storage_.address_[7].halves.low; // Fetch SSP.
steps.push_back(step); if(tolower(access_pattern[1]) == 'f') {
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; // IsProgram is a guess.
step.microcycle.address = &storage_.effective_address_[0].full;
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.address_[7].halves.high : &storage_.address_[7].halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord; step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
step.action = Action::IncrementEffectiveAddress0; step.action = Action::IncrementEffectiveAddress0;
steps.push_back(step); steps.push_back(step);
continue; continue;
} }
// Fetch exception vector. // Fetch exception vector.
if(token == "nV" || token == "nv") { if(tolower(access_pattern[1]) == 'v') {
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; // IsProgram is a guess. step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; // IsProgram is a guess.
step.microcycle.address = &storage_.effective_address_[0].full; step.microcycle.address = &storage_.effective_address_[0].full;
step.microcycle.value = isupper(token[1]) ? &storage_.program_counter_.halves.high : &storage_.program_counter_.halves.low; step.microcycle.value = isupper(access_pattern[1]) ? &storage_.program_counter_.halves.high : &storage_.program_counter_.halves.low;
steps.push_back(step); steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord; step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
step.action = Action::IncrementEffectiveAddress0; step.action = Action::IncrementEffectiveAddress0;
steps.push_back(step); steps.push_back(step);
continue; continue;
} }
// Fetch from the program counter into the prefetch queue. // Fetch from the program counter into the prefetch queue.
if(token == "np") { if(access_pattern[1] == 'p') {
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram; step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram;
step.microcycle.address = &storage_.program_counter_.full; step.microcycle.address = &storage_.program_counter_.full;
step.microcycle.value = &storage_.prefetch_queue_.halves.low; step.microcycle.value = &storage_.prefetch_queue_.halves.low;
step.action = Action::AdvancePrefetch; step.action = Action::AdvancePrefetch;
steps.push_back(step); steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord; step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
step.action = Action::IncrementProgramCounter; step.action = Action::IncrementProgramCounter;
steps.push_back(step); steps.push_back(step);
continue; continue;
}
// A stack write.
if(tolower(access_pattern[1]) == 's') {
step.microcycle.operation = Microcycle::NewAddress;
step.microcycle.address = &storage_.effective_address_[1].full;
step.microcycle.value = isupper(access_pattern[1]) ? &storage_.destination_bus_data_[0].halves.high : &storage_.destination_bus_data_[0].halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectWord;
step.action = Action::DecrementEffectiveAddress1;
steps.push_back(step);
continue;
}
// A stack read.
if(tolower(access_pattern[1]) == 'u') {
RegisterPair32 *const scratch_data = &storage_.source_bus_data_[0];
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read;
step.microcycle.address = &storage_.effective_address_[0].full;
step.microcycle.value = isupper(access_pattern[1]) ? &scratch_data->halves.high : &scratch_data->halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::SelectWord;
step.action = Action::IncrementEffectiveAddress0;
steps.push_back(step);
continue;
}
} }
// The reset cycle. // The reset cycle.
if(token == "_") { if(token_length == 1 && access_pattern[0] == '_') {
step.microcycle.length = HalfCycles(248); step.microcycle.length = HalfCycles(248);
step.microcycle.operation = Microcycle::Reset; step.microcycle.operation = Microcycle::Reset;
steps.push_back(step); steps.push_back(step);
@ -268,22 +337,32 @@ struct ProcessorStorageConstructor {
} }
// A standard read or write. // A standard read or write.
if(token == "nR" || token == "nr" || token == "nW" || token == "nw" || token == "nRd" || token == "nrd" || token == "nWr" || token == "nwr") { if(
const bool is_read = tolower(token[1]) == 'r'; access_pattern[0] == 'n' &&
const bool use_source_storage = (token == "nR" || token == "nr" || token == "nWr" || token == "nwr"); (tolower(access_pattern[1]) == 'r' || tolower(access_pattern[1]) == 'w') &&
(
token_length == 2 ||
(
token_length == 3 &&
(access_pattern[2] == 'd' || access_pattern[2] == 'r')
)
)
) {
const bool is_read = tolower(access_pattern[1]) == 'r';
const bool use_source_storage = tolower(end_of_pattern[-1]) == 'r';
RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_[0] : &storage_.destination_bus_data_[0]; RegisterPair32 *const scratch_data = use_source_storage ? &storage_.source_bus_data_[0] : &storage_.destination_bus_data_[0];
assert(address_iterator != addresses.end()); assert(address_iterator != addresses.end());
step.microcycle.operation = Microcycle::NewAddress | (is_read ? Microcycle::Read : 0); step.microcycle.operation = Microcycle::NewAddress | (is_read ? Microcycle::Read : 0);
step.microcycle.address = *address_iterator; step.microcycle.address = *address_iterator;
step.microcycle.value = isupper(token[1]) ? &scratch_data->halves.high : &scratch_data->halves.low; step.microcycle.value = isupper(access_pattern[1]) ? &scratch_data->halves.high : &scratch_data->halves.low;
steps.push_back(step); steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | (is_read ? Microcycle::Read : 0) | (read_full_words ? Microcycle::SelectWord : Microcycle::SelectByte); step.microcycle.operation = Microcycle::SameAddress | (is_read ? Microcycle::Read : 0) | (read_full_words ? Microcycle::SelectWord : Microcycle::SelectByte);
if(post_adjustment) { if(post_adjustment) {
// nr and nR should affect address 0; nw, nW, nrd and nRd should affect address 1. // nr and nR should affect address 0; nw, nW, nrd and nRd should affect address 1.
if(tolower(token[1]) == 'r' && token.size() == 2) { if(tolower(access_pattern[1]) == 'r' && token_length == 2) {
step.action = (post_adjustment > 0) ? Action::IncrementEffectiveAddress0 : Action::DecrementEffectiveAddress0; step.action = (post_adjustment > 0) ? Action::IncrementEffectiveAddress0 : Action::DecrementEffectiveAddress0;
} else { } else {
step.action = (post_adjustment > 0) ? Action::IncrementEffectiveAddress1 : Action::DecrementEffectiveAddress1; step.action = (post_adjustment > 0) ? Action::IncrementEffectiveAddress1 : Action::DecrementEffectiveAddress1;
@ -296,70 +375,42 @@ struct ProcessorStorageConstructor {
continue; continue;
} }
// The completing part of a TAS. if(token_length == 3) {
if(token == "tas") { // The completing part of a TAS.
RegisterPair32 *const scratch_data = &storage_.destination_bus_data_[0]; if(access_pattern[0] == 't' && access_pattern[1] == 'a' && access_pattern[2] == 's') {
RegisterPair32 *const scratch_data = &storage_.destination_bus_data_[0];
assert(address_iterator != addresses.end()); assert(address_iterator != addresses.end());
step.microcycle.length = HalfCycles(9); step.microcycle.length = HalfCycles(9);
step.microcycle.operation = Microcycle::SameAddress; step.microcycle.operation = Microcycle::SameAddress;
step.microcycle.address = *address_iterator; step.microcycle.address = *address_iterator;
step.microcycle.value = &scratch_data->halves.low; step.microcycle.value = &scratch_data->halves.low;
steps.push_back(step); steps.push_back(step);
step.microcycle.length = HalfCycles(3); step.microcycle.length = HalfCycles(3);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectByte; step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectByte;
steps.push_back(step); steps.push_back(step);
++address_iterator; ++address_iterator;
continue; continue;
}
// Interrupt acknowledge.
if(access_pattern[0] == 'i' && access_pattern[1] == 'n' && access_pattern[2] == 't') {
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::NewAddress;
step.microcycle.address = &storage_.effective_address_[0].full; // The selected interrupt should be in bits 13; but 0 should be set.
step.microcycle.value = &storage_.source_bus_data_[0].halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::SameAddress | Microcycle::SelectByte;
steps.push_back(step);
continue;
}
} }
// A stack write. std::cerr << "MC68000 program builder; Unknown access token " << std::string(access_pattern, end_of_pattern) << std::endl;
if(token == "nS" || token == "ns") {
step.microcycle.operation = Microcycle::NewAddress;
step.microcycle.address = &storage_.effective_address_[1].full;
step.microcycle.value = isupper(token[1]) ? &storage_.destination_bus_data_[0].halves.high : &storage_.destination_bus_data_[0].halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectWord;
step.action = Action::DecrementEffectiveAddress1;
steps.push_back(step);
continue;
}
// A stack read.
if(token == "nU" || token == "nu") {
RegisterPair32 *const scratch_data = &storage_.source_bus_data_[0];
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read;
step.microcycle.address = &storage_.effective_address_[0].full;
step.microcycle.value = isupper(token[1]) ? &scratch_data->halves.high : &scratch_data->halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::SelectWord;
step.action = Action::IncrementEffectiveAddress0;
steps.push_back(step);
continue;
}
// Interrupt acknowledge.
if(token == "int") {
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::NewAddress;
step.microcycle.address = &storage_.effective_address_[0].full; // The selected interrupt should be in bits 13; but 0 should be set.
step.microcycle.value = &storage_.source_bus_data_[0].halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::InterruptAcknowledge | Microcycle::SameAddress | Microcycle::SelectByte;
steps.push_back(step);
continue;
}
std::cerr << "MC68000 program builder; Unknown access token " << token << std::endl;
assert(false); assert(false);
} }
@ -3127,8 +3178,8 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() {
} }
movem_reads_pattern += "nr"; movem_reads_pattern += "nr";
addresses.push_back(nullptr); addresses.push_back(nullptr);
const size_t movem_read_offset = constructor.assemble_program(movem_reads_pattern, addresses); const size_t movem_read_offset = constructor.assemble_program(movem_reads_pattern.c_str(), addresses);
const size_t movem_write_offset = constructor.assemble_program(movem_writes_pattern, addresses); const size_t movem_write_offset = constructor.assemble_program(movem_writes_pattern.c_str(), addresses);
// Target addresses and values will be filled in by TRAP/illegal too. // Target addresses and values will be filled in by TRAP/illegal too.
const size_t trap_offset = constructor.assemble_program("r nw nw nW nV nv np np", { &precomputed_addresses_[0], &precomputed_addresses_[1], &precomputed_addresses_[2] }); const size_t trap_offset = constructor.assemble_program("r nw nw nW nV nv np np", { &precomputed_addresses_[0], &precomputed_addresses_[1], &precomputed_addresses_[2] });