1
0
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:
Thomas Harte 2020-09-28 18:43:53 -04:00
parent 5645f90abe
commit 6635876e7e
6 changed files with 113 additions and 69 deletions

View File

@ -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 */,

View File

@ -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"
/*!

View File

@ -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
View 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 */

View File

@ -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_ = &micro_ops_[&instructions[pending_exceptions_ ? size_t(OperationSlot::Exception) : size_t(OperationSlot::FetchDecodeExecute)].program_offset];
// TODO: reset instruction buffer.
continue;
default:

View File

@ -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_ = &micro_ops_[c];
break;
}
}
// Set next_op_ to start the exception program.
next_op_ = &micro_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 */