247 lines
5.1 KiB
C++
247 lines
5.1 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <fstream>
|
|
|
|
|
|
const unsigned int INSTRUCTION_COUNT = 256;
|
|
const unsigned int RAM_SIZE = 2048;
|
|
const unsigned int SRAM_SIZE = 8192;
|
|
const unsigned int IRQ_VECTOR_MSB = 0xFFFF;
|
|
const unsigned int IRQ_VECTOR_LSB = 0xFFFE;
|
|
|
|
class NES;
|
|
|
|
class Status
|
|
{
|
|
friend class NES;
|
|
|
|
public:
|
|
void SetByte(uint8_t byte)
|
|
{
|
|
negative = (byte & 0x80u) >> 7u;
|
|
overflow = (byte & 0x40u) >> 6u;
|
|
b1 = (byte & 0x20u) >> 5u;
|
|
b0 = (byte & 0x10u) >> 4u;
|
|
decimal = (byte & 0x08u) >> 3u;
|
|
interruptDisable = (byte & 0x04u) >> 2u;
|
|
zero = (byte & 0x02u) >> 1u;
|
|
carry = (byte & 0x01u);
|
|
}
|
|
|
|
uint8_t GetByte()
|
|
{
|
|
return negative << 7u | overflow << 6u | b1 << 5u | b0 << 4u
|
|
| decimal << 3u | interruptDisable << 2u | zero << 1u | carry;
|
|
}
|
|
|
|
uint8_t carry;
|
|
uint8_t zero;
|
|
uint8_t interruptDisable;
|
|
uint8_t decimal;
|
|
uint8_t b0;
|
|
uint8_t b1;
|
|
uint8_t overflow;
|
|
uint8_t negative;
|
|
};
|
|
|
|
|
|
class CPU;
|
|
class Instruction
|
|
{
|
|
public:
|
|
char const* name;
|
|
uint8_t cycles;
|
|
|
|
using CpuFunc = void (CPU::*)();
|
|
CpuFunc fetch;
|
|
CpuFunc execute;
|
|
};
|
|
|
|
|
|
class CPU
|
|
{
|
|
public:
|
|
CPU();
|
|
uint64_t Cycle();
|
|
void Log();
|
|
void Reset();
|
|
void NMI();
|
|
|
|
void WriteMemory(uint16_t address, uint8_t value);
|
|
uint8_t ReadMemory(uint16_t address);
|
|
|
|
friend class NES;
|
|
|
|
void StackPush(uint8_t val);
|
|
uint8_t StackPop();
|
|
|
|
// --------------- HELPERS --------------- //
|
|
static bool TestBits(uint8_t value, uint8_t bits);
|
|
static bool IsNegative(uint8_t value);
|
|
static bool IsPageBoundaryCrossed(uint16_t before, uint16_t after);
|
|
static uint16_t ComposeAddress(uint8_t msb, uint8_t lsb);
|
|
|
|
|
|
// --------------- ADDRESS MODES --------------- //
|
|
// Operand is implied by the instruction
|
|
void Implicit();
|
|
|
|
// Operand is the byte/STA
|
|
void Immediate();
|
|
|
|
// Operand contains a zero-page address (0x00 to 0xFF)
|
|
// Byte is retrieved from that address in memory
|
|
void ZeroPage();
|
|
|
|
// Operand is added to X to get effective address, with wrap around back to 0x00
|
|
void ZeroPageX();
|
|
|
|
// Operand is added to X to get effective address, with wrap around back to 0x00
|
|
void ZeroPageY();
|
|
|
|
// Operand is the memory location
|
|
void Absolute();
|
|
|
|
// Operand contains an absolute address which can be added to X to get a new address
|
|
// If the address after adding X crosses a page-boundary, an additional cycle occurs
|
|
void AbsoluteX();
|
|
|
|
// Operand contains an absolute address which can be added to Y to get a new address
|
|
// If the address after adding Y crosses a page-boundary, an additional cycle occurs
|
|
void AbsoluteY();
|
|
|
|
// Operand contains address of LSB of effective address - MSB is in address + 1
|
|
void Indirect();
|
|
|
|
// Operand contains a zero-page address (0x00 to 0xFF)
|
|
// Value in X is added to operand to retrieve two bytes of address in memory (addition will wrap around back to 0x00)
|
|
// Effective address is used to retrieve the byte
|
|
void IndirectX();
|
|
|
|
// Operand contains a zero-page address (0x00 to 0xFF)
|
|
// Two bytes of address are retrieved from that address in memory
|
|
// Y is added to that address and the new address is used to retrieve the byte
|
|
// If the addition of Y causes a page-boundary crossing, an additional cycle occurs
|
|
void IndirectY();
|
|
|
|
// Operand is an offset from current instruction used for branching
|
|
void Relative();
|
|
|
|
|
|
// --------------- LOGICAL --------------- //
|
|
void AND();
|
|
void EOR();
|
|
void ORA();
|
|
void BIT();
|
|
|
|
// --------------- ARITHMETIC --------------- //
|
|
void ADC();
|
|
void SBC();
|
|
void CMP();
|
|
void CPX();
|
|
void CPY();
|
|
|
|
// --------------- LOAD/STORE --------------- //
|
|
void STA();
|
|
void STX();
|
|
void STY();
|
|
void LDA();
|
|
void LDX();
|
|
void LDY();
|
|
|
|
// --------------- TRANSFERS --------------- //
|
|
void TXA();
|
|
void TYA();
|
|
void TXS();
|
|
void TAX();
|
|
void TAY();
|
|
void TSX();
|
|
|
|
// --------------- STACK --------------- //
|
|
void PHP();
|
|
void PLP();
|
|
void PHA();
|
|
void PLA();
|
|
|
|
// --------------- INCREMENTS & DECREMENTS --------------- //
|
|
void INC();
|
|
void INX();
|
|
void INY();
|
|
void DEC();
|
|
void DEX();
|
|
void DEY();
|
|
|
|
// --------------- SHIFTS --------------- //
|
|
void ASL();
|
|
void LSR();
|
|
void ROL();
|
|
void ROR();
|
|
|
|
// --------------- JUMPS & CALLS --------------- //
|
|
void JMP();
|
|
void JSR();
|
|
void RTS();
|
|
|
|
// --------------- STATUS FLAG CHANGES --------------- //
|
|
void CLC();
|
|
void SEC();
|
|
void CLI();
|
|
void SEI();
|
|
void CLV();
|
|
void CLD();
|
|
void SED();
|
|
|
|
// --------------- BRANCHES --------------- //
|
|
void BPL();
|
|
void BMI();
|
|
void BVC();
|
|
void BVS();
|
|
void BCC();
|
|
void BCS();
|
|
void BNE();
|
|
void BEQ();
|
|
|
|
// --------------- SYSTEM --------------- //
|
|
void BRK();
|
|
void NOP();
|
|
void RTI();
|
|
|
|
// --------------- UNOFFICIAL --------------- //
|
|
void LAX();
|
|
void SAX();
|
|
void DCP();
|
|
void ISC();
|
|
void SLO();
|
|
void RLA();
|
|
void SRE();
|
|
void RRA();
|
|
void ANC();
|
|
void ALR();
|
|
void ARR();
|
|
void AXS();
|
|
|
|
// Machine features
|
|
uint8_t ram[RAM_SIZE]{};
|
|
uint8_t sram[SRAM_SIZE]{};
|
|
uint16_t pc{};
|
|
uint8_t sp{};
|
|
uint8_t acc{};
|
|
uint8_t x{};
|
|
uint8_t y{};
|
|
Status status{};
|
|
uint64_t prevCycles{};
|
|
uint64_t cycles{};
|
|
bool irq{};
|
|
|
|
// Emulator variables
|
|
bool pageBoundaryCrossed{};
|
|
uint8_t opcode{};
|
|
uint16_t fetchedAddress{};
|
|
uint8_t fetchedByte{};
|
|
|
|
Instruction instructions[INSTRUCTION_COUNT]{};
|
|
|
|
NES* nes;
|
|
};
|