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
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();
using Action = BusStep::Action;
std::vector<BusStep> steps;
std::stringstream stream(access_pattern);
// Tokenise the access pattern by splitting on spaces.
std::string token;
while(stream >> token) {
const char *next_access_pattern = access_pattern;
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;
// Check for a plus-or-minus suffix.
int post_adjustment = 0;
if(token.back() == '-' || token.back() == '+') {
if(token.back() == '-') {
if(end_of_pattern[-1] == '-' || end_of_pattern[-1] == '+') {
if(end_of_pattern[-1] == '-') {
post_adjustment = -1;
}
if(token.back() == '+') {
if(end_of_pattern[-1] == '+') {
post_adjustment = 1;
}
token.pop_back();
--end_of_pattern;
}
const auto token_length = end_of_pattern - access_pattern;
// Do nothing (possibly twice).
if(token == "n" || token == "nn") {
if(token.size() == 2) {
if(
access_pattern[0] == 'n' &&
(
token_length == 1 ||
(token_length == 2 && access_pattern[1] == 'n')
)
) {
if(token_length == 2) {
step.microcycle.length = HalfCycles(8);
}
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.
if(token == "r") {
if(
access_pattern[0] == 'r' &&
token_length == 1
) {
#ifndef NDEBUG
// If this is a debug build, not where the resizeable microcycle is
// (and double check that there's only the one).
@ -215,51 +249,86 @@ struct ProcessorStorageConstructor {
continue;
}
// Fetch SSP.
if(token == "nF" || token == "nf") {
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(token[1]) ? &storage_.address_[7].halves.high : &storage_.address_[7].halves.low;
steps.push_back(step);
if(
token_length == 2 &&
access_pattern[0] == 'n'
) {
// Fetch SSP.
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.action = Action::IncrementEffectiveAddress0;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
step.action = Action::IncrementEffectiveAddress0;
steps.push_back(step);
continue;
}
continue;
}
// Fetch exception vector.
if(token == "nV" || token == "nv") {
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(token[1]) ? &storage_.program_counter_.halves.high : &storage_.program_counter_.halves.low;
steps.push_back(step);
// Fetch exception vector.
if(tolower(access_pattern[1]) == 'v') {
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_.program_counter_.halves.high : &storage_.program_counter_.halves.low;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
step.action = Action::IncrementEffectiveAddress0;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
step.action = Action::IncrementEffectiveAddress0;
steps.push_back(step);
continue;
}
continue;
}
// Fetch from the program counter into the prefetch queue.
if(token == "np") {
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram;
step.microcycle.address = &storage_.program_counter_.full;
step.microcycle.value = &storage_.prefetch_queue_.halves.low;
step.action = Action::AdvancePrefetch;
steps.push_back(step);
// Fetch from the program counter into the prefetch queue.
if(access_pattern[1] == 'p') {
step.microcycle.operation = Microcycle::NewAddress | Microcycle::Read | Microcycle::IsProgram;
step.microcycle.address = &storage_.program_counter_.full;
step.microcycle.value = &storage_.prefetch_queue_.halves.low;
step.action = Action::AdvancePrefetch;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
step.action = Action::IncrementProgramCounter;
steps.push_back(step);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::Read | Microcycle::IsProgram | Microcycle::SelectWord;
step.action = Action::IncrementProgramCounter;
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.
if(token == "_") {
if(token_length == 1 && access_pattern[0] == '_') {
step.microcycle.length = HalfCycles(248);
step.microcycle.operation = Microcycle::Reset;
steps.push_back(step);
@ -268,22 +337,32 @@ struct ProcessorStorageConstructor {
}
// A standard read or write.
if(token == "nR" || token == "nr" || token == "nW" || token == "nw" || token == "nRd" || token == "nrd" || token == "nWr" || token == "nwr") {
const bool is_read = tolower(token[1]) == 'r';
const bool use_source_storage = (token == "nR" || token == "nr" || token == "nWr" || token == "nwr");
if(
access_pattern[0] == 'n' &&
(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];
assert(address_iterator != addresses.end());
step.microcycle.operation = Microcycle::NewAddress | (is_read ? Microcycle::Read : 0);
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);
step.microcycle.operation = Microcycle::SameAddress | (is_read ? Microcycle::Read : 0) | (read_full_words ? Microcycle::SelectWord : Microcycle::SelectByte);
if(post_adjustment) {
// 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;
} else {
step.action = (post_adjustment > 0) ? Action::IncrementEffectiveAddress1 : Action::DecrementEffectiveAddress1;
@ -296,70 +375,42 @@ struct ProcessorStorageConstructor {
continue;
}
// The completing part of a TAS.
if(token == "tas") {
RegisterPair32 *const scratch_data = &storage_.destination_bus_data_[0];
if(token_length == 3) {
// The completing part of a TAS.
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.operation = Microcycle::SameAddress;
step.microcycle.address = *address_iterator;
step.microcycle.value = &scratch_data->halves.low;
steps.push_back(step);
step.microcycle.length = HalfCycles(9);
step.microcycle.operation = Microcycle::SameAddress;
step.microcycle.address = *address_iterator;
step.microcycle.value = &scratch_data->halves.low;
steps.push_back(step);
step.microcycle.length = HalfCycles(3);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectByte;
steps.push_back(step);
++address_iterator;
step.microcycle.length = HalfCycles(3);
step.microcycle.operation = Microcycle::SameAddress | Microcycle::SelectByte;
steps.push_back(step);
++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.
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;
std::cerr << "MC68000 program builder; Unknown access token " << std::string(access_pattern, end_of_pattern) << std::endl;
assert(false);
}
@ -3127,8 +3178,8 @@ CPU::MC68000::ProcessorStorage::ProcessorStorage() {
}
movem_reads_pattern += "nr";
addresses.push_back(nullptr);
const size_t movem_read_offset = constructor.assemble_program(movem_reads_pattern, addresses);
const size_t movem_write_offset = constructor.assemble_program(movem_writes_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.c_str(), addresses);
// 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] });