Fixes a crash in Super Chinese 3 caused by the PPU rapidly switching the NMI signal on and then back off (which caused the CPU to incorrectly jump to the IRQ handler and crashed the game)
885 lines
No EOL
20 KiB
C++
885 lines
No EOL
20 KiB
C++
#if (defined(DUMMYCPU) && !defined(__DUMMYCPU__H)) || (!defined(DUMMYCPU) && !defined(__CPU__H))
|
|
#ifdef DUMMYCPU
|
|
#define __DUMMYCPU__H
|
|
#else
|
|
#define __CPU__H
|
|
#endif
|
|
|
|
#include "stdafx.h"
|
|
#include "Snapshotable.h"
|
|
#include "Types.h"
|
|
|
|
enum class NesModel;
|
|
class Console;
|
|
class MemoryManager;
|
|
class DummyCpu;
|
|
|
|
class CPU : public Snapshotable
|
|
{
|
|
#ifndef DUMMYCPU
|
|
friend DummyCpu;
|
|
#endif
|
|
|
|
public:
|
|
static constexpr uint16_t NMIVector = 0xFFFA;
|
|
static constexpr uint16_t ResetVector = 0xFFFC;
|
|
static constexpr uint16_t IRQVector = 0xFFFE;
|
|
static constexpr uint32_t ClockRateNtsc = 1789773;
|
|
static constexpr uint32_t ClockRatePal = 1662607;
|
|
static constexpr uint32_t ClockRateDendy = 1773448;
|
|
|
|
private:
|
|
typedef void(CPU::*Func)();
|
|
|
|
uint64_t _cycleCount;
|
|
uint64_t _masterClock;
|
|
uint8_t _ppuOffset;
|
|
uint8_t _startClockCount;
|
|
uint8_t _endClockCount;
|
|
uint16_t _operand;
|
|
|
|
Func _opTable[256];
|
|
AddrMode _addrMode[256];
|
|
AddrMode _instAddrMode;
|
|
|
|
bool _needHalt = false;
|
|
bool _spriteDmaTransfer = false;
|
|
bool _dmcDmaRunning = false;
|
|
bool _needDummyRead = false;
|
|
uint8_t _spriteDmaOffset;
|
|
|
|
bool _cpuWrite = false;
|
|
|
|
uint8_t _irqMask;
|
|
|
|
State _state;
|
|
shared_ptr<Console> _console;
|
|
MemoryManager* _memoryManager;
|
|
|
|
bool _prevRunIrq = false;
|
|
bool _runIrq = false;
|
|
|
|
bool _prevNmiFlag = false;
|
|
bool _prevNeedNmi = false;
|
|
bool _needNmi = false;
|
|
|
|
uint64_t _lastCrashWarning = 0;
|
|
|
|
#ifdef DUMMYCPU
|
|
uint32_t _writeCounter = 0;
|
|
uint16_t _writeAddresses[10];
|
|
uint8_t _writeValue[10];
|
|
bool _isDummyWrite[10];
|
|
|
|
uint32_t _readCounter = 0;
|
|
uint16_t _readAddresses[10];
|
|
uint8_t _readValue[10];
|
|
bool _isDummyRead[10];
|
|
#endif
|
|
|
|
__forceinline void StartCpuCycle(bool forRead);
|
|
__forceinline void ProcessPendingDma(uint16_t readAddress);
|
|
__forceinline uint16_t FetchOperand();
|
|
__forceinline void EndCpuCycle(bool forRead);
|
|
void IRQ();
|
|
|
|
uint8_t GetOPCode()
|
|
{
|
|
uint8_t opCode = MemoryRead(_state.PC, MemoryOperationType::ExecOpCode);
|
|
_state.PC++;
|
|
return opCode;
|
|
}
|
|
|
|
void DummyRead()
|
|
{
|
|
MemoryRead(_state.PC, MemoryOperationType::DummyRead);
|
|
}
|
|
|
|
uint8_t ReadByte()
|
|
{
|
|
uint8_t value = MemoryRead(_state.PC, MemoryOperationType::ExecOperand);
|
|
_state.PC++;
|
|
return value;
|
|
}
|
|
|
|
uint16_t ReadWord()
|
|
{
|
|
uint16_t value = MemoryReadWord(_state.PC, MemoryOperationType::ExecOperand);
|
|
_state.PC += 2;
|
|
return value;
|
|
}
|
|
|
|
void ClearFlags(uint8_t flags)
|
|
{
|
|
_state.PS &= ~flags;
|
|
}
|
|
|
|
void SetFlags(uint8_t flags)
|
|
{
|
|
_state.PS |= flags;
|
|
}
|
|
|
|
bool CheckFlag(uint8_t flag)
|
|
{
|
|
return (_state.PS & flag) == flag;
|
|
}
|
|
|
|
void SetZeroNegativeFlags(uint8_t value)
|
|
{
|
|
if(value == 0) {
|
|
SetFlags(PSFlags::Zero);
|
|
} else if(value & 0x80) {
|
|
SetFlags(PSFlags::Negative);
|
|
}
|
|
}
|
|
|
|
bool CheckPageCrossed(uint16_t valA, int8_t valB)
|
|
{
|
|
return ((valA + valB) & 0xFF00) != (valA & 0xFF00);
|
|
}
|
|
|
|
bool CheckPageCrossed(uint16_t valA, uint8_t valB)
|
|
{
|
|
return ((valA + valB) & 0xFF00) != (valA & 0xFF00);
|
|
}
|
|
|
|
void MemoryWrite(uint16_t addr, uint8_t value, MemoryOperationType operationType = MemoryOperationType::Write);
|
|
uint8_t MemoryRead(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read);
|
|
|
|
uint16_t MemoryReadWord(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read) {
|
|
uint8_t lo = MemoryRead(addr, operationType);
|
|
uint8_t hi = MemoryRead(addr + 1, operationType);
|
|
return lo | hi << 8;
|
|
}
|
|
|
|
void SetRegister(uint8_t ®, uint8_t value) {
|
|
ClearFlags(PSFlags::Zero | PSFlags::Negative);
|
|
SetZeroNegativeFlags(value);
|
|
reg = value;
|
|
}
|
|
|
|
void Push(uint8_t value) {
|
|
MemoryWrite(SP() + 0x100, value);
|
|
SetSP(SP() - 1);
|
|
}
|
|
|
|
void Push(uint16_t value) {
|
|
Push((uint8_t)(value >> 8));
|
|
Push((uint8_t)value);
|
|
}
|
|
|
|
uint8_t Pop() {
|
|
SetSP(SP() + 1);
|
|
return MemoryRead(0x100 + SP());
|
|
}
|
|
|
|
uint16_t PopWord() {
|
|
uint8_t lo = Pop();
|
|
uint8_t hi = Pop();
|
|
|
|
return lo | hi << 8;
|
|
}
|
|
|
|
uint8_t A() { return _state.A; }
|
|
void SetA(uint8_t value) { SetRegister(_state.A, value); }
|
|
uint8_t X() { return _state.X; }
|
|
void SetX(uint8_t value) { SetRegister(_state.X, value); }
|
|
uint8_t Y() { return _state.Y; }
|
|
void SetY(uint8_t value) { SetRegister(_state.Y, value); }
|
|
uint8_t SP() { return _state.SP; }
|
|
void SetSP(uint8_t value) { _state.SP = value; }
|
|
uint8_t PS() { return _state.PS; }
|
|
void SetPS(uint8_t value) { _state.PS = value & 0xCF; }
|
|
uint16_t PC() { return _state.PC; }
|
|
void SetPC(uint16_t value) { _state.PC = value; }
|
|
|
|
uint16_t GetOperand()
|
|
{
|
|
return _operand;
|
|
}
|
|
|
|
uint8_t GetOperandValue()
|
|
{
|
|
if(_instAddrMode >= AddrMode::Zero) {
|
|
return MemoryRead(GetOperand());
|
|
} else {
|
|
return (uint8_t)GetOperand();
|
|
}
|
|
}
|
|
|
|
uint16_t GetIndAddr() { return ReadWord(); }
|
|
uint8_t GetImmediate() { return ReadByte(); }
|
|
uint8_t GetZeroAddr() { return ReadByte(); }
|
|
uint8_t GetZeroXAddr() {
|
|
uint8_t value = ReadByte();
|
|
MemoryRead(value, MemoryOperationType::DummyRead); //Dummy read
|
|
return value + X();
|
|
}
|
|
uint8_t GetZeroYAddr() {
|
|
uint8_t value = ReadByte();
|
|
MemoryRead(value, MemoryOperationType::DummyRead); //Dummy read
|
|
return value + Y();
|
|
}
|
|
uint16_t GetAbsAddr() { return ReadWord(); }
|
|
|
|
uint16_t GetAbsXAddr(bool dummyRead = true) {
|
|
uint16_t baseAddr = ReadWord();
|
|
bool pageCrossed = CheckPageCrossed(baseAddr, X());
|
|
|
|
if(pageCrossed || dummyRead) {
|
|
//Dummy read done by the processor (only when page is crossed for READ instructions)
|
|
MemoryRead(baseAddr + X() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
|
|
}
|
|
return baseAddr + X();
|
|
}
|
|
|
|
uint16_t GetAbsYAddr(bool dummyRead = true) {
|
|
uint16_t baseAddr = ReadWord();
|
|
bool pageCrossed = CheckPageCrossed(baseAddr, Y());
|
|
|
|
if(pageCrossed || dummyRead) {
|
|
//Dummy read done by the processor (only when page is crossed for READ instructions)
|
|
MemoryRead(baseAddr + Y() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
|
|
}
|
|
|
|
return baseAddr + Y();
|
|
}
|
|
|
|
uint16_t GetInd() {
|
|
uint16_t addr = GetOperand();
|
|
if((addr & 0xFF) == 0xFF) {
|
|
auto lo = MemoryRead(addr);
|
|
auto hi = MemoryRead(addr - 0xFF);
|
|
return (lo | hi << 8);
|
|
} else {
|
|
return MemoryReadWord(addr);
|
|
}
|
|
}
|
|
|
|
uint16_t GetIndXAddr() {
|
|
uint8_t zero = ReadByte();
|
|
|
|
//Dummy read
|
|
MemoryRead(zero, MemoryOperationType::DummyRead);
|
|
|
|
zero += X();
|
|
|
|
uint16_t addr;
|
|
if(zero == 0xFF) {
|
|
addr = MemoryRead(0xFF) | MemoryRead(0x00) << 8;
|
|
} else {
|
|
addr = MemoryReadWord(zero);
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
uint16_t GetIndYAddr(bool dummyRead = true) {
|
|
uint8_t zero = ReadByte();
|
|
|
|
uint16_t addr;
|
|
if(zero == 0xFF) {
|
|
addr = MemoryRead(0xFF) | MemoryRead(0x00) << 8;
|
|
} else {
|
|
addr = MemoryReadWord(zero);
|
|
}
|
|
|
|
bool pageCrossed = CheckPageCrossed(addr, Y());
|
|
if(pageCrossed || dummyRead) {
|
|
//Dummy read done by the processor (only when page is crossed for READ instructions)
|
|
MemoryRead(addr + Y() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
|
|
}
|
|
return addr + Y();
|
|
}
|
|
|
|
void AND() { SetA(A() & GetOperandValue()); }
|
|
void EOR() { SetA(A() ^ GetOperandValue()); }
|
|
void ORA() { SetA(A() | GetOperandValue()); }
|
|
|
|
void ADD(uint8_t value)
|
|
{
|
|
uint16_t result = (uint16_t)A() + (uint16_t)value + (CheckFlag(PSFlags::Carry) ? PSFlags::Carry : 0x00);
|
|
|
|
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Overflow | PSFlags::Zero);
|
|
SetZeroNegativeFlags((uint8_t)result);
|
|
if(~(A() ^ value) & (A() ^ result) & 0x80) {
|
|
SetFlags(PSFlags::Overflow);
|
|
}
|
|
if(result > 0xFF) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
SetA((uint8_t)result);
|
|
}
|
|
|
|
void ADC() { ADD(GetOperandValue()); }
|
|
void SBC() { ADD(GetOperandValue() ^ 0xFF); }
|
|
|
|
void CMP(uint8_t reg, uint8_t value)
|
|
{
|
|
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
|
|
|
|
auto result = reg - value;
|
|
|
|
if(reg >= value) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
if(reg == value) {
|
|
SetFlags(PSFlags::Zero);
|
|
}
|
|
if((result & 0x80) == 0x80) {
|
|
SetFlags(PSFlags::Negative);
|
|
}
|
|
}
|
|
|
|
void CPA() { CMP(A(), GetOperandValue()); }
|
|
void CPX() { CMP(X(), GetOperandValue()); }
|
|
void CPY() { CMP(Y(), GetOperandValue()); }
|
|
|
|
void INC()
|
|
{
|
|
uint16_t addr = GetOperand();
|
|
ClearFlags(PSFlags::Negative | PSFlags::Zero);
|
|
uint8_t value = MemoryRead(addr);
|
|
|
|
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
|
|
|
|
value++;
|
|
SetZeroNegativeFlags(value);
|
|
MemoryWrite(addr, value);
|
|
}
|
|
|
|
void DEC()
|
|
{
|
|
uint16_t addr = GetOperand();
|
|
ClearFlags(PSFlags::Negative | PSFlags::Zero);
|
|
uint8_t value = MemoryRead(addr);
|
|
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
|
|
|
|
value--;
|
|
SetZeroNegativeFlags(value);
|
|
MemoryWrite(addr, value);
|
|
}
|
|
|
|
uint8_t ASL(uint8_t value)
|
|
{
|
|
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
|
|
if(value & 0x80) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
|
|
uint8_t result = value << 1;
|
|
SetZeroNegativeFlags(result);
|
|
return result;
|
|
}
|
|
|
|
uint8_t LSR(uint8_t value) {
|
|
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
|
|
if(value & 0x01) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
|
|
uint8_t result = value >> 1;
|
|
SetZeroNegativeFlags(result);
|
|
return result;
|
|
}
|
|
|
|
uint8_t ROL(uint8_t value) {
|
|
bool carryFlag = CheckFlag(PSFlags::Carry);
|
|
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
|
|
|
|
if(value & 0x80) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
|
|
uint8_t result = (value << 1 | (carryFlag ? 0x01 : 0x00));
|
|
SetZeroNegativeFlags(result);
|
|
return result;
|
|
}
|
|
|
|
uint8_t ROR(uint8_t value) {
|
|
bool carryFlag = CheckFlag(PSFlags::Carry);
|
|
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
|
|
if(value & 0x01) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
|
|
uint8_t result = (value >> 1 | (carryFlag ? 0x80 : 0x00));
|
|
SetZeroNegativeFlags(result);
|
|
return result;
|
|
}
|
|
|
|
void ASLAddr() {
|
|
uint16_t addr = GetOperand();
|
|
uint8_t value = MemoryRead(addr);
|
|
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
|
|
MemoryWrite(addr, ASL(value));
|
|
}
|
|
|
|
void LSRAddr() {
|
|
uint16_t addr = GetOperand();
|
|
uint8_t value = MemoryRead(addr);
|
|
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
|
|
MemoryWrite(addr, LSR(value));
|
|
}
|
|
|
|
void ROLAddr() {
|
|
uint16_t addr = GetOperand();
|
|
uint8_t value = MemoryRead(addr);
|
|
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
|
|
MemoryWrite(addr, ROL(value));
|
|
}
|
|
|
|
void RORAddr() {
|
|
uint16_t addr = GetOperand();
|
|
uint8_t value = MemoryRead(addr);
|
|
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
|
|
MemoryWrite(addr, ROR(value));
|
|
}
|
|
|
|
void JMP(uint16_t addr) {
|
|
SetPC(addr);
|
|
}
|
|
|
|
void BranchRelative(bool branch) {
|
|
int8_t offset = (int8_t)GetOperand();
|
|
if(branch) {
|
|
//"a taken non-page-crossing branch ignores IRQ/NMI during its last clock, so that next instruction executes before the IRQ"
|
|
//Fixes "branch_delays_irq" test
|
|
if(_runIrq && !_prevRunIrq) {
|
|
_runIrq = false;
|
|
}
|
|
DummyRead();
|
|
|
|
if(CheckPageCrossed(PC(), offset)) {
|
|
DummyRead();
|
|
}
|
|
|
|
SetPC(PC() + offset);
|
|
}
|
|
}
|
|
|
|
void BIT() {
|
|
uint8_t value = GetOperandValue();
|
|
ClearFlags(PSFlags::Zero | PSFlags::Overflow | PSFlags::Negative);
|
|
if((A() & value) == 0) {
|
|
SetFlags(PSFlags::Zero);
|
|
}
|
|
if(value & 0x40) {
|
|
SetFlags(PSFlags::Overflow);
|
|
}
|
|
if(value & 0x80) {
|
|
SetFlags(PSFlags::Negative);
|
|
}
|
|
}
|
|
|
|
//OP Codes
|
|
void LDA() { SetA(GetOperandValue()); }
|
|
void LDX() { SetX(GetOperandValue()); }
|
|
void LDY() { SetY(GetOperandValue()); }
|
|
|
|
void STA() { MemoryWrite(GetOperand(), A()); }
|
|
void STX() { MemoryWrite(GetOperand(), X()); }
|
|
void STY() { MemoryWrite(GetOperand(), Y()); }
|
|
|
|
void TAX() { SetX(A()); }
|
|
void TAY() { SetY(A()); }
|
|
void TSX() { SetX(SP()); }
|
|
void TXA() { SetA(X()); }
|
|
void TXS() { SetSP(X()); }
|
|
void TYA() { SetA(Y()); }
|
|
|
|
void PHA() { Push(A()); }
|
|
void PHP() {
|
|
uint8_t flags = PS() | PSFlags::Break | PSFlags::Reserved;
|
|
Push((uint8_t)flags);
|
|
}
|
|
void PLA() {
|
|
DummyRead();
|
|
SetA(Pop());
|
|
}
|
|
void PLP() {
|
|
DummyRead();
|
|
SetPS(Pop());
|
|
}
|
|
|
|
void INX() { SetX(X() + 1); }
|
|
void INY() { SetY(Y() + 1); }
|
|
|
|
void DEX() { SetX(X() - 1); }
|
|
void DEY() { SetY(Y() - 1); }
|
|
|
|
void ASL_Acc() { SetA(ASL(A())); }
|
|
void ASL_Memory() { ASLAddr(); }
|
|
|
|
void LSR_Acc() { SetA(LSR(A())); }
|
|
void LSR_Memory() { LSRAddr(); }
|
|
|
|
void ROL_Acc() { SetA(ROL(A())); }
|
|
void ROL_Memory() { ROLAddr(); }
|
|
|
|
void ROR_Acc() { SetA(ROR(A())); }
|
|
void ROR_Memory() { RORAddr(); }
|
|
|
|
void JMP_Abs() {
|
|
JMP(GetOperand());
|
|
}
|
|
void JMP_Ind() { JMP(GetInd()); }
|
|
void JSR() {
|
|
uint16_t addr = GetOperand();
|
|
DummyRead();
|
|
Push((uint16_t)(PC() - 1));
|
|
JMP(addr);
|
|
}
|
|
void RTS() {
|
|
uint16_t addr = PopWord();
|
|
DummyRead();
|
|
DummyRead();
|
|
SetPC(addr + 1);
|
|
}
|
|
|
|
void BCC() {
|
|
BranchRelative(!CheckFlag(PSFlags::Carry));
|
|
}
|
|
|
|
void BCS() {
|
|
BranchRelative(CheckFlag(PSFlags::Carry));
|
|
}
|
|
|
|
void BEQ() {
|
|
BranchRelative(CheckFlag(PSFlags::Zero));
|
|
}
|
|
|
|
void BMI() {
|
|
BranchRelative(CheckFlag(PSFlags::Negative));
|
|
}
|
|
|
|
void BNE() {
|
|
BranchRelative(!CheckFlag(PSFlags::Zero));
|
|
}
|
|
|
|
void BPL() {
|
|
BranchRelative(!CheckFlag(PSFlags::Negative));
|
|
}
|
|
|
|
void BVC() {
|
|
BranchRelative(!CheckFlag(PSFlags::Overflow));
|
|
}
|
|
|
|
void BVS() {
|
|
BranchRelative(CheckFlag(PSFlags::Overflow));
|
|
}
|
|
|
|
void CLC() { ClearFlags(PSFlags::Carry); }
|
|
void CLD() { ClearFlags(PSFlags::Decimal); }
|
|
void CLI() { ClearFlags(PSFlags::Interrupt); }
|
|
void CLV() { ClearFlags(PSFlags::Overflow); }
|
|
void SEC() { SetFlags(PSFlags::Carry); }
|
|
void SED() { SetFlags(PSFlags::Decimal); }
|
|
void SEI() { SetFlags(PSFlags::Interrupt); }
|
|
|
|
void BRK();
|
|
|
|
void RTI() {
|
|
DummyRead();
|
|
SetPS(Pop());
|
|
SetPC(PopWord());
|
|
}
|
|
|
|
void NOP() {
|
|
//Make sure the nop operation takes as many cycles as meant to
|
|
GetOperandValue();
|
|
}
|
|
|
|
|
|
//Unofficial OpCodes
|
|
void SLO()
|
|
{
|
|
//ASL & ORA
|
|
uint8_t value = GetOperandValue();
|
|
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
|
|
uint8_t shiftedValue = ASL(value);
|
|
SetA(A() | shiftedValue);
|
|
MemoryWrite(GetOperand(), shiftedValue);
|
|
}
|
|
|
|
void SRE()
|
|
{
|
|
//ROL & AND
|
|
uint8_t value = GetOperandValue();
|
|
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
|
|
uint8_t shiftedValue = LSR(value);
|
|
SetA(A() ^ shiftedValue);
|
|
MemoryWrite(GetOperand(), shiftedValue);
|
|
}
|
|
|
|
void RLA()
|
|
{
|
|
//LSR & EOR
|
|
uint8_t value = GetOperandValue();
|
|
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
|
|
uint8_t shiftedValue = ROL(value);
|
|
SetA(A() & shiftedValue);
|
|
MemoryWrite(GetOperand(), shiftedValue);
|
|
}
|
|
|
|
void RRA()
|
|
{
|
|
//ROR & ADC
|
|
uint8_t value = GetOperandValue();
|
|
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
|
|
uint8_t shiftedValue = ROR(value);
|
|
ADD(shiftedValue);
|
|
MemoryWrite(GetOperand(), shiftedValue);
|
|
}
|
|
|
|
void SAX()
|
|
{
|
|
//STA & STX
|
|
MemoryWrite(GetOperand(), A() & X());
|
|
}
|
|
|
|
void LAX()
|
|
{
|
|
//LDA & LDX
|
|
uint8_t value = GetOperandValue();
|
|
SetX(value);
|
|
SetA(value);
|
|
}
|
|
|
|
void DCP()
|
|
{
|
|
//DEC & CMP
|
|
uint8_t value = GetOperandValue();
|
|
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
|
|
value--;
|
|
CMP(A(), value);
|
|
MemoryWrite(GetOperand(), value);
|
|
}
|
|
|
|
void ISB()
|
|
{
|
|
//INC & SBC
|
|
uint8_t value = GetOperandValue();
|
|
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
|
|
value++;
|
|
ADD(value ^ 0xFF);
|
|
MemoryWrite(GetOperand(), value);
|
|
}
|
|
|
|
void AAC()
|
|
{
|
|
SetA(A() & GetOperandValue());
|
|
|
|
ClearFlags(PSFlags::Carry);
|
|
if(CheckFlag(PSFlags::Negative)) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
}
|
|
|
|
void ASR()
|
|
{
|
|
ClearFlags(PSFlags::Carry);
|
|
SetA(A() & GetOperandValue());
|
|
if(A() & 0x01) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
SetA(A() >> 1);
|
|
}
|
|
|
|
void ARR()
|
|
{
|
|
SetA(((A() & GetOperandValue()) >> 1) | (CheckFlag(PSFlags::Carry) ? 0x80 : 0x00));
|
|
ClearFlags(PSFlags::Carry | PSFlags::Overflow);
|
|
if(A() & 0x40) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
if((CheckFlag(PSFlags::Carry) ? 0x01 : 0x00) ^ ((A() >> 5) & 0x01)) {
|
|
SetFlags(PSFlags::Overflow);
|
|
}
|
|
}
|
|
|
|
void ATX()
|
|
{
|
|
//LDA & TAX
|
|
uint8_t value = GetOperandValue();
|
|
SetA(value); //LDA
|
|
SetX(A()); //TAX
|
|
SetA(A()); //Update flags based on A
|
|
}
|
|
|
|
void AXS()
|
|
{
|
|
//CMP & DEX
|
|
uint8_t opValue = GetOperandValue();
|
|
uint8_t value = (A() & X()) - opValue;
|
|
|
|
ClearFlags(PSFlags::Carry);
|
|
if((A() & X()) >= opValue) {
|
|
SetFlags(PSFlags::Carry);
|
|
}
|
|
|
|
SetX(value);
|
|
}
|
|
|
|
void SYA()
|
|
{
|
|
uint8_t addrHigh = GetOperand() >> 8;
|
|
uint8_t addrLow = GetOperand() & 0xFF;
|
|
uint8_t value = Y() & (addrHigh + 1);
|
|
|
|
//From here: http://forums.nesdev.com/viewtopic.php?f=3&t=3831&start=30
|
|
//Unsure if this is accurate or not
|
|
//"the target address for e.g. SYA becomes ((y & (addr_high + 1)) << 8) | addr_low instead of the normal ((addr_high + 1) << 8) | addr_low"
|
|
MemoryWrite(((Y() & (addrHigh + 1)) << 8) | addrLow, value);
|
|
}
|
|
|
|
void SXA()
|
|
{
|
|
uint8_t addrHigh = GetOperand() >> 8;
|
|
uint8_t addrLow = GetOperand() & 0xFF;
|
|
uint8_t value = X() & (addrHigh + 1);
|
|
MemoryWrite(((X() & (addrHigh + 1)) << 8) | addrLow, value);
|
|
}
|
|
|
|
//Unimplemented/Incorrect Unofficial OP codes
|
|
void HLT()
|
|
{
|
|
//normally freezes the cpu, we can probably assume nothing will ever call this
|
|
GetOperandValue();
|
|
}
|
|
|
|
void UNK()
|
|
{
|
|
//Make sure we take the right amount of cycles (not reliable for operations that write to memory, etc.)
|
|
GetOperandValue();
|
|
}
|
|
|
|
void AXA()
|
|
{
|
|
uint16_t addr = GetOperand();
|
|
|
|
//"This opcode stores the result of A AND X AND the high byte of the target address of the operand +1 in memory."
|
|
//This may not be the actual behavior, but the read/write operations are needed for proper cycle counting
|
|
MemoryWrite(GetOperand(), ((addr >> 8) + 1) & A() & X());
|
|
}
|
|
|
|
void TAS()
|
|
{
|
|
//"AND X register with accumulator and store result in stack
|
|
//pointer, then AND stack pointer with the high byte of the
|
|
//target address of the argument + 1. Store result in memory."
|
|
uint16_t addr = GetOperand();
|
|
SetSP(X() & A());
|
|
MemoryWrite(addr, SP() & ((addr >> 8) + 1));
|
|
}
|
|
|
|
void LAS()
|
|
{
|
|
//"AND memory with stack pointer, transfer result to accumulator, X register and stack pointer."
|
|
uint8_t value = GetOperandValue();
|
|
SetA(value & SP());
|
|
SetX(A());
|
|
SetSP(A());
|
|
}
|
|
|
|
protected:
|
|
void StreamState(bool saving) override;
|
|
|
|
public:
|
|
CPU(shared_ptr<Console> console);
|
|
|
|
uint64_t GetCycleCount() { return _cycleCount; }
|
|
void SetMasterClockDivider(NesModel region);
|
|
void SetNmiFlag() { _state.NMIFlag = true; }
|
|
void ClearNmiFlag() { _state.NMIFlag = false; }
|
|
void SetIrqMask(uint8_t mask) { _irqMask = mask; }
|
|
void SetIrqSource(IRQSource source) { _state.IRQFlag |= (int)source; }
|
|
bool HasIrqSource(IRQSource source) { return (_state.IRQFlag & (int)source) != 0; }
|
|
void ClearIrqSource(IRQSource source) { _state.IRQFlag &= ~(int)source; }
|
|
|
|
void RunDMATransfer(uint8_t offsetValue);
|
|
void StartDmcTransfer();
|
|
|
|
uint32_t GetClockRate(NesModel model);
|
|
bool IsCpuWrite() { return _cpuWrite; }
|
|
|
|
//Used by debugger for "Set Next Statement"
|
|
void SetDebugPC(uint16_t value) {
|
|
SetPC(value);
|
|
_state.PreviousDebugPC = _state.DebugPC;
|
|
_state.DebugPC = value;
|
|
}
|
|
|
|
void Reset(bool softReset, NesModel model);
|
|
void Exec();
|
|
|
|
void GetState(State &state)
|
|
{
|
|
state = _state;
|
|
state.CycleCount = _cycleCount;
|
|
}
|
|
|
|
uint16_t GetDebugPC() { return _state.DebugPC; }
|
|
uint16_t GetPC() { return _state.PC; }
|
|
|
|
void SetState(State state)
|
|
{
|
|
uint16_t originalPc = state.PC;
|
|
uint16_t originalDebugPc = state.DebugPC;
|
|
_state = state;
|
|
_cycleCount = state.CycleCount;
|
|
state.PC = originalPc;
|
|
state.DebugPC = originalDebugPc;
|
|
}
|
|
|
|
#ifdef DUMMYCPU
|
|
#undef CPU
|
|
void SetDummyState(CPU *c)
|
|
{
|
|
#define CPU DummyCpu
|
|
_writeCounter = 0;
|
|
_readCounter = 0;
|
|
|
|
_state = c->_state;
|
|
|
|
_cycleCount = c->_cycleCount;
|
|
_operand = c->_operand;
|
|
_spriteDmaTransfer = c->_spriteDmaTransfer;
|
|
_needHalt = c->_needHalt;
|
|
_dmcDmaRunning = c->_dmcDmaRunning;
|
|
_cpuWrite = c->_cpuWrite;
|
|
_needDummyRead = c->_needDummyRead;
|
|
_needHalt = c->_needHalt;
|
|
_spriteDmaOffset = c->_spriteDmaOffset;
|
|
_irqMask = c->_irqMask;
|
|
_prevRunIrq = c->_prevRunIrq;
|
|
_runIrq = c->_runIrq;
|
|
_cycleCount = c->_cycleCount;
|
|
}
|
|
|
|
uint32_t GetWriteCount()
|
|
{
|
|
return _writeCounter;
|
|
}
|
|
|
|
uint32_t GetReadCount()
|
|
{
|
|
return _readCounter;
|
|
}
|
|
|
|
void GetWriteAddrValue(uint32_t index, uint16_t &addr, uint8_t &value, bool &isDummyWrite)
|
|
{
|
|
addr = _writeAddresses[index];
|
|
value = _writeValue[index];
|
|
isDummyWrite = _isDummyWrite[index];
|
|
}
|
|
|
|
void GetReadAddr(uint32_t index, uint16_t &addr, uint8_t &value, bool &isDummyRead)
|
|
{
|
|
addr = _readAddresses[index];
|
|
value = _readValue[index];
|
|
isDummyRead = _isDummyRead[index];
|
|
}
|
|
#endif
|
|
};
|
|
|
|
#endif |