1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 18:31:53 +00:00
CLK/Processors/6502/6502.hpp
2018-11-23 22:32:32 -05:00

240 lines
7.2 KiB
C++

//
// 6502.hpp
// CLK
//
// Created by Thomas Harte on 09/07/2015.
// Copyright 2015 Thomas Harte. All rights reserved.
//
#ifndef MOS6502_cpp
#define MOS6502_cpp
#include <cassert>
#include <cstdio>
#include <cstdint>
#include "../RegisterSizes.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
namespace CPU {
namespace MOS6502 {
/*
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
*/
enum Register {
LastOperationAddress,
ProgramCounter,
StackPointer,
Flags,
A,
X,
Y,
S
};
/*
The list of 6502 variants supported by this implementation.
*/
enum Personality {
PNES6502, // the NES's 6502, which is like a 6502 but lacks decimal mode (though it retains the decimal flag)
P6502, // the original [NMOS] 6502, replete with various undocumented instructions
PSynertek65C02, // a 6502 extended with BRA, P[H/L][X/Y], STZ, TRB, TSB and the (zp) addressing mode and a few other additions
PRockwell65C02, // like the Synertek, but with BBR, BBS, RMB and SMB
PWDC65C02, // like the Rockwell, but with STP and WAI
};
#define has_decimal_mode(p) ((p) >= Personality::P6502)
#define is_65c02(p) ((p) >= Personality::PSynertek65C02)
#define has_bbrbbsrmbsmb(p) ((p) >= Personality::PRockwell65C02)
#define has_stpwai(p) ((p) >= Personality::PWDC65C02)
/*
Flags as defined on the 6502; can be used to decode the result of @c get_value_of_register(Flags) or to form a value for
the corresponding set.
*/
enum Flag: uint8_t {
Sign = 0x80,
Overflow = 0x40,
Always = 0x20,
Break = 0x10,
Decimal = 0x08,
Interrupt = 0x04,
Zero = 0x02,
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(CPU::MOS6502::BusOperation operation, uint16_t address, 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"
/*!
A base class from which the 6502 descends; separated for implementation reasons only.
*/
class ProcessorBase: public ProcessorStorage {
public:
ProcessorBase(Personality personality) : ProcessorStorage(personality) {}
/*!
Gets the value of a register.
@see set_value_of_register
@param r The register to set.
@returns The value of the register. 8-bit registers will be returned as unsigned.
*/
uint16_t get_value_of_register(Register r);
/*!
Sets the value of a register.
@see get_value_of_register
@param r The register to set.
@param value The value to set. If the register is only 8 bit, the value will be truncated.
*/
void set_value_of_register(Register r, uint16_t value);
/*!
Sets the current level of the RST line.
@param active @c true if the line is logically active; @c false otherwise.
*/
inline void set_reset_line(bool active);
/*!
Gets whether the 6502 would reset at the next opportunity.
@returns @c true if the line is logically active; @c false otherwise.
*/
inline bool get_is_resetting();
/*!
This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a
reset at the first opportunity. Use @c set_power_on to disable that behaviour.
*/
inline void set_power_on(bool active);
/*!
Sets the current level of the IRQ line.
@param active @c true if the line is logically active; @c false otherwise.
*/
inline void set_irq_line(bool active);
/*!
Sets the current level of the set overflow line.
@param active @c true if the line is logically active; @c false otherwise.
*/
inline void set_overflow_line(bool active);
/*!
Sets the current level of the NMI line.
@param active `true` if the line is logically active; `false` otherwise.
*/
inline void set_nmi_line(bool active);
/*!
Queries whether the 6502 is now 'jammed'; i.e. has entered an invalid state
such that it will not of itself perform any more meaningful processing.
@returns @c true if the 6502 is jammed; @c false otherwise.
*/
bool is_jammed();
};
/*!
@abstact Template providing emulation of a 6502 processor.
@discussion Users should provide as the first template parameter a subclass of CPU::MOS6502::BusHandler; the 6502
will announce its cycle-by-cycle activity via the bus handler, which is responsible for marrying it to a bus. They
can also nominate whether the processor includes support for the ready line. Declining to support the ready line
can produce a minor runtime performance improvement.
*/
template <Personality personality, typename T, bool uses_ready_line> class Processor: public ProcessorBase {
public:
/*!
Constructs an instance of the 6502 that will use @c bus_handler for all bus communications.
*/
Processor(T &bus_handler) : ProcessorBase(personality), bus_handler_(bus_handler) {}
/*!
Runs the 6502 for a supplied number of cycles.
@param cycles The number of cycles to run the 6502 for.
*/
void run_for(const Cycles cycles);
/*!
Sets the current level of the RDY line.
@param active @c true if the line is logically active; @c false otherwise.
*/
void set_ready_line(bool active);
private:
T &bus_handler_;
};
#include "Implementation/6502Implementation.hpp"
}
}
#endif /* MOS6502_cpp */