1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

Starts to bend towards getting some performers in motion.

This commit is contained in:
Thomas Harte 2021-01-18 16:45:52 -05:00
parent 5ace61f9b9
commit 8b19c523cf
4 changed files with 69 additions and 26 deletions

View File

@ -61,16 +61,35 @@ template <
uint64_t max_address, uint64_t max_address,
/// Indicates the maximum number of potential performers that will be provided. /// Indicates the maximum number of potential performers that will be provided.
uint64_t max_performer_count, uint64_t max_performer_count,
/// Provides the type of Instruction to /// Provides the type of Instruction to expect.
typename InstructionType typename InstructionType,
/// Indicates whether instructions should be treated as ephemeral or included in the cache.
bool retain_instructions
> class CachingExecutor { > class CachingExecutor {
public: public:
protected:
using Performer = void (Executor::*)(); using Performer = void (Executor::*)();
using PerformerIndex = typename MinIntTypeValue<max_performer_count>::type; using PerformerIndex = typename MinIntTypeValue<max_performer_count>::type;
using ProgramCounterType = typename MinIntTypeValue<max_address>::type; using ProgramCounterType = typename MinIntTypeValue<max_address>::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<Executor *>(this)->action_for(instruction));
if constexpr (retain_instructions) {
// TODO.
}
}
protected:
// Storage for the statically-allocated list of performers. It's a bit more // 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 // 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. // 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. and doing any translation as is necessary.
*/ */
void set_program_counter(ProgramCounterType address) { void set_program_counter(ProgramCounterType address) {
const auto page = find_page(address); // Temporary implementation: just interpret.
const auto entry = page->entry_points.find(address); program_.clear();
if(entry == page->entry_points.end()) { static_cast<Executor *>(this)->parse(address, ProgramCounterType(max_address));
// Requested segment wasn't found; check whether it was
// within the recently translated list and otherwise // const auto page = find_page(address);
// translate it. // 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: private:
struct Page { std::vector<PerformerIndex> program_;
std::map<ProgramCounterType, PerformerIndex> entry_points;
/* 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<ProgramCounterType, PerformerIndex> entry_points;
// TODO: can I statically these two? Should I? // TODO: can I statically these two? Should I?
std::vector<PerformerIndex> actions_; // std::vector<PerformerIndex> actions_;
// std::vector<typename std::enable_if<!std::is_same<InstructionType, void>::value, InstructionType>::type> instructions_; // std::vector<typename std::enable_if<!std::is_same<InstructionType, void>::value, InstructionType>::type> instructions_;
}; // };
// std::array<Page, max_cached_pages> pages_;
// Maps from page numbers to pages. // Maps from page numbers to pages.
std::unordered_map<ProgramCounterType, Page> cached_pages_; // std::unordered_map<ProgramCounterType, Page *> cached_pages_;
// Maintains an LRU of recently-used pages in case of a need for reuse. // Maintains an LRU of recently-used pages in case of a need for reuse.
std::list<ProgramCounterType> touched_pages_; // std::list<ProgramCounterType> touched_pages_;
/*! /*!
Finds or creates the page that contains @c address. 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? // TODO: are 1kb pages always appropriate? Is 64 the correct amount to keep?
const auto page_address = address >> 10; const auto page_address = ProgramCounterType(address >> 10);
constexpr size_t max_cached_pages = 64;
auto page = cached_pages_.find(page_address); auto page = cached_pages_.find(page_address);
if(page == cached_pages_.end()) { if(page == cached_pages_.end()) {
@ -130,7 +159,9 @@ template <
} else { } else {
// Page was found; LRU shuffle it. // Page was found; LRU shuffle it.
} }
}
return nullptr;
}*/
}; };
} }

View File

@ -20,6 +20,9 @@ Executor::Executor() {
const auto instruction = decoder.instrucion_for_opcode(uint8_t(c)); const auto instruction = decoder.instrucion_for_opcode(uint8_t(c));
performers_[c] = performer_lookup_.performer(instruction.operation, instruction.addressing_mode); 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 <Operation operation, AddressingMode addressing_mode> void Executor::perform() { template <Operation operation, AddressingMode addressing_mode> void Executor::perform() {

View File

@ -17,15 +17,16 @@ namespace InstructionSet {
namespace M50740 { namespace M50740 {
class Executor; class Executor;
using CachingExecutor = CachingExecutor<Executor, 0x1fff, 256, Instruction, false>;
class Executor: public CachingExecutor<Executor, 0x2000, 256, void> { class Executor: public CachingExecutor {
public: public:
Executor(); Executor();
private: private:
// MARK: - CachingExecutor-facing interface. // MARK: - CachingExecutor-facing interface.
friend CachingExecutor<Executor, 0x2000, 256, void>; friend CachingExecutor;
/*! /*!
Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial. Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial.
@ -35,6 +36,14 @@ class Executor: public CachingExecutor<Executor, 0x2000, 256, void> {
return instruction.opcode; 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<Executor, false> parser;
parser.parse(*this, memory_, start, closing_bound);
}
private: private:
// MARK: - Internal framework for generator performers. // MARK: - Internal framework for generator performers.

View File

@ -20,8 +20,8 @@ template<typename Target, bool include_entries_and_accesses> struct Parser {
void parse(Target &target, const uint8_t *storage, uint16_t start, uint16_t closing_bound) { void parse(Target &target, const uint8_t *storage, uint16_t start, uint16_t closing_bound) {
Decoder decoder; Decoder decoder;
while(start != closing_bound) { while(start <= closing_bound) {
const auto next = decoder.decode(&storage[start], closing_bound - start); const auto next = decoder.decode(&storage[start], 1 + closing_bound - start);
if(next.first <= 0) { if(next.first <= 0) {
// If there weren't enough bytes left before the closing bound to complete // If there weren't enough bytes left before the closing bound to complete
// an instruction, but implicitly there were some bytes left, announce overflow // an instruction, but implicitly there were some bytes left, announce overflow
@ -36,7 +36,7 @@ template<typename Target, bool include_entries_and_accesses> struct Parser {
// Do a simplified test: is this a terminating operation? // Do a simplified test: is this a terminating operation?
switch(next.second.operation) { switch(next.second.operation) {
case Operation::RTS: case Operation::RTI: case Operation::BRK: case Operation::RTS: case Operation::RTI: case Operation::BRK:
case Operation::JMP: case Operation::BRA: case Operation::JMP: case Operation::BRA:
return; return;
default: break; default: break;