started docs on cpu
This commit is contained in:
parent
7c424b3137
commit
dfef02df47
|
@ -53,14 +53,13 @@ uint8_t CPU::Tick()
|
||||||
RESET_DEBUG_STRING();
|
RESET_DEBUG_STRING();
|
||||||
APPEND_DEBUG_STRING(std::setw(4) << pc.Raw << " ");
|
APPEND_DEBUG_STRING(std::setw(4) << pc.Raw << " ");
|
||||||
|
|
||||||
// Implement instructions
|
|
||||||
pastPCs.push_back(pc.Raw);
|
|
||||||
if (pastPCs.size() > 50)
|
|
||||||
pastPCs.pop_front();
|
|
||||||
|
|
||||||
Byte opcode = Read(pc.Raw++);
|
Byte opcode = Read(pc.Raw++);
|
||||||
currentInstruction = &(InstructionTable[opcode]);
|
currentInstruction = &(InstructionTable[opcode]);
|
||||||
|
|
||||||
|
pastPCs.push_back(currentInstruction);
|
||||||
|
if (pastPCs.size() > 50)
|
||||||
|
pastPCs.pop_front();
|
||||||
|
|
||||||
if (currentInstruction->Operation == nullptr || currentInstruction->Mode == nullptr)
|
if (currentInstruction->Operation == nullptr || currentInstruction->Mode == nullptr)
|
||||||
{
|
{
|
||||||
LOG_DEBUG_ERROR("Unknown instruction {0:02X} at ${1:04X}", opcode, pc.Raw);
|
LOG_DEBUG_ERROR("Unknown instruction {0:02X} at ${1:04X}", opcode, pc.Raw);
|
||||||
|
|
112
src/CPU.hpp
112
src/CPU.hpp
|
@ -11,6 +11,9 @@ class Bus;
|
||||||
using Operation = std::function<void(void)>;
|
using Operation = std::function<void(void)>;
|
||||||
using AddressingMode = std::function<void(void)>;
|
using AddressingMode = std::function<void(void)>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Addressing modes of the CPU.
|
||||||
|
*/
|
||||||
enum class Addressing
|
enum class Addressing
|
||||||
{
|
{
|
||||||
ABS,
|
ABS,
|
||||||
|
@ -45,18 +48,25 @@ union StatusFlag
|
||||||
Word Raw;
|
Word Raw;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stores data of an instruction.
|
||||||
|
*/
|
||||||
struct Instruction
|
struct Instruction
|
||||||
{
|
{
|
||||||
Operation Operation = nullptr;
|
Operation Operation = nullptr;
|
||||||
AddressingMode Mode = nullptr;
|
AddressingMode Mode = nullptr;
|
||||||
Addressing AddrType = Addressing::IMP;
|
Addressing AddrType = Addressing::IMP;
|
||||||
uint8_t Size = 0;
|
uint8_t Size = 0;
|
||||||
uint8_t Cycles = 0;
|
uint8_t Cycles = 0;
|
||||||
char Mnemonic[5] = " XXX";
|
char Mnemonic[5] = " XXX";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents the CPU.
|
||||||
|
*/
|
||||||
class CPU
|
class CPU
|
||||||
{
|
{
|
||||||
|
// Give certain debugger components direct access
|
||||||
friend class Debugger;
|
friend class Debugger;
|
||||||
friend class CPUWatcher;
|
friend class CPUWatcher;
|
||||||
friend class Disassembler;
|
friend class Disassembler;
|
||||||
|
@ -64,44 +74,95 @@ class CPU
|
||||||
public:
|
public:
|
||||||
CPU(Bus* bus);
|
CPU(Bus* bus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Do one CPU cycle.
|
||||||
|
*/
|
||||||
uint8_t Tick();
|
uint8_t Tick();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Powerup the CPU.
|
||||||
|
* Internal state is equal to the powerup state
|
||||||
|
*/
|
||||||
void Powerup();
|
void Powerup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the CPU.
|
||||||
|
* Internal state is equal to the reset state
|
||||||
|
*/
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Halt the CPU (stops it from executing anything).
|
||||||
|
*/
|
||||||
inline void Halt() { halted = true; }
|
inline void Halt() { halted = true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request an interrupt.
|
||||||
|
* Can be blocked if the IRQ disable flag is set in the status register
|
||||||
|
*/
|
||||||
void IRQ();
|
void IRQ();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Trigger a non-maskable interrupt.
|
||||||
|
*/
|
||||||
void NMI();
|
void NMI();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Create a lookup table of instructions.
|
||||||
|
*/
|
||||||
void CreateInstructionTable();
|
void CreateInstructionTable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Push a byte to the stack
|
||||||
|
*/
|
||||||
inline void Push(Byte val) { Write(0x0100 | (sp--), val); }
|
inline void Push(Byte val) { Write(0x0100 | (sp--), val); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pop a byte from the stack.
|
||||||
|
*/
|
||||||
inline Byte Pop() { return Read(0x0100 | (++sp)); }
|
inline Byte Pop() { return Read(0x0100 | (++sp)); }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wraps Bus::ReadCPU.
|
||||||
|
*/
|
||||||
Byte Read(Word addr);
|
Byte Read(Word addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wraps Bus::WriteCPU.
|
||||||
|
*/
|
||||||
void Write(Word addr, Byte val);
|
void Write(Word addr, Byte val);
|
||||||
|
|
||||||
private:
|
|
||||||
Address rawAddress;
|
private: // Stuff regarding addressing modes
|
||||||
Address absoluteAddress;
|
Address rawAddress; //< Temporary storage while decoding addresses
|
||||||
Byte relativeAddress;
|
Address absoluteAddress; //< Address the current instruction operates on
|
||||||
Byte fetchedVal;
|
Byte relativeAddress; //< (Relative) address the current instruction operates on
|
||||||
|
Byte fetchedVal; //< The value needed for the current instruction
|
||||||
bool accumulatorAddressing = false;
|
bool accumulatorAddressing = false;
|
||||||
void ABS();
|
|
||||||
void ABX();
|
|
||||||
void ABY();
|
|
||||||
void ACC();
|
|
||||||
void IDX();
|
|
||||||
void IDY();
|
|
||||||
void IMM();
|
|
||||||
void IMP();
|
|
||||||
void IND();
|
|
||||||
void REL();
|
|
||||||
void ZPG();
|
|
||||||
void ZPX();
|
|
||||||
void ZPY();
|
|
||||||
|
|
||||||
private:
|
// The following functions all perform the same steps:
|
||||||
|
// 1. Fetch bytes needed for opcode (if any) and advance PC
|
||||||
|
// 2. Construct the address the instruction operates on (addressing mode specific)
|
||||||
|
// 3. Fetch the value in RAM at that address
|
||||||
|
void ABS(); // Absolute
|
||||||
|
void ABX(); // Absolute X-indexed
|
||||||
|
void ABY(); // Absolute Y-indexed
|
||||||
|
void ACC(); // Accumulator
|
||||||
|
void IDX(); // X-indexed indirect
|
||||||
|
void IDY(); // Indirect Y-indexed
|
||||||
|
void IMM(); // Immediate
|
||||||
|
void IMP(); // Implied
|
||||||
|
void IND(); // Indirect
|
||||||
|
void REL(); // Relative
|
||||||
|
void ZPG(); // Zeropage
|
||||||
|
void ZPX(); // Zeropage X-indexed
|
||||||
|
void ZPY(); // Zeropage Y-indexed
|
||||||
|
|
||||||
|
private: // Stuff regarding instructions
|
||||||
|
// Instructions that the NES can perform
|
||||||
|
// They simply perform the operations needed
|
||||||
void ADC();
|
void ADC();
|
||||||
void AND();
|
void AND();
|
||||||
void ASL();
|
void ASL();
|
||||||
|
@ -167,20 +228,21 @@ private:
|
||||||
void TXS();
|
void TXS();
|
||||||
void TYA();
|
void TYA();
|
||||||
|
|
||||||
private:
|
private: // CPU internals
|
||||||
|
// Registers
|
||||||
Byte acc;
|
Byte acc;
|
||||||
Byte idx, idy;
|
Byte idx, idy;
|
||||||
Address pc;
|
Address pc;
|
||||||
Word sp;
|
Word sp;
|
||||||
|
|
||||||
StatusFlag status;
|
StatusFlag status;
|
||||||
|
|
||||||
Instruction* currentInstruction = nullptr;
|
Instruction* currentInstruction = nullptr;
|
||||||
uint8_t remainingCycles = 0;
|
uint8_t remainingCycles = 0;
|
||||||
uint8_t additionalCycles = 0;
|
uint8_t additionalCycles = 0; //< E.g. when a page boundary was crossed
|
||||||
uint64_t totalCycles = 0;
|
uint64_t totalCycles = 0;
|
||||||
std::deque<Word> pastPCs;
|
std::deque<Instruction*> pastPCs; //< For debugging, saves the past 50 instructions
|
||||||
bool halted = false;
|
bool halted = false;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::stringstream debugString;
|
std::stringstream debugString;
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue