started docs on cpu

This commit is contained in:
Lauchmelder 2022-02-28 16:57:23 +01:00
parent 7c424b3137
commit dfef02df47
No known key found for this signature in database
GPG key ID: C2403C69D78F011D
2 changed files with 91 additions and 30 deletions

View file

@ -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);

View file

@ -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