1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-24 12:30:17 +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>"; };
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>"; };
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>"; };
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>"; };
@ -1838,7 +1837,6 @@
children = (
4B1414561B58879D00E04248 /* 6502 */,
4B77069E1EC9045B0053B588 /* Z80 */,
4B7706A01EC9398D0053B588 /* MicroOpScheduler.hpp */,
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */,
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,

View File

@ -12,7 +12,6 @@
#include <cstdio>
#include <cstdint>
#include "../MicroOpScheduler.hpp"
#include "../RegisterSizes.hpp"
namespace CPU {
@ -67,51 +66,6 @@ enum BusOperation {
*/
extern const uint8_t JamOpcode;
/*
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
to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle).
*/
enum MicroOp {
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
OperationSetI,
OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector,
CycleReadVectorLow, CycleReadVectorHigh,
CycleReadFromS, CycleReadFromPC,
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
CycleNoWritePush,
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress, CycleLoadAddressAbsolute,
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead,
OperationMoveToNextProgram, OperationIncrementPC,
CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA,
CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand,
OperationIncrementOperand, OperationORA, OperationAND, OperationEOR,
OperationINS, OperationADC, OperationSBC, OperationLDA,
OperationLDX, OperationLDY, OperationLAX, OperationSTA,
OperationSTX, OperationSTY, OperationSAX, OperationSHA,
OperationSHX, OperationSHY, OperationSHS, OperationCMP,
OperationCPX, OperationCPY, OperationBIT, OperationASL,
OperationASO, OperationROL, OperationRLA, OperationLSR,
OperationLSE, OperationASR, OperationROR, OperationRRA,
OperationCLC, OperationCLI, OperationCLV, OperationCLD,
OperationSEC, OperationSEI, OperationSED, OperationINC,
OperationDEC, OperationINX, OperationDEX, OperationINY,
OperationDEY, OperationBPL, OperationBMI, OperationBVC,
OperationBVS, OperationBCC, OperationBCS, OperationBNE,
OperationBEQ, OperationTXA, OperationTYA, OperationTXS,
OperationTAY, OperationTAX, OperationTSX, OperationARR,
OperationSBX, OperationLXA, OperationANE, OperationANC,
OperationLAS, CycleAddSignedOperandToPC, OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
OperationSetOperandFromFlags,
OperationSetFlagsFromA,
CycleScheduleJam
};
/*!
@abstact An abstract base class for emulation of a 6502 processor via the curiously recurring template pattern/f-bounded polymorphism.
@ -123,7 +77,7 @@ enum MicroOp {
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> {
template <class T> class Processor {
public:
class JamHandler {
@ -133,7 +87,53 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
private:
#define JAM {CycleFetchOperand, CycleScheduleJam, OperationMoveToNextProgram}
/*
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
to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle).
*/
enum MicroOp {
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
OperationSetI,
OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector,
CycleReadVectorLow, CycleReadVectorHigh,
CycleReadFromS, CycleReadFromPC,
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
CycleNoWritePush,
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress, CycleLoadAddressAbsolute,
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead,
OperationMoveToNextProgram, OperationIncrementPC,
CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA,
CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand,
OperationIncrementOperand, OperationORA, OperationAND, OperationEOR,
OperationINS, OperationADC, OperationSBC, OperationLDA,
OperationLDX, OperationLDY, OperationLAX, OperationSTA,
OperationSTX, OperationSTY, OperationSAX, OperationSHA,
OperationSHX, OperationSHY, OperationSHS, OperationCMP,
OperationCPX, OperationCPY, OperationBIT, OperationASL,
OperationASO, OperationROL, OperationRLA, OperationLSR,
OperationLSE, OperationASR, OperationROR, OperationRRA,
OperationCLC, OperationCLI, OperationCLV, OperationCLD,
OperationSEC, OperationSEI, OperationSED, OperationINC,
OperationDEC, OperationINX, OperationDEX, OperationINY,
OperationDEY, OperationBPL, OperationBMI, OperationBVC,
OperationBVS, OperationBCC, OperationBCS, OperationBNE,
OperationBEQ, OperationTXA, OperationTYA, OperationTXS,
OperationTAY, OperationTAX, OperationTSX, OperationARR,
OperationSBX, OperationLXA, OperationANE, OperationANC,
OperationLAS, CycleAddSignedOperandToPC, OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
OperationSetOperandFromFlags,
OperationSetFlagsFromA,
CycleScheduleJam
};
const MicroOp *scheduled_program_counter_;
#define JAM {CycleFetchOperand, CycleScheduleJam}
/*
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 Implied
schedule_program(operations[operation]);
scheduled_program_counter_ = operations[operation];
}
bool is_jammed_;
@ -522,7 +522,8 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
interrupt_requests_(InterruptRequestFlags::PowerOn),
irq_line_(0),
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
// mask the other flags so we need to do that, at least
carry_flag_ &= Flag::Carry;
@ -550,8 +551,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
static const MicroOp fetch_decode_execute[] = {
CycleFetchOperation,
CycleFetchOperand,
OperationDecodeOperation,
OperationMoveToNextProgram
OperationDecodeOperation
};
// 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) \
if(!scheduled_program_counter_) {\
schedule_programs_read_pointer_ = schedule_programs_write_pointer_ = 0; \
if(interrupt_requests_) {\
if(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)) {\
interrupt_requests_ &= ~InterruptRequestFlags::PowerOn;\
schedule_program(get_reset_program());\
scheduled_program_counter_ = get_reset_program();\
} else if(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) {\
schedule_program(get_irq_program());\
scheduled_program_counter_ = get_irq_program();\
} \
} else {\
schedule_program(fetch_decode_execute);\
scheduled_program_counter_ = fetch_decode_execute;\
}\
op;\
}
@ -644,7 +643,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
continue;
case OperationMoveToNextProgram:
move_to_next_program();
scheduled_program_counter_ = nullptr;
checkSchedule();
continue;
@ -704,7 +703,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
case CycleScheduleJam: {
is_jammed_ = true;
static const MicroOp jam[] = JAM;
schedule_program(jam);
scheduled_program_counter_ = jam;
if(jam_handler_) {
jam_handler_->processor_did_jam(this, pc_.full - 1);
@ -1003,7 +1002,7 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
#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 OperationBMI: BRA(negative_result_&0x80); continue;
@ -1145,7 +1144,6 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
pc_.full++;
if(is_jammed_) {
scheduled_programs_[0] = scheduled_programs_[1] = scheduled_programs_[2] = scheduled_programs_[3] = 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) {
timestamp_++;
// if(operation == MOS6502::BusOperation::ReadOpcode) printf("%04x\n", address);
if(isReadOperation(operation)) {
*value = memory_[address];
} 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 <vector>
#include "../MicroOpScheduler.hpp"
#include "../RegisterSizes.hpp"
namespace CPU {
@ -79,92 +78,6 @@ struct MachineCycle {
uint8_t *value;
};
struct MicroOp {
enum Type {
BusOperation,
DecodeOperation,
DecodeOperationNoRChange,
MoveToNextProgram,
Increment8,
Increment16,
Decrement8,
Decrement16,
Move8,
Move16,
IncrementPC,
AssembleAF,
DisassembleAF,
And,
Or,
Xor,
TestNZ,
TestZ,
TestNC,
TestC,
TestPO,
TestPE,
TestP,
TestM,
ADD16, ADC16, SBC16,
CP8, SUB8, SBC8, ADD8, ADC8,
NEG,
ExDEHL, ExAFAFDash, EXX,
EI, DI, IM,
LDI, LDIR, LDD, LDDR,
CPI, CPIR, CPD, CPDR,
INI, INIR, IND, INDR,
OUTI, OUTD, OUT_R,
RLA, RLCA, RRA, RRCA,
RLC, RRC, RL, RR,
SLA, SRA, SLL, SRL,
RLD, RRD,
SetInstructionPage,
CalculateIndexAddress,
BeginNMI,
BeginIRQ,
BeginIRQMode0,
RETN,
JumpTo66,
HALT,
DJNZ,
DAA,
CPL,
SCF,
CCF,
RES,
BIT,
SET,
CalculateRSTDestination,
SetAFlags,
SetInFlags,
SetZero,
IndexedPlaceHolder,
Reset
};
Type type;
void *source;
void *destination;
MachineCycle machine_cycle;
};
/*!
@abstact An abstract base class for emulation of a Z80 processor via the curiously recurring template pattern/f-bounded polymorphism.
@ -172,7 +85,7 @@ struct MicroOp {
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> {
template <class T> class Processor {
private:
uint8_t a_, i_, r_;
RegisterPair bc_, de_, hl_;
@ -207,6 +120,94 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
RegisterPair temp16_;
uint8_t temp8_;
struct MicroOp {
enum Type {
BusOperation,
DecodeOperation,
DecodeOperationNoRChange,
MoveToNextProgram,
Increment8,
Increment16,
Decrement8,
Decrement16,
Move8,
Move16,
IncrementPC,
AssembleAF,
DisassembleAF,
And,
Or,
Xor,
TestNZ,
TestZ,
TestNC,
TestC,
TestPO,
TestPE,
TestP,
TestM,
ADD16, ADC16, SBC16,
CP8, SUB8, SBC8, ADD8, ADC8,
NEG,
ExDEHL, ExAFAFDash, EXX,
EI, DI, IM,
LDI, LDIR, LDD, LDDR,
CPI, CPIR, CPD, CPDR,
INI, INIR, IND, INDR,
OUTI, OUTD, OUT_R,
RLA, RLCA, RRA, RRCA,
RLC, RRC, RL, RR,
SLA, SRA, SLL, SRL,
RLD, RRD,
SetInstructionPage,
CalculateIndexAddress,
BeginNMI,
BeginIRQ,
BeginIRQMode0,
RETN,
JumpTo66,
HALT,
DJNZ,
DAA,
CPL,
SCF,
CCF,
RES,
BIT,
SET,
CalculateRSTDestination,
SetAFlags,
SetInFlags,
SetZero,
IndexedPlaceHolder,
Reset
};
Type type;
void *source;
void *destination;
MachineCycle machine_cycle;
};
const MicroOp *scheduled_program_counter_;
struct InstructionPage {
std::vector<MicroOp *> instructions;
std::vector<MicroOp> all_operations;
@ -665,14 +666,15 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
}
public:
Processor() : MicroOpScheduler(),
Processor() :
halt_mask_(0xff),
number_of_cycles_(0),
request_status_(Interrupt::PowerOn),
last_request_status_(Interrupt::PowerOn),
irq_line_(false),
bus_request_line_(false),
pc_increment_(1) {
pc_increment_(1),
scheduled_program_counter_(nullptr) {
set_flags(0xff);
assemble_base_page(base_page_, hl_, false, cb_page_);