diff --git a/InstructionSets/CachingExecutor.hpp b/InstructionSets/CachingExecutor.hpp index 9a61d5755..1a4dfb37f 100644 --- a/InstructionSets/CachingExecutor.hpp +++ b/InstructionSets/CachingExecutor.hpp @@ -61,16 +61,35 @@ template < uint64_t max_address, /// Indicates the maximum number of potential performers that will be provided. uint64_t max_performer_count, - /// Provides the type of Instruction to - typename InstructionType + /// Provides the type of Instruction to expect. + typename InstructionType, + /// Indicates whether instructions should be treated as ephemeral or included in the cache. + bool retain_instructions > class CachingExecutor { public: - - protected: using Performer = void (Executor::*)(); using PerformerIndex = typename MinIntTypeValue::type; using ProgramCounterType = typename MinIntTypeValue::type; + // MARK: - Parser call-ins. + + void announce_overflow(ProgramCounterType) { + /* + Should be impossible for now; this is intended to provide information + when page caching. + */ + } + void announce_instruction(ProgramCounterType, InstructionType instruction) { + // Dutifully map the instruction to a performer and keep it. + program_.push_back(static_cast(this)->action_for(instruction)); + + if constexpr (retain_instructions) { + // TODO. + } + } + + protected: + // Storage for the statically-allocated list of performers. It's a bit more // work for executors to fill this array, but subsequently performers can be // indexed by array position, which is a lot more compact than a generic pointer. @@ -86,37 +105,47 @@ template < and doing any translation as is necessary. */ void set_program_counter(ProgramCounterType address) { - const auto page = find_page(address); - const auto entry = page->entry_points.find(address); - if(entry == page->entry_points.end()) { - // Requested segment wasn't found; check whether it was - // within the recently translated list and otherwise - // translate it. - } + // Temporary implementation: just interpret. + program_.clear(); + static_cast(this)->parse(address, ProgramCounterType(max_address)); + +// const auto page = find_page(address); +// const auto entry = page->entry_points.find(address); +// if(entry == page->entry_points.end()) { +// // Requested segment wasn't found; check whether it was +// // within the recently translated list and otherwise +// // translate it. +// } } private: - struct Page { - std::map entry_points; + std::vector program_; + + /* TODO: almost below here can be shoved off into an LRUCache object, or similar. */ + +// static constexpr size_t max_cached_pages = 64; + +// struct Page { +// std::map entry_points; // TODO: can I statically these two? Should I? - std::vector actions_; +// std::vector actions_; // std::vector::value, InstructionType>::type> instructions_; - }; +// }; +// std::array pages_; // Maps from page numbers to pages. - std::unordered_map cached_pages_; +// std::unordered_map cached_pages_; // Maintains an LRU of recently-used pages in case of a need for reuse. - std::list touched_pages_; +// std::list touched_pages_; /*! Finds or creates the page that contains @c address. */ - Page *find_page(ProgramCounterType address) { +/* Page *find_page(ProgramCounterType address) { // TODO: are 1kb pages always appropriate? Is 64 the correct amount to keep? - const auto page_address = address >> 10; - constexpr size_t max_cached_pages = 64; + const auto page_address = ProgramCounterType(address >> 10); auto page = cached_pages_.find(page_address); if(page == cached_pages_.end()) { @@ -130,7 +159,9 @@ template < } else { // Page was found; LRU shuffle it. } - } + + return nullptr; + }*/ }; } diff --git a/InstructionSets/M50740/Executor.cpp b/InstructionSets/M50740/Executor.cpp index c32187495..e554635b9 100644 --- a/InstructionSets/M50740/Executor.cpp +++ b/InstructionSets/M50740/Executor.cpp @@ -20,6 +20,9 @@ Executor::Executor() { const auto instruction = decoder.instrucion_for_opcode(uint8_t(c)); performers_[c] = performer_lookup_.performer(instruction.operation, instruction.addressing_mode); } + + // TODO: read reset vector, etc. This is just the start of ROM. + set_program_counter(0x1400); } template void Executor::perform() { diff --git a/InstructionSets/M50740/Executor.hpp b/InstructionSets/M50740/Executor.hpp index 58791349a..b70a12f38 100644 --- a/InstructionSets/M50740/Executor.hpp +++ b/InstructionSets/M50740/Executor.hpp @@ -17,15 +17,16 @@ namespace InstructionSet { namespace M50740 { class Executor; +using CachingExecutor = CachingExecutor; -class Executor: public CachingExecutor { +class Executor: public CachingExecutor { public: Executor(); private: // MARK: - CachingExecutor-facing interface. - friend CachingExecutor; + friend CachingExecutor; /*! Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial. @@ -35,6 +36,14 @@ class Executor: public CachingExecutor { return instruction.opcode; } + /*! + Parses from @c start and no later than @c max_address, using the CachingExecutor as a target. + */ + inline void parse(uint16_t start, uint16_t closing_bound) { + Parser parser; + parser.parse(*this, memory_, start, closing_bound); + } + private: // MARK: - Internal framework for generator performers. diff --git a/InstructionSets/M50740/Parser.hpp b/InstructionSets/M50740/Parser.hpp index 397c2b311..8be7a2d00 100644 --- a/InstructionSets/M50740/Parser.hpp +++ b/InstructionSets/M50740/Parser.hpp @@ -20,8 +20,8 @@ template struct Parser { void parse(Target &target, const uint8_t *storage, uint16_t start, uint16_t closing_bound) { Decoder decoder; - while(start != closing_bound) { - const auto next = decoder.decode(&storage[start], closing_bound - start); + while(start <= closing_bound) { + const auto next = decoder.decode(&storage[start], 1 + closing_bound - start); if(next.first <= 0) { // If there weren't enough bytes left before the closing bound to complete // an instruction, but implicitly there were some bytes left, announce overflow @@ -36,7 +36,7 @@ template struct Parser { // Do a simplified test: is this a terminating operation? switch(next.second.operation) { case Operation::RTS: case Operation::RTI: case Operation::BRK: - case Operation::JMP: case Operation::BRA: + case Operation::JMP: case Operation::BRA: return; default: break;