2021-01-17 01:51:02 +00:00
|
|
|
//
|
|
|
|
// Executor.h
|
|
|
|
// Clock Signal
|
|
|
|
//
|
2021-01-17 03:09:19 +00:00
|
|
|
// Created by Thomas Harte on 16/01/21.
|
2021-01-17 01:51:02 +00:00
|
|
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef Executor_h
|
|
|
|
#define Executor_h
|
|
|
|
|
|
|
|
#include "Instruction.hpp"
|
2021-01-17 02:45:44 +00:00
|
|
|
#include "Parser.hpp"
|
2021-01-17 03:06:16 +00:00
|
|
|
#include "../CachingExecutor.hpp"
|
2021-01-22 03:36:44 +00:00
|
|
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
2021-01-17 01:51:02 +00:00
|
|
|
|
2021-01-25 03:30:42 +00:00
|
|
|
#include <array>
|
2021-01-18 21:59:49 +00:00
|
|
|
#include <cstdint>
|
|
|
|
#include <vector>
|
|
|
|
|
2023-05-10 21:02:18 +00:00
|
|
|
namespace InstructionSet::M50740 {
|
2021-01-17 01:51:02 +00:00
|
|
|
|
|
|
|
class Executor;
|
2021-01-18 22:53:14 +00:00
|
|
|
using CachingExecutor = CachingExecutor<Executor, 0x1fff, 255, Instruction, false>;
|
2021-01-17 01:51:02 +00:00
|
|
|
|
2021-01-24 23:07:05 +00:00
|
|
|
struct PortHandler {
|
2021-01-25 22:43:22 +00:00
|
|
|
virtual void run_ports_for(Cycles) = 0;
|
2021-01-24 23:07:05 +00:00
|
|
|
virtual void set_port_output(int port, uint8_t value) = 0;
|
|
|
|
virtual uint8_t get_port_input(int port) = 0;
|
|
|
|
};
|
|
|
|
|
2021-01-21 01:16:55 +00:00
|
|
|
/*!
|
|
|
|
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.
|
|
|
|
*/
|
2021-01-18 21:45:52 +00:00
|
|
|
class Executor: public CachingExecutor {
|
2021-01-17 01:51:02 +00:00
|
|
|
public:
|
2021-01-24 23:07:05 +00:00
|
|
|
Executor(PortHandler &);
|
2021-01-18 21:59:49 +00:00
|
|
|
void set_rom(const std::vector<uint8_t> &rom);
|
2021-02-15 03:20:58 +00:00
|
|
|
|
2021-01-18 21:59:49 +00:00
|
|
|
void reset();
|
2021-02-15 03:20:58 +00:00
|
|
|
void set_interrupt_line(bool);
|
2021-01-17 01:51:02 +00:00
|
|
|
|
2021-02-20 02:46:18 +00:00
|
|
|
uint8_t get_output_mask(int port);
|
|
|
|
|
2021-01-22 03:36:44 +00:00
|
|
|
/*!
|
|
|
|
Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles.
|
|
|
|
*/
|
|
|
|
void run_for(Cycles cycles);
|
|
|
|
|
2021-01-17 03:06:16 +00:00
|
|
|
private:
|
2021-01-18 01:03:36 +00:00
|
|
|
// MARK: - CachingExecutor-facing interface.
|
|
|
|
|
2021-01-18 21:45:52 +00:00
|
|
|
friend CachingExecutor;
|
2021-01-17 01:51:02 +00:00
|
|
|
|
|
|
|
/*!
|
2021-01-18 01:03:36 +00:00
|
|
|
Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial.
|
2021-01-17 01:51:02 +00:00
|
|
|
*/
|
2021-01-18 01:03:36 +00:00
|
|
|
inline PerformerIndex action_for(Instruction instruction) {
|
|
|
|
// This is a super-simple processor, so the opcode can be used directly to index the performers.
|
|
|
|
return instruction.opcode;
|
|
|
|
}
|
2021-01-17 01:51:02 +00:00
|
|
|
|
2021-01-18 21:45:52 +00:00
|
|
|
/*!
|
|
|
|
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;
|
2021-01-25 03:30:42 +00:00
|
|
|
parser.parse(*this, &memory_[0], start & 0x1fff, closing_bound);
|
2021-01-18 21:45:52 +00:00
|
|
|
}
|
|
|
|
|
2021-01-18 01:03:36 +00:00
|
|
|
private:
|
|
|
|
// MARK: - Internal framework for generator performers.
|
2021-01-17 02:45:44 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Provides dynamic lookup of @c perform(Executor*).
|
|
|
|
*/
|
|
|
|
class PerformerLookup {
|
|
|
|
public:
|
|
|
|
PerformerLookup() {
|
2021-01-18 22:53:14 +00:00
|
|
|
fill<int(MinOperation)>(performers_);
|
2021-01-17 02:45:44 +00:00
|
|
|
}
|
|
|
|
|
2021-01-18 01:53:11 +00:00
|
|
|
Performer performer(Operation operation, AddressingMode addressing_mode) {
|
2021-01-18 22:53:14 +00:00
|
|
|
const auto index =
|
|
|
|
(int(operation) - MinOperation) * (1 + MaxAddressingMode - MinAddressingMode) +
|
|
|
|
(int(addressing_mode) - MinAddressingMode);
|
|
|
|
return performers_[index];
|
2021-01-17 02:45:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-01-18 22:53:14 +00:00
|
|
|
Performer performers_[(1 + MaxAddressingMode - MinAddressingMode) * (1 + MaxOperation - MinOperation)];
|
2021-01-17 02:45:44 +00:00
|
|
|
|
2021-01-18 01:53:11 +00:00
|
|
|
template<int operation, int addressing_mode> void fill_operation(Performer *target) {
|
2021-01-17 02:45:44 +00:00
|
|
|
*target = &Executor::perform<Operation(operation), AddressingMode(addressing_mode)>;
|
2021-01-18 22:53:14 +00:00
|
|
|
|
|
|
|
if constexpr (addressing_mode+1 <= MaxAddressingMode) {
|
2021-01-17 02:50:48 +00:00
|
|
|
fill_operation<operation, addressing_mode+1>(target + 1);
|
2021-01-17 02:45:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-18 22:53:14 +00:00
|
|
|
template<int operation> void fill(Performer *target) {
|
|
|
|
fill_operation<operation, int(MinAddressingMode)>(target);
|
|
|
|
target += 1 + MaxAddressingMode - MinAddressingMode;
|
|
|
|
|
|
|
|
if constexpr (operation+1 <= MaxOperation) {
|
|
|
|
fill<operation+1>(target);
|
2021-01-17 02:45:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
inline static PerformerLookup performer_lookup_;
|
2021-01-18 01:03:36 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Performs @c operation using @c operand as the value fetched from memory, if any.
|
|
|
|
*/
|
2021-01-18 01:53:11 +00:00
|
|
|
template <Operation operation> void perform(uint8_t *operand);
|
2021-01-18 01:03:36 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
Performs @c operation in @c addressing_mode.
|
|
|
|
*/
|
2021-01-18 01:53:11 +00:00
|
|
|
template <Operation operation, AddressingMode addressing_mode> void perform();
|
2021-01-18 01:03:36 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
// MARK: - Instruction set state.
|
2021-01-18 01:53:11 +00:00
|
|
|
|
|
|
|
// Memory.
|
2021-01-25 03:30:42 +00:00
|
|
|
std::array<uint8_t, 0x2000> memory_;
|
2021-01-18 01:53:11 +00:00
|
|
|
|
|
|
|
// Registers.
|
2021-02-19 03:48:53 +00:00
|
|
|
uint8_t a_ = 0, x_ = 0, y_ = 0, s_ = 0;
|
2021-01-20 02:51:01 +00:00
|
|
|
|
|
|
|
uint8_t negative_result_ = 0;
|
|
|
|
uint8_t zero_result_ = 0;
|
2021-02-08 02:53:57 +00:00
|
|
|
uint8_t interrupt_disable_ = 0x04;
|
2021-01-21 01:16:55 +00:00
|
|
|
uint8_t carry_flag_ = 0;
|
2021-02-19 03:48:53 +00:00
|
|
|
uint8_t overflow_result_ = 0;
|
2021-01-20 02:54:15 +00:00
|
|
|
bool index_mode_ = false;
|
|
|
|
bool decimal_mode_ = false;
|
2021-01-20 23:15:24 +00:00
|
|
|
|
2021-02-07 02:02:44 +00:00
|
|
|
// IO ports.
|
2021-02-08 02:53:57 +00:00
|
|
|
uint8_t port_directions_[4] = {0x00, 0x00, 0x00, 0x00};
|
2021-01-29 02:06:11 +00:00
|
|
|
uint8_t port_outputs_[4] = {0xff, 0xff, 0xff, 0xff};
|
|
|
|
|
2021-02-07 02:02:44 +00:00
|
|
|
// Timers.
|
|
|
|
struct Timer {
|
2021-02-08 02:53:57 +00:00
|
|
|
uint8_t value = 0xff, reload_value = 0xff;
|
2021-02-07 02:02:44 +00:00
|
|
|
};
|
|
|
|
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;
|
2021-02-15 03:20:58 +00:00
|
|
|
bool interrupt_line_ = false;
|
2021-02-07 02:02:44 +00:00
|
|
|
|
|
|
|
// Access helpers.
|
2021-01-20 23:15:24 +00:00
|
|
|
inline uint8_t read(uint16_t address);
|
|
|
|
inline void write(uint16_t address, uint8_t value);
|
|
|
|
inline void push(uint8_t value);
|
2021-01-20 23:21:44 +00:00
|
|
|
inline uint8_t pull();
|
2021-01-21 01:27:09 +00:00
|
|
|
inline void set_flags(uint8_t);
|
|
|
|
inline uint8_t flags();
|
2021-02-15 03:20:58 +00:00
|
|
|
template<bool is_brk> inline void perform_interrupt(uint16_t vector);
|
2021-02-08 23:14:08 +00:00
|
|
|
inline void set_port_output(int port);
|
2021-01-24 03:55:12 +00:00
|
|
|
|
2021-01-25 22:43:22 +00:00
|
|
|
// MARK: - Execution time
|
|
|
|
|
2021-01-24 03:55:12 +00:00
|
|
|
Cycles cycles_;
|
2021-01-25 22:43:22 +00:00
|
|
|
Cycles cycles_since_port_handler_;
|
2021-01-24 23:07:05 +00:00
|
|
|
PortHandler &port_handler_;
|
2021-01-25 22:43:22 +00:00
|
|
|
inline void subtract_duration(int duration);
|
2021-01-17 01:51:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* Executor_h */
|