752 lines
12 KiB
C++
752 lines
12 KiB
C++
#include "CPU/CPU.hpp"
|
|
#include "NES.hpp"
|
|
|
|
|
|
// --------------- ARITHMETIC --------------- //
|
|
void CPU::ADC()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
uint16_t tempSum = acc + fetchedByte;
|
|
|
|
tempSum += status.carry;
|
|
|
|
status.carry = (tempSum & 0xFF00u) ? 1u : 0u;
|
|
|
|
bool accSign = IsNegative(acc);
|
|
bool valSign = IsNegative(fetchedByte);
|
|
bool sumSign = IsNegative(tempSum);
|
|
|
|
acc = tempSum;
|
|
|
|
// If sign of operands does not match sign of result -> overflow
|
|
status.overflow = ((accSign == valSign) && (accSign != sumSign)) ? 1u : 0u;
|
|
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::SBC()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
uint16_t tempDiff = acc - fetchedByte;
|
|
|
|
tempDiff -= (1u - status.carry);
|
|
|
|
status.carry = (tempDiff & 0xFF00) ? 0u : 1u;
|
|
|
|
bool accSign = IsNegative(acc);
|
|
bool valSign = IsNegative(fetchedByte);
|
|
bool sumSign = IsNegative(tempDiff);
|
|
|
|
// Set and truncate most-significant byte of temp sum
|
|
acc = tempDiff;
|
|
|
|
// If sign of operands does not match sign of result -> overflow
|
|
status.overflow = ((accSign != valSign) && (valSign == sumSign)) ? 1u : 0u;
|
|
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::CMP()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
status.carry = (acc >= fetchedByte) ? 1u : 0u;
|
|
status.zero = (acc == fetchedByte) ? 1u : 0u;
|
|
status.negative = (IsNegative(acc - fetchedByte)) ? 1u : 0u;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::CPX()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
status.carry = (x >= fetchedByte) ? 1u : 0u;
|
|
status.zero = (x == fetchedByte) ? 1u : 0u;
|
|
status.negative = (IsNegative(x - fetchedByte)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::CPY()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
status.carry = (y >= fetchedByte) ? 1u : 0u;
|
|
status.zero = (y == fetchedByte) ? 1u : 0u;
|
|
status.negative = (IsNegative(y - fetchedByte)) ? 1u : 0u;
|
|
}
|
|
|
|
|
|
// --------------- BRANCHES --------------- //
|
|
void CPU::BPL()
|
|
{
|
|
if (!status.negative)
|
|
{
|
|
pc = fetchedAddress;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
|
}
|
|
}
|
|
|
|
void CPU::BMI()
|
|
{
|
|
if (status.negative)
|
|
{
|
|
pc = fetchedAddress;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
|
}
|
|
}
|
|
|
|
void CPU::BVC()
|
|
{
|
|
if (!status.overflow)
|
|
{
|
|
pc = fetchedAddress;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
|
}
|
|
}
|
|
|
|
void CPU::BVS()
|
|
{
|
|
if (status.overflow)
|
|
{
|
|
pc = fetchedAddress;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
|
}
|
|
}
|
|
|
|
void CPU::BCC()
|
|
{
|
|
if (!status.carry)
|
|
{
|
|
pc = fetchedAddress;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
|
}
|
|
}
|
|
|
|
void CPU::BCS()
|
|
{
|
|
if (status.carry)
|
|
{
|
|
pc = fetchedAddress;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
|
}
|
|
}
|
|
|
|
void CPU::BNE()
|
|
{
|
|
if (!status.zero)
|
|
{
|
|
pc = fetchedAddress;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
|
}
|
|
}
|
|
|
|
void CPU::BEQ()
|
|
{
|
|
if (status.zero)
|
|
{
|
|
pc = fetchedAddress;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 2u : 1u;
|
|
}
|
|
}
|
|
|
|
|
|
// --------------- INCREMENT/DECREMENT --------------- //
|
|
void CPU::INC()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
++fetchedByte;
|
|
|
|
nes->Write(BusSource::CPU, fetchedAddress, fetchedByte);
|
|
|
|
status.zero = fetchedByte ? 0u : 1u;
|
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::INX()
|
|
{
|
|
++x;
|
|
|
|
status.zero = x ? 0u : 1u;
|
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::INY()
|
|
{
|
|
++y;
|
|
|
|
status.zero = y ? 0u : 1u;
|
|
status.negative = (IsNegative(y)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::DEC()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
--fetchedByte;
|
|
|
|
nes->Write(BusSource::CPU, fetchedAddress, fetchedByte);
|
|
|
|
status.zero = fetchedByte ? 0u : 1u;
|
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::DEX()
|
|
{
|
|
--x;
|
|
|
|
status.zero = x ? 0u : 1u;
|
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::DEY()
|
|
{
|
|
--y;
|
|
|
|
status.zero = y ? 0u : 1u;
|
|
status.negative = (IsNegative(y)) ? 1u : 0u;
|
|
}
|
|
|
|
|
|
// --------------- JUMPS & CALLS --------------- //
|
|
void CPU::JMP()
|
|
{
|
|
pc = fetchedAddress;
|
|
}
|
|
|
|
void CPU::JSR()
|
|
{
|
|
uint8_t pcMsb = ((pc - 1u) & 0xFF00u) >> 8u;
|
|
uint8_t pcLsb = (pc - 1u) & 0xFFu;
|
|
|
|
StackPush(pcMsb);
|
|
StackPush(pcLsb);
|
|
|
|
pc = fetchedAddress;
|
|
}
|
|
|
|
void CPU::RTS()
|
|
{
|
|
uint8_t pcLsb = StackPop();
|
|
uint8_t pcMsb = StackPop();
|
|
|
|
uint16_t address = ComposeAddress(pcMsb, pcLsb) + 1u;
|
|
|
|
pc = address;
|
|
}
|
|
|
|
|
|
// --------------- LOAD/STORE --------------- //
|
|
void CPU::LDA()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
|
|
acc = fetchedByte;
|
|
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::LDX()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
|
|
x = fetchedByte;
|
|
|
|
status.zero = x ? 0u : 1u;
|
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::LDY()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
|
|
y = fetchedByte;
|
|
|
|
status.zero = y ? 0u : 1u;
|
|
status.negative = (IsNegative(y)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::STA()
|
|
{
|
|
nes->Write(BusSource::CPU, fetchedAddress, acc);
|
|
}
|
|
|
|
void CPU::STX()
|
|
{
|
|
nes->Write(BusSource::CPU, fetchedAddress, x);
|
|
}
|
|
|
|
void CPU::STY()
|
|
{
|
|
nes->Write(BusSource::CPU, fetchedAddress, y);
|
|
}
|
|
|
|
|
|
// --------------- LOGICAL --------------- //
|
|
void CPU::AND()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
|
|
acc &= fetchedByte;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::EOR()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
acc ^= fetchedByte;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::ORA()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
acc |= fetchedByte;
|
|
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::BIT()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
status.zero = (acc & fetchedByte) ? 0u : 1u;
|
|
status.overflow = (TestBits(fetchedByte, (1u << 6u))) ? 1u : 0u;
|
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
|
}
|
|
|
|
|
|
// --------------- SHIFTS --------------- //
|
|
void CPU::ASL()
|
|
{
|
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
|
{
|
|
fetchedByte = acc;
|
|
}
|
|
else
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
}
|
|
|
|
// Set carry to fetchedByte of MSB pre-shift
|
|
if (TestBits(fetchedByte, 0x80))
|
|
{
|
|
status.carry = 1u;
|
|
}
|
|
else
|
|
{
|
|
status.carry = 0u;
|
|
}
|
|
|
|
fetchedByte <<= 1u;
|
|
|
|
status.zero = fetchedByte ? 0u : 1u;
|
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
|
|
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
|
{
|
|
acc = fetchedByte;
|
|
}
|
|
else
|
|
{
|
|
nes->Write(BusSource::CPU, fetchedAddress, fetchedByte);
|
|
}
|
|
}
|
|
|
|
void CPU::LSR()
|
|
{
|
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
|
{
|
|
fetchedByte = acc;
|
|
}
|
|
else
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
}
|
|
|
|
// Set carry to fetchedByte of LSB pre-shift
|
|
if (TestBits(fetchedByte, 0x01))
|
|
{
|
|
status.carry = 1u;
|
|
}
|
|
else
|
|
{
|
|
status.carry = 0u;
|
|
}
|
|
|
|
fetchedByte >>= 1u;
|
|
|
|
status.zero = fetchedByte ? 0u : 1u;
|
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
|
|
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
|
{
|
|
acc = fetchedByte;
|
|
}
|
|
else
|
|
{
|
|
nes->Write(BusSource::CPU, fetchedAddress, fetchedByte);
|
|
}
|
|
}
|
|
|
|
void CPU::ROL()
|
|
{
|
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
|
{
|
|
fetchedByte = acc;
|
|
}
|
|
else
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
}
|
|
|
|
uint8_t oldCarry = status.carry;
|
|
|
|
// Set carry to fetchedByte of MSB pre-shift
|
|
if (TestBits(fetchedByte, 0x80))
|
|
{
|
|
status.carry = 1u;
|
|
}
|
|
else
|
|
{
|
|
status.carry = 0u;
|
|
}
|
|
|
|
fetchedByte <<= 1u;
|
|
|
|
// Put old carry at LSB
|
|
if (oldCarry)
|
|
{
|
|
fetchedByte |= 0x1u;
|
|
}
|
|
|
|
status.zero = fetchedByte ? 0u : 1u;
|
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
|
|
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
|
{
|
|
acc = fetchedByte;
|
|
}
|
|
else
|
|
{
|
|
nes->Write(BusSource::CPU, fetchedAddress, fetchedByte);
|
|
}
|
|
}
|
|
|
|
void CPU::ROR()
|
|
{
|
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
|
{
|
|
fetchedByte = acc;
|
|
}
|
|
else
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
}
|
|
|
|
uint8_t oldCarry = status.carry;
|
|
|
|
// Set carry to fetchedByte of LSB pre-shift
|
|
if (TestBits(fetchedByte, 0x01))
|
|
{
|
|
status.carry = 1u;
|
|
}
|
|
else
|
|
{
|
|
status.carry = 0u;
|
|
}
|
|
|
|
fetchedByte >>= 1u;
|
|
|
|
// Put old carry at MSB
|
|
if (oldCarry)
|
|
{
|
|
fetchedByte |= 0x80u;
|
|
}
|
|
|
|
status.zero = fetchedByte ? 0u : 1u;
|
|
status.negative = (IsNegative(fetchedByte)) ? 1u : 0u;
|
|
|
|
if (instructions[opcode].fetch == &CPU::Implicit)
|
|
{
|
|
acc = fetchedByte;
|
|
}
|
|
else
|
|
{
|
|
nes->Write(BusSource::CPU, fetchedAddress, fetchedByte);
|
|
}
|
|
}
|
|
|
|
|
|
// --------------- STACK --------------- //
|
|
void CPU::PHP()
|
|
{
|
|
Status statusCopy = status;
|
|
statusCopy.b1 = 1u;
|
|
statusCopy.b0 = 1u;
|
|
|
|
StackPush(statusCopy.GetByte());
|
|
}
|
|
|
|
void CPU::PLP()
|
|
{
|
|
uint8_t stackCopy = StackPop();
|
|
|
|
status.SetByte(stackCopy);
|
|
status.b1 = 1u;
|
|
status.b0 = 0u;
|
|
}
|
|
|
|
void CPU::PHA()
|
|
{
|
|
StackPush(acc);
|
|
}
|
|
|
|
void CPU::PLA()
|
|
{
|
|
acc = StackPop();
|
|
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
}
|
|
|
|
|
|
// --------------- STATUS FLAG CHANGES --------------- //
|
|
void CPU::CLC()
|
|
{
|
|
status.carry = 0u;
|
|
}
|
|
|
|
void CPU::SEC()
|
|
{
|
|
status.carry = 1u;
|
|
}
|
|
|
|
void CPU::CLI()
|
|
{
|
|
status.interruptDisable = 0u;
|
|
}
|
|
|
|
void CPU::SEI()
|
|
{
|
|
status.interruptDisable = 1u;
|
|
}
|
|
|
|
void CPU::CLV()
|
|
{
|
|
status.overflow = 0u;
|
|
}
|
|
|
|
void CPU::CLD()
|
|
{
|
|
status.decimal = 0u;
|
|
}
|
|
|
|
void CPU::SED()
|
|
{
|
|
status.decimal = 1u;
|
|
}
|
|
|
|
|
|
// --------------- SYSTEM --------------- //
|
|
void CPU::BRK()
|
|
{
|
|
uint8_t pcMsb = (pc & 0xFF00u) >> 8u;
|
|
uint8_t pcLsb = pc & 0xFFu;
|
|
|
|
StackPush(pcMsb);
|
|
StackPush(pcLsb);
|
|
|
|
Status statusCopy = status;
|
|
statusCopy.b1 = 1u;
|
|
statusCopy.b0 = 0u;
|
|
|
|
StackPush(statusCopy.GetByte());
|
|
|
|
pcMsb = nes->Read(BusSource::CPU, IRQ_VECTOR_MSB);
|
|
pcLsb = nes->Read(BusSource::CPU, IRQ_VECTOR_LSB);
|
|
|
|
pc = ComposeAddress(pcMsb, pcLsb);
|
|
}
|
|
|
|
void CPU::NOP()
|
|
{
|
|
// Do nothing
|
|
cycles += (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::RTI()
|
|
{
|
|
uint8_t statusCopy = StackPop();
|
|
|
|
status.SetByte(statusCopy);
|
|
status.b1 = 1u;
|
|
|
|
uint8_t pcLsb = StackPop();
|
|
uint8_t pcMsb = StackPop();
|
|
|
|
pc = ComposeAddress(pcMsb, pcLsb);
|
|
}
|
|
|
|
|
|
// --------------- TRANSFERS --------------- //
|
|
void CPU::TXA()
|
|
{
|
|
acc = x;
|
|
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::TYA()
|
|
{
|
|
acc = y;
|
|
|
|
status.zero = acc ? 0u : 1u;
|
|
status.negative = (IsNegative(acc)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::TXS()
|
|
{
|
|
sp = x;
|
|
}
|
|
|
|
void CPU::TAY()
|
|
{
|
|
y = acc;
|
|
|
|
status.zero = y ? 0u : 1u;
|
|
status.negative = (IsNegative(y)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::TAX()
|
|
{
|
|
x = acc;
|
|
|
|
status.zero = x ? 0u : 1u;
|
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::TSX()
|
|
{
|
|
x = sp;
|
|
|
|
status.zero = x ? 0u : 1u;
|
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
|
}
|
|
|
|
|
|
// --------------- UNOFFICIAL --------------- //
|
|
void CPU::LAX()
|
|
{
|
|
LDA();
|
|
TAX();
|
|
}
|
|
|
|
void CPU::SAX()
|
|
{
|
|
uint8_t byte = acc & x;
|
|
|
|
nes->Write(BusSource::CPU, fetchedAddress, byte);
|
|
}
|
|
|
|
void CPU::DCP()
|
|
{
|
|
DEC();
|
|
CMP();
|
|
|
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::ISC()
|
|
{
|
|
INC();
|
|
SBC();
|
|
|
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::SLO()
|
|
{
|
|
ASL();
|
|
ORA();
|
|
|
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::RLA()
|
|
{
|
|
ROL();
|
|
AND();
|
|
|
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::SRE()
|
|
{
|
|
LSR();
|
|
EOR();
|
|
|
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::RRA()
|
|
{
|
|
ROR();
|
|
ADC();
|
|
|
|
cycles -= (pageBoundaryCrossed) ? 1u : 0u;
|
|
}
|
|
|
|
void CPU::ANC()
|
|
{
|
|
AND();
|
|
|
|
status.carry = (fetchedByte & 0x80u) >> 7u;
|
|
}
|
|
|
|
void CPU::ALR()
|
|
{
|
|
AND();
|
|
LSR();
|
|
}
|
|
|
|
void CPU::ARR()
|
|
{
|
|
AND();
|
|
ROR();
|
|
|
|
status.carry = (fetchedByte & 0x40u) >> 6u;
|
|
status.overflow = ((fetchedByte & 0x40u) >> 6u) ^ ((fetchedByte & 0x20u) >> 5u);
|
|
}
|
|
|
|
void CPU::AXS()
|
|
{
|
|
fetchedByte = nes->Read(BusSource::CPU, fetchedAddress);
|
|
x = (acc & x) - fetchedByte;
|
|
|
|
status.negative = (IsNegative(x)) ? 1u : 0u;
|
|
status.zero = x ? 0u : 1u;
|
|
status.carry = (x & 0xFF00u) ? 1u : 0u;
|
|
}
|