// // Executor.h // Clock Signal // // Created by Thomas Harte on 16/01/21. // Copyright © 2021 Thomas Harte. All rights reserved. // #pragma once #include "Instruction.hpp" #include "Parser.hpp" #include "../CachingExecutor.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" #include #include #include namespace InstructionSet::M50740 { class Executor; using CachingExecutor = CachingExecutor; struct PortHandler { virtual void run_ports_for(Cycles) = 0; virtual void set_port_output(int port, uint8_t value) = 0; virtual uint8_t get_port_input(int port) = 0; }; /*! Executes M50740 code subject to heavy limitations: * the instruction stream cannot run across any of the specialised IO addresses; and * timing is correct to whole-opcode boundaries only. */ class Executor: public CachingExecutor { public: Executor(PortHandler &); void set_rom(const std::vector &rom); void reset(); void set_interrupt_line(bool); uint8_t get_output_mask(int port); /*! Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles. */ void run_for(Cycles); private: // MARK: - CachingExecutor-facing interface. friend CachingExecutor; /*! Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial. */ inline PerformerIndex action_for(const Instruction instruction) { // This is a super-simple processor, so the opcode can be used directly to index the performers. return instruction.opcode; } /*! Parses from @c start and no later than @c max_address, using the CachingExecutor as a target. */ inline void parse(const uint16_t start, const uint16_t closing_bound) { Parser parser; parser.parse(*this, &memory_[0], start & 0x1fff, closing_bound); } // MARK: - Internal framework for generator performers. /*! Provides dynamic lookup of @c perform(Executor*). */ class PerformerLookup { public: PerformerLookup() { fill(performers_); } Performer performer(const Operation operation, const AddressingMode addressing_mode) { const auto index = (int(operation) - MinOperation) * (1 + MaxAddressingMode - MinAddressingMode) + (int(addressing_mode) - MinAddressingMode); return performers_[index]; } private: Performer performers_[(1 + MaxAddressingMode - MinAddressingMode) * (1 + MaxOperation - MinOperation)]; template void fill_operation(Performer *target) { *target = &Executor::perform; if constexpr (addressing_mode+1 <= MaxAddressingMode) { fill_operation(target + 1); } } template void fill(Performer *target) { fill_operation(target); target += 1 + MaxAddressingMode - MinAddressingMode; if constexpr (operation+1 <= MaxOperation) { fill(target); } } }; inline static PerformerLookup performer_lookup_; /*! Performs @c operation using @c operand as the value fetched from memory, if any. */ template void perform(uint8_t *operand); /*! Performs @c operation in @c addressing_mode. */ template void perform(); private: // MARK: - Instruction set state. // Memory. std::array memory_; // Registers. uint8_t a_ = 0, x_ = 0, y_ = 0, s_ = 0; uint8_t negative_result_ = 0; uint8_t zero_result_ = 0; uint8_t interrupt_disable_ = 0x04; uint8_t carry_flag_ = 0; uint8_t overflow_result_ = 0; bool index_mode_ = false; bool decimal_mode_ = false; // IO ports. uint8_t port_directions_[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t port_outputs_[4] = {0xff, 0xff, 0xff, 0xff}; // Timers. struct Timer { uint8_t value = 0xff, reload_value = 0xff; }; int timer_divider_ = 0; Timer timers_[3], prescalers_[2]; inline int update_timer(Timer &timer, int count); // Interrupt and timer control. uint8_t interrupt_control_ = 0, timer_control_ = 0; bool interrupt_line_ = false; // Access helpers. inline uint8_t read(uint16_t address); inline void write(uint16_t address, uint8_t value); inline void push(uint8_t); inline uint8_t pull(); inline void set_flags(uint8_t); inline uint8_t flags(); template inline void perform_interrupt(uint16_t vector); inline void set_port_output(int port); void set_interrupt_request(uint8_t ®, uint8_t value, uint16_t vector); // MARK: - Execution time Cycles cycles_; Cycles cycles_since_port_handler_; PortHandler &port_handler_; inline void subtract_duration(int); }; }