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:
parent
5ace61f9b9
commit
8b19c523cf
@ -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;
|
||||||
|
}*/
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user