mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-23 17:28:58 +00:00
Eliminated the 6502's reliance on the micro-op scheduler.
This commit is contained in:
parent
3ceef2005b
commit
b304c3a4b9
@ -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 */,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 */
|
|
Loading…
Reference in New Issue
Block a user