1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-12 15:31:09 +00:00

Merge pull request #128 from TomHarte/Scheduling

Eliminates the micro-op scheduler
This commit is contained in:
Thomas Harte 2017-06-03 20:32:39 -04:00 committed by GitHub
commit 0b2a3f18bc
5 changed files with 153 additions and 209 deletions

View File

@ -559,7 +559,6 @@
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DiskController.hpp; sourceTree = "<group>"; }; 4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DiskController.hpp; sourceTree = "<group>"; };
4B77069B1EC904570053B588 /* Z80.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80.cpp; path = Z80/Z80.cpp; sourceTree = "<group>"; }; 4B77069B1EC904570053B588 /* Z80.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80.cpp; path = Z80/Z80.cpp; sourceTree = "<group>"; };
4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Z80.hpp; path = Z80/Z80.hpp; sourceTree = "<group>"; }; 4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Z80.hpp; path = Z80/Z80.hpp; sourceTree = "<group>"; };
4B7706A01EC9398D0053B588 /* MicroOpScheduler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MicroOpScheduler.hpp; sourceTree = "<group>"; };
4B7913CA1DFCD80E00175A82 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = Electron/Video.cpp; sourceTree = "<group>"; }; 4B7913CA1DFCD80E00175A82 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = Electron/Video.cpp; sourceTree = "<group>"; };
4B7913CB1DFCD80E00175A82 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = Electron/Video.hpp; sourceTree = "<group>"; }; 4B7913CB1DFCD80E00175A82 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = Electron/Video.hpp; sourceTree = "<group>"; };
4B79E4411E3AF38600141F11 /* cassette.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cassette.png; sourceTree = "<group>"; }; 4B79E4411E3AF38600141F11 /* cassette.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cassette.png; sourceTree = "<group>"; };
@ -1838,7 +1837,6 @@
children = ( children = (
4B1414561B58879D00E04248 /* 6502 */, 4B1414561B58879D00E04248 /* 6502 */,
4B77069E1EC9045B0053B588 /* Z80 */, 4B77069E1EC9045B0053B588 /* Z80 */,
4B7706A01EC9398D0053B588 /* MicroOpScheduler.hpp */,
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */, 4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */, 4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */,
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */, 4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,

View File

