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

Starts formalising end conditions.

This commit is contained in:
Thomas Harte 2021-01-21 22:36:44 -05:00
parent 0fafbf5092
commit adef2e9b4e
3 changed files with 51 additions and 18 deletions

View File

@ -94,7 +94,7 @@ template <
// 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.
std::array<Performer, max_performer_count> performers_;
ProgramCounterType program_counter_;
/*!
Moves the current point of execution to @c address, updating necessary performer caches
@ -104,6 +104,7 @@ template <
// Set flag to terminate any inner loop currently running through
// previously-parsed content.
has_branched_ = true;
program_counter_ = address;
// Temporary implementation: just interpret.
program_.clear();
@ -137,11 +138,36 @@ template <
}
}
private:
bool has_branched_ = false;
/*!
Runs for @c duration; the intention is that subclasses provide a method
that is clear about units, and call this to count down in whatever units they
count down in.
*/
void run_for(int duration) {
remaining_duration_ += duration;
while(remaining_duration_ > 0) {
has_branched_ = false;
while(remaining_duration_ > 0 && !has_branched_) {
(static_cast<Executor *>(this)->*performers_[program_index_])();
++program_index_;
}
}
}
/*!
Should be called by a specific executor to subtract from the remaining
running duration.
*/
inline void subtract_duration(int duration) {
remaining_duration_ -= duration;
}
private:
bool has_branched_ = false;
int remaining_duration_ = 0;
std::vector<PerformerIndex> program_;
size_t program_index_ = 0;
/* TODO: almost below here can be shoved off into an LRUCache object, or similar. */

View File

@ -37,9 +37,11 @@ void Executor::set_rom(const std::vector<uint8_t> &rom) {
reset();
// TEMPORARY: just to test initial wiring.
for(int c = 0; c < 1300; c++) {
run_to_branch();
run_for(Cycles(13000));
}
void Executor::run_for(Cycles cycles) {
CachingExecutor::run_for(cycles.as<int>());
}
void Executor::reset() {
@ -126,6 +128,12 @@ template<bool is_brk> inline void Executor::perform_interrupt() {
}
template <Operation operation, AddressingMode addressing_mode> void Executor::perform() {
printf("%04x\t%02x\t%d %d\t[x:%02x s:%02x]\n", program_counter_ & 0x1fff, memory_[program_counter_ & 0x1fff], int(operation), int(addressing_mode), x_, s_);
// Post cycle cost; this emulation _does not provide accurate timing_.
// TODO: post actual cycle counts. For now count instructions only.
subtract_duration(1);
// Deal with all modes that don't access memory up here;
// those that access memory will go through a slightly longer
// sequence below that wraps the address and checks whether
@ -135,8 +143,6 @@ template <Operation operation, AddressingMode addressing_mode> void Executor::pe
#define next8() memory_[(program_counter_ + 1) & 0x1fff]
#define next16() (memory_[(program_counter_ + 1) & 0x1fff] | (memory_[(program_counter_ + 2) & 0x1fff] << 8))
printf("%04x\t%02x\t%d %d\t[x:%02x s:%02x]\n", program_counter_ & 0x1fff, memory_[program_counter_ & 0x1fff], int(operation), int(addressing_mode), x_, s_);
// Underlying assumption below: the instruction stream will never
// overlap with IO ports.
switch(addressing_mode) {
@ -204,7 +210,7 @@ template <Operation operation, AddressingMode addressing_mode> void Executor::pe
case AddressingMode::AbsoluteY: address = next16() + y_; break;
case AddressingMode::ZeroPage: address = next8(); break;
case AddressingMode::ZeroPageX: address = (next8() + x_) & 0xff; break;
case AddressingMode::ZeroPageY: address = (next8() + x_) & 0xff; break;
case AddressingMode::ZeroPageY: address = (next8() + y_) & 0xff; break;
case AddressingMode::ZeroPageIndirect:
address = next8();
@ -398,6 +404,10 @@ template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_u
overflow_result_ = uint8_t(*operand << 1);
break;
case Operation::TST:
set_nz(*operand);
break;
/*
Operations affected by the index mode flag: ADC, AND, CMP, EOR, LDA, ORA, and SBC.
*/
@ -514,9 +524,3 @@ template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_u
}
#undef set_nz
}
void Executor::set_program_counter(uint16_t address) {
printf("--- %04x ---\n", (address & 0x1fff) - 0x1000);
program_counter_ = address;
CachingExecutor::set_program_counter(address);
}

View File

@ -12,6 +12,7 @@
#include "Instruction.hpp"
#include "Parser.hpp"
#include "../CachingExecutor.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include <cstdint>
#include <vector>
@ -34,6 +35,11 @@ class Executor: public CachingExecutor {
void set_rom(const std::vector<uint8_t> &rom);
void reset();
/*!
Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles.
*/
void run_for(Cycles cycles);
private:
// MARK: - CachingExecutor-facing interface.
@ -106,8 +112,6 @@ class Executor: public CachingExecutor {
*/
template <Operation operation, AddressingMode addressing_mode> void perform();
void set_program_counter(uint16_t address);
private:
// MARK: - Instruction set state.
@ -115,7 +119,6 @@ class Executor: public CachingExecutor {
uint8_t memory_[0x2000];
// Registers.
uint16_t program_counter_;
uint8_t a_, x_, y_, s_;
uint8_t negative_result_ = 0;