mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Performs a bare factoring out of the 6502 bus handler.
This commit is contained in:
parent
5645f90abe
commit
6635876e7e
@ -1343,6 +1343,7 @@
|
||||
4BAF2B4C2004580C00480230 /* DMK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DMK.cpp; sourceTree = "<group>"; };
|
||||
4BAF2B4D2004580C00480230 /* DMK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DMK.hpp; sourceTree = "<group>"; };
|
||||
4BB023FF25212888009F8D90 /* 65816Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = 65816Implementation.hpp; path = ../../Processors/65816/Implementation/65816Implementation.hpp; sourceTree = "<group>"; };
|
||||
4BB0240425229C6E009F8D90 /* 6502Esque.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Esque.hpp; sourceTree = "<group>"; };
|
||||
4BB06B211F316A3F00600C7A /* ForceInline.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ForceInline.hpp; sourceTree = "<group>"; };
|
||||
4BB0A6592044FD3000FB3688 /* SN76489.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SN76489.cpp; sourceTree = "<group>"; };
|
||||
4BB0A65A2044FD3000FB3688 /* SN76489.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SN76489.hpp; sourceTree = "<group>"; };
|
||||
@ -3489,6 +3490,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BFCA1211ECBDCAF00AC40C1 /* AllRAMProcessor.cpp */,
|
||||
4BB0240425229C6E009F8D90 /* 6502Esque.hpp */,
|
||||
4BFCA1221ECBDCAF00AC40C1 /* AllRAMProcessor.hpp */,
|
||||
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
|
||||
4B1414561B58879D00E04248 /* 6502 */,
|
||||
|
@ -15,10 +15,14 @@
|
||||
|
||||
#include "../RegisterSizes.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../6502Esque.hpp"
|
||||
|
||||
namespace CPU {
|
||||
namespace MOS6502 {
|
||||
|
||||
using BusOperation = CPU::MOS6502Esque::BusOperation;
|
||||
using BusHandler = CPU::MOS6502Esque::BusHandler<uint16_t>;
|
||||
|
||||
/*
|
||||
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
|
||||
*/
|
||||
@ -63,61 +67,11 @@ enum Flag: uint8_t {
|
||||
Carry = 0x01
|
||||
};
|
||||
|
||||
/*!
|
||||
Subclasses will be given the task of performing bus operations, allowing them to provide whatever interface they like
|
||||
between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested.
|
||||
|
||||
@c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the
|
||||
isReadOperation macro to make a binary choice between reading and writing.
|
||||
*/
|
||||
enum BusOperation {
|
||||
Read, ReadOpcode, Write, Ready, None
|
||||
};
|
||||
|
||||
/*!
|
||||
Evaluates to `true` if the operation is a read; `false` if it is a write.
|
||||
*/
|
||||
#define isReadOperation(v) (v == CPU::MOS6502::BusOperation::Read || v == CPU::MOS6502::BusOperation::ReadOpcode)
|
||||
|
||||
/*!
|
||||
An opcode that is guaranteed to cause the CPU to jam.
|
||||
*/
|
||||
extern const uint8_t JamOpcode;
|
||||
|
||||
/*!
|
||||
A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus,
|
||||
machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus
|
||||
handler.
|
||||
*/
|
||||
class BusHandler {
|
||||
public:
|
||||
/*!
|
||||
Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502,
|
||||
all bus cycles take one clock cycle so the amoutn of time advanced is implicit.
|
||||
|
||||
@param operation The type of bus cycle: read, read opcode (i.e. read, with sync active),
|
||||
write or ready.
|
||||
@param address The value of the address bus during this bus cycle.
|
||||
@param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is
|
||||
a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during
|
||||
a read cycle will produce undefined behaviour.
|
||||
|
||||
@returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing.
|
||||
On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502
|
||||
during some periods; one way to simulate that is to have the bus handler return a number other than
|
||||
Cycles(1) to describe lengthened bus cycles.
|
||||
*/
|
||||
Cycles perform_bus_operation([[maybe_unused]] CPU::MOS6502::BusOperation operation, [[maybe_unused]] uint16_t address, [[maybe_unused]] uint8_t *value) {
|
||||
return Cycles(1);
|
||||
}
|
||||
|
||||
/*!
|
||||
Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow
|
||||
bus handlers to perform any deferred output work.
|
||||
*/
|
||||
void flush() {}
|
||||
};
|
||||
|
||||
#include "Implementation/6502Storage.hpp"
|
||||
|
||||
/*!
|
||||
|
@ -6,9 +6,6 @@
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MOS6502Storage_h
|
||||
#define MOS6502Storage_h
|
||||
|
||||
/*!
|
||||
A repository for all the internal state of a CPU::MOS6502::Processor; extracted into a separate base
|
||||
class in order to remove it from visibility within the main 6502.hpp.
|
||||
@ -288,5 +285,3 @@ class ProcessorStorage {
|
||||
// Allow state objects to capture and apply state.
|
||||
friend struct State;
|
||||
};
|
||||
|
||||
#endif /* _502Storage_h */
|
||||
|
78
Processors/6502Esque.hpp
Normal file
78
Processors/6502Esque.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
//
|
||||
// 6502Esque.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/09/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef m6502Esque_h
|
||||
#define m6502Esque_h
|
||||
|
||||
/*
|
||||
This file defines how the CPU-controlled part of a bus looks for the 6502 and
|
||||
for other processors with a sufficiently-similar bus.
|
||||
|
||||
I'm not yet a big fan of the name I've used here, and I'm still on the fence
|
||||
about what to do when eventually I get around to the 6800 and/or 6809, which have
|
||||
very similar bus characteristics.
|
||||
|
||||
So: this is _very_ provisional stuff.
|
||||
*/
|
||||
namespace CPU {
|
||||
namespace MOS6502Esque {
|
||||
|
||||
/*!
|
||||
Bus handlers will be given the task of performing bus operations, allowing them to provide whatever interface they like
|
||||
between a 6502 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested.
|
||||
|
||||
@c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the
|
||||
isReadOperation macro to make a binary choice between reading and writing.
|
||||
*/
|
||||
enum BusOperation {
|
||||
Read, ReadOpcode, Write, Ready, None
|
||||
};
|
||||
|
||||
/*!
|
||||
Evaluates to `true` if the operation is a read; `false` if it is a write.
|
||||
*/
|
||||
#define isReadOperation(v) (v == CPU::MOS6502Esque::BusOperation::Read || v == CPU::MOS6502Esque::BusOperation::ReadOpcode)
|
||||
|
||||
/*!
|
||||
A class providing empty implementations of the methods a 6502 uses to access the bus. To wire the 6502 to a bus,
|
||||
machines should subclass BusHandler and then declare a realisation of the 6502 template, suplying their bus
|
||||
handler.
|
||||
*/
|
||||
template <typename AddressType> class BusHandler {
|
||||
public:
|
||||
/*!
|
||||
Announces that the 6502 has performed the cycle defined by operation, address and value. On the 6502,
|
||||
all bus cycles take one clock cycle so the amoutn of time advanced is implicit.
|
||||
|
||||
@param operation The type of bus cycle: read, read opcode (i.e. read, with sync active),
|
||||
write or ready.
|
||||
@param address The value of the address bus during this bus cycle.
|
||||
@param value If this is a cycle that puts a value onto the data bus, *value is that value. If this is
|
||||
a cycle that reads the bus, the bus handler should write a value to *value. Writing to *value during
|
||||
a read cycle will produce undefined behaviour.
|
||||
|
||||
@returns The number of cycles that passed in objective time while this 6502 bus cycle was ongoing.
|
||||
On an archetypal machine this will be Cycles(1) but some architectures may choose not to clock the 6502
|
||||
during some periods; one way to simulate that is to have the bus handler return a number other than
|
||||
Cycles(1) to describe lengthened bus cycles.
|
||||
*/
|
||||
Cycles perform_bus_operation([[maybe_unused]] BusOperation operation, [[maybe_unused]] AddressType address, [[maybe_unused]] uint8_t *value) {
|
||||
return Cycles(1);
|
||||
}
|
||||
|
||||
/*!
|
||||
Announces completion of all the cycles supplied to a .run_for request on the 6502. Intended to allow
|
||||
bus handlers to perform any deferred output work.
|
||||
*/
|
||||
void flush() {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* m6502Esque_h */
|
@ -17,7 +17,9 @@ template <typename BusHandler> void Processor<BusHandler>::run_for(const Cycles
|
||||
// The exception program will determine the appropriate way to respond
|
||||
// based on the pending exception if one exists; otherwise just do a
|
||||
// standard fetch-decode-execute.
|
||||
next_op_ = &instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)];
|
||||
next_op_ = µ_ops_[&instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offset];
|
||||
|
||||
// TODO: reset instruction buffer.
|
||||
continue;
|
||||
|
||||
default:
|
||||
|
@ -6,9 +6,6 @@
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef WDC65816Implementation_h
|
||||
#define WDC65816Implementation_h
|
||||
|
||||
enum MicroOp: uint8_t {
|
||||
/// Fetches a byte from the program counter to the instruction buffer and increments the program counter.
|
||||
CycleFetchIncrementPC,
|
||||
@ -189,14 +186,8 @@ struct ProcessorStorage {
|
||||
};
|
||||
|
||||
void set_power_on_state() {
|
||||
// Set next_op_ to any instance of OperationMoveToNextProgram.
|
||||
for(size_t c = 0; c < micro_ops_.size(); ++c) {
|
||||
if(micro_ops_[c] == OperationMoveToNextProgram) {
|
||||
next_op_ = µ_ops_[c];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set next_op_ to start the exception program.
|
||||
next_op_ = µ_ops_[instructions[size_t(OperationSlot::Exception)].program_offset];
|
||||
pending_exceptions_ = PowerOn;
|
||||
}
|
||||
|
||||
@ -219,8 +210,30 @@ struct ProcessorStorage {
|
||||
static constexpr int NMI = 1 << 3;
|
||||
int pending_exceptions_ = 0;
|
||||
|
||||
/// Defines a four-byte buffer which can be cleared or filled in single-byte increments from least significant byte
|
||||
/// to most significant.
|
||||
struct Buffer {
|
||||
uint32_t value = 0;
|
||||
int size = 0;
|
||||
|
||||
void clear() {
|
||||
value = 0;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
uint8_t *next() {
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
uint8_t *const target = reinterpret_cast<uint8_t *>(&value) + (3 ^ size);
|
||||
#else
|
||||
uint8_t *const target = reinterpret_cast<uint8_t *>(&value) + size;
|
||||
#endif
|
||||
|
||||
++size;
|
||||
return target;
|
||||
}
|
||||
};
|
||||
Buffer instruction_buffer_, data_buffer_;
|
||||
|
||||
std::vector<MicroOp> micro_ops_;
|
||||
MicroOp *next_op_ = nullptr;
|
||||
};
|
||||
|
||||
#endif /* WDC65816Implementation_h */
|
||||
|
Loading…
x
Reference in New Issue
Block a user