1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 18:30:21 +00:00

Eliminated the 6502's reliance on the micro-op scheduler.

This commit is contained in:
Thomas Harte 2017-06-03 20:30:07 -04:00
parent 3ceef2005b
commit b304c3a4b9
4 changed files with 61 additions and 119 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,51 +66,6 @@ enum BusOperation {
*/ */
extern const uint8_t JamOpcode; 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. @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 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. jammed state.
*/ */
template <class T> class Processor: public MicroOpScheduler<MicroOp> { template <class T> class Processor {
public: public:
class JamHandler { class JamHandler {
@ -133,7 +87,53 @@ template <class T> class Processor: public MicroOpScheduler<MicroOp> {
private: 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. 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 */