@ -12,7 +12,6 @@
#include <cstdio> #include <cstdio>
#include <cstdint> #include <cstdint>
#include "../MicroOpScheduler.hpp"
#include "../RegisterSizes.hpp" #include "../RegisterSizes.hpp"
namespace CPU { namespace CPU {
@ -67,12 +66,33 @@ enum BusOperation {
*/ */
extern const uint8_t JamOpcode; extern const uint8_t JamOpcode;
/* /*!
@abstact An abstract base class for emulation of a 6502 processor via the curiously recurring template pattern/f-bounded polymorphism.
@discussion Subclasses should implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) in
order to provide the bus on which the 6502 operates and @c flush(), which is called upon completion of a continuous run
of cycles to allow a subclass to bring any on-demand activities up to date.
Additional functionality can be provided by the host machine by providing a jam handler and inserting jam opcodes where appropriate;
that will cause call outs when the program counter reaches those addresses. @c return_from_subroutine can be used to exit from a
jammed state.
*/
template <class T> class Processor {
public:
class JamHandler {
public:
virtual void processor_did_jam(Processor *processor, uint16_t address) = 0;
};
private:
/*
This emulation functions by decomposing instructions into micro programs, consisting of the micro operations This emulation functions by decomposing instructions into micro programs, consisting of the micro operations
as per the enum below. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle as per the enum below. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle
to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle). to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle).
*/ */
enum MicroOp { enum MicroOp {
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH, CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand, CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
OperationSetI, OperationSetI,
@ -110,30 +130,10 @@ enum MicroOp {
OperationSetOperandFromFlags, OperationSetOperandFromFlags,
OperationSetFlagsFromA, OperationSetFlagsFromA,
CycleScheduleJam CycleScheduleJam
};
/*!
@abstact An abstract base class for emulation of a 6502 processor via the curiously recurring template pattern/f-bounded polymorphism.
@discussion Subclasses should implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) in
order to provide the bus on which the 6502 operates and @c flush(), which is called upon completion of a continuous run
of cycles to allow a subclass to bring any on-demand activities up to date.
Additional functionality can be provided by the host machine by providing a jam handler and inserting jam opcodes where appropriate;
that will cause call outs when the program counter reaches those addresses. @c return_from_subroutine can be used to exit from a
jammed state.
*/
template <class T> class Processor: public MicroOpScheduler<MicroOp> {
public:
class JamHandler {
public:
virtual void processor_did_jam(Processor *processor, uint16_t address) = 0;
}; };
const MicroOp *scheduled_program_counter_;
private: #define JAM {CycleFetchOperand, CycleScheduleJam}
#define JAM {CycleFetchOperand, CycleScheduleJam, OperationMoveToNextProgram}
/* /*
Storage for the 6502 registers; F is stored as individual flags. Storage for the 6502 registers; F is stored as individual flags.
@ -422,7 +422,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
#undef Immediate #undef Immediate
#undef Implied #undef Implied
schedule_program(operations[operation]); scheduled_program_counter_ = operations[operation];
} }
bool is_jammed_; bool is_jammed_;
@ -522,7 +522,8 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
interrupt_requests_(InterruptRequestFlags::PowerOn), interrupt_requests_(InterruptRequestFlags::PowerOn),
irq_line_(0), irq_line_(0),
nmi_line_is_enabled_(false), nmi_line_is_enabled_(false),
set_overflow_line_is_enabled_(false) { set_overflow_line_is_enabled_(false),
scheduled_program_counter_(nullptr) {
// only the interrupt flag is defined upon reset but get_flags isn't going to // only the interrupt flag is defined upon reset but get_flags isn't going to
// mask the other flags so we need to do that, at least // mask the other flags so we need to do that, at least
carry_flag_ &= Flag::Carry; carry_flag_ &= Flag::Carry;
@ -550,8 +551,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
static const MicroOp fetch_decode_execute[] = { static const MicroOp fetch_decode_execute[] = {
CycleFetchOperation, CycleFetchOperation,
CycleFetchOperand, CycleFetchOperand,
OperationDecodeOperation, OperationDecodeOperation
OperationMoveToNextProgram
}; };
// These plus program below act to give the compiler permission to update these values // These plus program below act to give the compiler permission to update these values
@ -564,19 +564,18 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
#define checkSchedule(op) \ #define checkSchedule(op) \
if(!scheduled_program_counter_) {\ if(!scheduled_program_counter_) {\
schedule_programs_read_pointer_ = schedule_programs_write_pointer_ = 0; \
if(interrupt_requests_) {\ if(interrupt_requests_) {\
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\ if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\ interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
schedule_program(get_reset_program());\ scheduled_program_counter_ = get_reset_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\ } else if(interrupt_requests_ & InterruptRequestFlags::NMI) {\
interrupt_requests_ &= ~InterruptRequestFlags::NMI;\ interrupt_requests_ &= ~InterruptRequestFlags::NMI;\
schedule_program(get_nmi_program());\ scheduled_program_counter_ = get_nmi_program();\
} else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\ } else if(interrupt_requests_ & InterruptRequestFlags::IRQ) {\
schedule_program(get_irq_program());\ scheduled_program_counter_ = get_irq_program();\
} \ } \
} else {\ } else {\
schedule_program(fetch_decode_execute);\ scheduled_program_counter_ = fetch_decode_execute;\
}\ }\
op;\ op;\
} }
@ -644,7 +643,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
continue; continue;
case OperationMoveToNextProgram: case OperationMoveToNextProgram:
move_to_next_program(); scheduled_program_counter_ = nullptr;
checkSchedule(); checkSchedule();
continue; continue;
@ -704,7 +703,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
case CycleScheduleJam: { case CycleScheduleJam: {
is_jammed_ = true; is_jammed_ = true;
static const MicroOp jam[] = JAM; static const MicroOp jam[] = JAM;
schedule_program(jam); scheduled_program_counter_ = jam;
if(jam_handler_) { if(jam_handler_) {
jam_handler_->processor_did_jam(this, pc_.full - 1); jam_handler_->processor_did_jam(this, pc_.full - 1);
@ -1003,7 +1002,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
#pragma mark - Branching #pragma mark - Branching
#define BRA(condition) pc_.full++; if(condition) schedule_program(doBranch) #define BRA(condition) pc_.full++; if(condition) scheduled_program_counter_ = doBranch
case OperationBPL: BRA(!(negative_result_&0x80)); continue; case OperationBPL: BRA(!(negative_result_&0x80)); continue;
case OperationBMI: BRA(negative_result_&0x80); continue; case OperationBMI: BRA(negative_result_&0x80); continue;
@ -1145,7 +1144,6 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
pc_.full++; pc_.full++;
if(is_jammed_) { if(is_jammed_) {
scheduled_programs_[0] = scheduled_programs_[1] = scheduled_programs_[2] = scheduled_programs_[3] = nullptr;
scheduled_program_counter_ = nullptr; scheduled_program_counter_ = nullptr;
} }
} }

View File

@ -19,6 +19,8 @@ AllRAMProcessor::AllRAMProcessor() : ::CPU::AllRAMProcessor(65536) {
int AllRAMProcessor::perform_bus_operation(MOS6502::BusOperation operation, uint16_t address, uint8_t *value) { int AllRAMProcessor::perform_bus_operation(MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
timestamp_++; timestamp_++;
// if(operation == MOS6502::BusOperation::ReadOpcode) printf("%04x\n", address);
if(isReadOperation(operation)) { if(isReadOperation(operation)) {
*value = memory_[address]; *value = memory_[address];
} else { } else {

View File

@ -1,56 +0,0 @@
//
// MicroOpScheduler.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/05/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef MicroOpScheduler_hpp
#define MicroOpScheduler_hpp
namespace CPU {
template <class T> class MicroOpScheduler {
public:
MicroOpScheduler() :
scheduled_programs_{nullptr, nullptr, nullptr, nullptr},
schedule_programs_write_pointer_(0),
schedule_programs_read_pointer_(0),
scheduled_program_counter_(nullptr) {}
protected:
/*
Up to four programs can be scheduled; each will be carried out in turn. This
storage maintains pointers to the scheduled list of programs.
Programs should be terminated by an OperationMoveToNextProgram, causing this
queue to take that step.
*/
const T *scheduled_programs_[4];
const T *scheduled_program_counter_;
unsigned int schedule_programs_write_pointer_, schedule_programs_read_pointer_;
/*!
Schedules a new program, adding it to the end of the queue. Programs should be
terminated with a OperationMoveToNextProgram. No attempt to copy the program
is made; a non-owning reference is kept.
@param program The program to schedule.
*/
inline void schedule_program(const T *program) {
scheduled_programs_[schedule_programs_write_pointer_] = program;
if(schedule_programs_write_pointer_ == schedule_programs_read_pointer_) scheduled_program_counter_ = program;
schedule_programs_write_pointer_ = (schedule_programs_write_pointer_+1)&3;
}
inline void move_to_next_program() {
scheduled_programs_[schedule_programs_read_pointer_] = nullptr;
schedule_programs_read_pointer_ = (schedule_programs_read_pointer_+1)&3;
scheduled_program_counter_ = scheduled_programs_[schedule_programs_read_pointer_];
}
};
}
#endif /* MicroOpScheduler_hpp */

View File

@ -14,7 +14,6 @@
#include <cstdio> #include <cstdio>
#include <vector> #include <vector>
#include "../MicroOpScheduler.hpp"
#include "../RegisterSizes.hpp" #include "../RegisterSizes.hpp"
namespace CPU { namespace CPU {
@ -79,7 +78,49 @@ struct MachineCycle {
uint8_t *value; uint8_t *value;
}; };
struct MicroOp { /*!
@abstact An abstract base class for emulation of a Z80 processor via the curiously recurring template pattern/f-bounded polymorphism.
@discussion Subclasses should implement @c perform_machine_cycle in
order to provide the bus on which the Z80 operates and @c flush(), which is called upon completion of a continuous run
of cycles to allow a subclass to bring any on-demand activities up to date.
*/
template <class T> class Processor {
private:
uint8_t a_, i_, r_;
RegisterPair bc_, de_, hl_;
RegisterPair afDash_, bcDash_, deDash_, hlDash_;
RegisterPair ix_, iy_, pc_, sp_;
bool iff1_, iff2_;
int interrupt_mode_;
uint16_t pc_increment_;
uint8_t sign_result_; // the sign flag is set if the value in sign_result_ is negative
uint8_t zero_result_; // the zero flag is set if the value in zero_result_ is zero
uint8_t half_carry_result_; // the half-carry flag is set if bit 4 of half_carry_result_ is set
uint8_t bit53_result_; // the bit 3 and 5 flags are set if the corresponding bits of bit53_result_ are set
uint8_t parity_overflow_result_; // the parity/overflow flag is set if the corresponding bit of parity_overflow_result_ is set
uint8_t subtract_flag_; // contains a copy of the subtract flag in isolation
uint8_t carry_result_; // the carry flag is set if bit 0 of carry_result_ is set
uint8_t halt_mask_;
int number_of_cycles_;
enum Interrupt: uint8_t {
IRQ = 0x01,
NMI = 0x02,
Reset = 0x04,
PowerOn = 0x08
};
uint8_t request_status_;
uint8_t last_request_status_;
bool irq_line_;
bool bus_request_line_;
uint8_t operation_;
RegisterPair temp16_;
uint8_t temp8_;
struct MicroOp {
enum Type { enum Type {
BusOperation, BusOperation,
DecodeOperation, DecodeOperation,
@ -163,49 +204,9 @@ struct MicroOp {
void *source; void *source;
void *destination; void *destination;
MachineCycle machine_cycle; MachineCycle machine_cycle;
};
/*!
@abstact An abstract base class for emulation of a Z80 processor via the curiously recurring template pattern/f-bounded polymorphism.
@discussion Subclasses should implement @c perform_machine_cycle in
order to provide the bus on which the Z80 operates and @c flush(), which is called upon completion of a continuous run
of cycles to allow a subclass to bring any on-demand activities up to date.
*/
template <class T> class Processor: public MicroOpScheduler<MicroOp> {
private:
uint8_t a_, i_, r_;
RegisterPair bc_, de_, hl_;
RegisterPair afDash_, bcDash_, deDash_, hlDash_;
RegisterPair ix_, iy_, pc_, sp_;
bool iff1_, iff2_;
int interrupt_mode_;
uint16_t pc_increment_;
uint8_t sign_result_; // the sign flag is set if the value in sign_result_ is negative
uint8_t zero_result_; // the zero flag is set if the value in zero_result_ is zero
uint8_t half_carry_result_; // the half-carry flag is set if bit 4 of half_carry_result_ is set
uint8_t bit53_result_; // the bit 3 and 5 flags are set if the corresponding bits of bit53_result_ are set
uint8_t parity_overflow_result_; // the parity/overflow flag is set if the corresponding bit of parity_overflow_result_ is set
uint8_t subtract_flag_; // contains a copy of the subtract flag in isolation
uint8_t carry_result_; // the carry flag is set if bit 0 of carry_result_ is set
uint8_t halt_mask_;
int number_of_cycles_;
enum Interrupt: uint8_t {
IRQ = 0x01,
NMI = 0x02,
Reset = 0x04,
PowerOn = 0x08
}; };
uint8_t request_status_; const MicroOp *scheduled_program_counter_;
uint8_t last_request_status_;
bool irq_line_;
bool bus_request_line_;
uint8_t operation_;
RegisterPair temp16_;
uint8_t temp8_;
struct InstructionPage { struct InstructionPage {
std::vector<MicroOp *> instructions; std::vector<MicroOp *> instructions;
@ -665,14 +666,15 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
} }
public: public:
Processor() : MicroOpScheduler(), Processor() :
halt_mask_(0xff), halt_mask_(0xff),
number_of_cycles_(0), number_of_cycles_(0),
request_status_(Interrupt::PowerOn), request_status_(Interrupt::PowerOn),
last_request_status_(Interrupt::PowerOn), last_request_status_(Interrupt::PowerOn),
irq_line_(false), irq_line_(false),
bus_request_line_(false), bus_request_line_(false),
pc_increment_(1) { pc_increment_(1),
scheduled_program_counter_(nullptr) {
set_flags(0xff); set_flags(0xff);
assemble_base_page(base_page_, hl_, false, cb_page_); assemble_base_page(base_page_, hl_, false, cb_page_);