mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +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>"; };
|
||||
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 */,
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
#include "../MicroOpScheduler.hpp"
|
||||
#include "../RegisterSizes.hpp"
|
||||
|
||||
namespace CPU {
|
||||
@ -67,12 +66,33 @@ enum BusOperation {
|
||||
*/
|
||||
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
|
||||
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 {
|
||||
*/
|
||||
enum MicroOp {
|
||||
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
|
||||
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
|
||||
OperationSetI,
|
||||
@ -110,30 +130,10 @@ enum MicroOp {
|
||||
OperationSetOperandFromFlags,
|
||||
OperationSetFlagsFromA,
|
||||
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, OperationMoveToNextProgram}
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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