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:
parent
d7883d18d4
commit
31edb15369
@ -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 1–3; 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 1–3; 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] });
|
||||||
|
Loading…
Reference in New Issue
Block a user