Mesen-S/Core/Cpu.Instructions.h
Sour 1d6253d2e5 SA-1: Fixed SA-1 vector logic
Reads done by the code should return the values at the read address, rather than returning the vector register value (fixes Gradius/Contra 3 SA-1 romhacks that are known to work on hardware)
2020-03-01 18:24:24 -05:00

1304 lines
25 KiB
C++

/************************
Add/substract operations
*************************/
void Cpu::Add8(uint8_t value)
{
uint32_t result;
if(CheckFlag(ProcFlags::Decimal)) {
result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
if(result > 0x09) result += 0x06;
result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
} else {
result = (_state.A & 0xFF) + value + (_state.PS & ProcFlags::Carry);
}
if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) {
SetFlags(ProcFlags::Overflow);
} else {
ClearFlags(ProcFlags::Overflow);
}
if(CheckFlag(ProcFlags::Decimal) && result > 0x9F) {
result += 0x60;
}
ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
SetZeroNegativeFlags((uint8_t)result);
if(result > 0xFF) {
SetFlags(ProcFlags::Carry);
}
_state.A = (_state.A & 0xFF00) | (uint8_t)result;
}
void Cpu::Add16(uint16_t value)
{
uint32_t result;
if(CheckFlag(ProcFlags::Decimal)) {
result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
if(result > 0x09) result += 0x06;
result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
if(result > 0x9F) result += 0x60;
result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF);
if(result > 0x9FF) result += 0x600;
result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF);
} else {
result = _state.A + value + (_state.PS & ProcFlags::Carry);
}
if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) {
SetFlags(ProcFlags::Overflow);
} else {
ClearFlags(ProcFlags::Overflow);
}
if(CheckFlag(ProcFlags::Decimal) && result > 0x9FFF) {
result += 0x6000;
}
ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
SetZeroNegativeFlags((uint16_t)result);
if(result > 0xFFFF) {
SetFlags(ProcFlags::Carry);
}
_state.A = (uint16_t)result;
}
void Cpu::ADC()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
Add8(GetByteValue());
} else {
Add16(GetWordValue());
}
}
void Cpu::Sub8(uint8_t value)
{
int32_t result;
if(CheckFlag(ProcFlags::Decimal)) {
result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
if(result <= 0x0F) result -= 0x06;
result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
} else {
result = (_state.A & 0xFF) + value + (_state.PS & ProcFlags::Carry);
}
if(~(_state.A ^ value) & (_state.A ^ result) & 0x80) {
SetFlags(ProcFlags::Overflow);
} else {
ClearFlags(ProcFlags::Overflow);
}
if(CheckFlag(ProcFlags::Decimal) && result <= 0xFF) {
result -= 0x60;
}
ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
SetZeroNegativeFlags((uint8_t)result);
if(result > 0xFF) {
SetFlags(ProcFlags::Carry);
}
_state.A = (_state.A & 0xFF00) | (uint8_t)result;
}
void Cpu::Sub16(uint16_t value)
{
int32_t result;
if(CheckFlag(ProcFlags::Decimal)) {
result = (_state.A & 0x0F) + (value & 0x0F) + (_state.PS & ProcFlags::Carry);
if(result <= 0x0F) result -= 0x06;
result = (_state.A & 0xF0) + (value & 0xF0) + (result > 0x0F ? 0x10 : 0) + (result & 0x0F);
if(result <= 0xFF) result -= 0x60;
result = (_state.A & 0xF00) + (value & 0xF00) + (result > 0xFF ? 0x100 : 0) + (result & 0xFF);
if(result <= 0xFFF) result -= 0x600;
result = (_state.A & 0xF000) + (value & 0xF000) + (result > 0xFFF ? 0x1000 : 0) + (result & 0xFFF);
} else {
result = _state.A + value + (_state.PS & ProcFlags::Carry);
}
if(~(_state.A ^ value) & (_state.A ^ result) & 0x8000) {
SetFlags(ProcFlags::Overflow);
} else {
ClearFlags(ProcFlags::Overflow);
}
if(CheckFlag(ProcFlags::Decimal) && result <= 0xFFFF) {
result -= 0x6000;
}
ClearFlags(ProcFlags::Carry | ProcFlags::Negative | ProcFlags::Zero);
SetZeroNegativeFlags((uint16_t)result);
if(result > 0xFFFF) {
SetFlags(ProcFlags::Carry);
}
_state.A = (uint16_t)result;
}
void Cpu::SBC()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
Sub8(~GetByteValue());
} else {
Sub16(~GetWordValue());
}
}
/****************
Branch operations
****************/
void Cpu::BCC()
{
BranchRelative(!CheckFlag(ProcFlags::Carry));
}
void Cpu::BCS()
{
BranchRelative(CheckFlag(ProcFlags::Carry));
}
void Cpu::BEQ()
{
BranchRelative(CheckFlag(ProcFlags::Zero));
}
void Cpu::BMI()
{
BranchRelative(CheckFlag(ProcFlags::Negative));
}
void Cpu::BNE()
{
BranchRelative(!CheckFlag(ProcFlags::Zero));
}
void Cpu::BPL()
{
BranchRelative(!CheckFlag(ProcFlags::Negative));
}
void Cpu::BRA()
{
BranchRelative(true);
}
void Cpu::BRL()
{
_state.PC = (uint16_t)(_state.PC + (int16_t)_operand);
IdleTakeBranch();
}
void Cpu::BVC()
{
BranchRelative(!CheckFlag(ProcFlags::Overflow));
}
void Cpu::BVS()
{
BranchRelative(CheckFlag(ProcFlags::Overflow));
}
void Cpu::BranchRelative(bool branch)
{
if(branch) {
int8_t offset = _operand;
Idle();
if(_state.EmulationMode && ((uint16_t)(_state.PC + offset) & 0xFF00) != (_state.PC & 0xFF00)) {
//Extra cycle in emulation mode if crossing a page
Idle();
}
_state.PC = (uint16_t)(_state.PC + offset);
IdleTakeBranch();
}
}
/***************************
Set/clear flag instructions
****************************/
void Cpu::CLC()
{
ClearFlags(ProcFlags::Carry);
}
void Cpu::CLD()
{
ClearFlags(ProcFlags::Decimal);
}
void Cpu::CLI()
{
ClearFlags(ProcFlags::IrqDisable);
}
void Cpu::CLV()
{
ClearFlags(ProcFlags::Overflow);
}
void Cpu::SEC()
{
SetFlags(ProcFlags::Carry);
}
void Cpu::SED()
{
SetFlags(ProcFlags::Decimal);
}
void Cpu::SEI()
{
SetFlags(ProcFlags::IrqDisable);
}
void Cpu::REP()
{
Idle();
ClearFlags((uint8_t)_operand);
}
void Cpu::SEP()
{
Idle();
SetFlags((uint8_t)_operand);
if(CheckFlag(ProcFlags::IndexMode8)) {
//Truncate X/Y when 8-bit indexes are enabled
_state.Y &= 0xFF;
_state.X &= 0xFF;
}
}
/******************************
Increment/decrement operations
*******************************/
void Cpu::DEX()
{
IncDecReg(_state.X, -1);
}
void Cpu::DEY()
{
IncDecReg(_state.Y, -1);
}
void Cpu::INX()
{
IncDecReg(_state.X, 1);
}
void Cpu::INY()
{
IncDecReg(_state.Y, 1);
}
void Cpu::DEC()
{
IncDec(-1);
}
void Cpu::INC()
{
IncDec(1);
}
void Cpu::DEC_Acc()
{
SetRegister(_state.A, _state.A - 1, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::INC_Acc()
{
SetRegister(_state.A, _state.A + 1, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::IncDecReg(uint16_t &reg, int8_t offset)
{
SetRegister(reg, reg + offset, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::IncDec(int8_t offset)
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
uint8_t value = GetByteValue() + offset;
SetZeroNegativeFlags(value);
Idle();
Write(_operand, value);
} else {
uint16_t value = GetWordValue() + offset;
SetZeroNegativeFlags(value);
Idle();
WriteWord(_operand, value);
}
}
/********************
Compare instructions
*********************/
void Cpu::Compare(uint16_t reg, bool eightBitMode)
{
if(eightBitMode) {
uint8_t value = GetByteValue();
if((uint8_t)reg >= value) {
SetFlags(ProcFlags::Carry);
} else {
ClearFlags(ProcFlags::Carry);
}
uint8_t result = (uint8_t)reg - value;
SetZeroNegativeFlags(result);
} else {
uint16_t value = GetWordValue();
if(reg >= value) {
SetFlags(ProcFlags::Carry);
} else {
ClearFlags(ProcFlags::Carry);
}
uint16_t result = reg - value;
SetZeroNegativeFlags(result);
}
}
void Cpu::CMP()
{
Compare(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::CPX()
{
Compare(_state.X, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::CPY()
{
Compare(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}
/*****************
Jump instructions
******************/
void Cpu::JML()
{
_state.K = (_operand >> 16) & 0xFF;
_state.PC = (uint16_t)_operand;
IdleEndJump();
}
void Cpu::JMP()
{
_state.PC = (uint16_t)_operand;
IdleEndJump();
}
void Cpu::JSL()
{
PushByte(_state.K);
Idle();
PushWord(_state.PC - 1);
_state.K = (_operand >> 16) & 0xFF;
_state.PC = (uint16_t)_operand;
IdleEndJump();
}
void Cpu::JSR()
{
PushWord(_state.PC - 1);
_state.PC = (uint16_t)_operand;
IdleEndJump();
}
void Cpu::RTI()
{
Idle();
Idle();
if(_state.EmulationMode) {
SetPS(PopByte());
_state.PC = PopWord();
} else {
SetPS(PopByte());
_state.PC = PopWord();
_state.K = PopByte();
}
IdleEndJump();
}
void Cpu::RTL()
{
Idle();
Idle();
_state.PC = PopWord();
_state.PC++;
_state.K = PopByte();
IdleEndJump();
}
void Cpu::RTS()
{
Idle();
Idle();
_state.PC = PopWord();
Idle();
_state.PC++;
IdleEndJump();
}
/**********
Interrupts
***********/
void Cpu::ProcessInterrupt(uint16_t vector, bool forHardwareInterrupt)
{
if(forHardwareInterrupt) {
//IRQ/NMI waste 2 cycles here. BRK/COP do not (because they do those 2 cycles while loading the OP code + signature byte)
ReadCode(_state.PC);
Idle();
}
if(_state.EmulationMode) {
PushWord(_state.PC);
PushByte(_state.PS | 0x20);
SetFlags(ProcFlags::IrqDisable);
ClearFlags(ProcFlags::Decimal);
_state.K = 0;
_state.PC = ReadVector(vector);
} else {
PushByte(_state.K);
PushWord(_state.PC);
PushByte(_state.PS);
SetFlags(ProcFlags::IrqDisable);
ClearFlags(ProcFlags::Decimal);
_state.K = 0;
_state.PC = ReadVector(vector);
}
}
void Cpu::BRK()
{
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyIrqVector : Cpu::BreakVector, false);
}
void Cpu::COP()
{
ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyCoprocessorVector : Cpu::CoprocessorVector, false);
}
/******************
Bitwise operations
*******************/
void Cpu::AND()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
SetRegister(_state.A, _state.A & GetByteValue(), true);
} else {
SetRegister(_state.A, _state.A & GetWordValue(), false);
}
}
void Cpu::EOR()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
SetRegister(_state.A, _state.A ^ GetByteValue(), true);
} else {
SetRegister(_state.A, _state.A ^ GetWordValue(), false);
}
}
void Cpu::ORA()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
SetRegister(_state.A, _state.A | GetByteValue(), true);
} else {
SetRegister(_state.A, _state.A | GetWordValue(), false);
}
}
/****************
Shift operations
*****************/
template<typename T> T Cpu::ShiftLeft(T value)
{
T result = value << 1;
if(value & (1 << (sizeof(T) * 8 - 1))) {
SetFlags(ProcFlags::Carry);
} else {
ClearFlags(ProcFlags::Carry);
}
SetZeroNegativeFlags(result);
return result;
}
template<typename T> T Cpu::RollLeft(T value)
{
T result = value << 1 | (_state.PS & ProcFlags::Carry);
if(value & (1 << (sizeof(T) * 8 - 1))) {
SetFlags(ProcFlags::Carry);
} else {
ClearFlags(ProcFlags::Carry);
}
SetZeroNegativeFlags(result);
return result;
}
template<typename T> T Cpu::ShiftRight(T value)
{
T result = value >> 1;
if(value & 0x01) {
SetFlags(ProcFlags::Carry);
} else {
ClearFlags(ProcFlags::Carry);
}
SetZeroNegativeFlags(result);
return result;
}
template<typename T> T Cpu::RollRight(T value)
{
T result = value >> 1 | ((_state.PS & 0x01) << (sizeof(T) * 8 - 1));
if(value & 0x01) {
SetFlags(ProcFlags::Carry);
} else {
ClearFlags(ProcFlags::Carry);
}
SetZeroNegativeFlags(result);
return result;
}
void Cpu::ASL_Acc()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
_state.A = (_state.A & 0xFF00) | (ShiftLeft<uint8_t>((uint8_t)_state.A));
} else {
_state.A = ShiftLeft<uint16_t>(_state.A);
}
}
void Cpu::ASL()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
uint8_t value = GetByteValue();
Idle();
Write(_operand, ShiftLeft<uint8_t>(value));
} else {
uint16_t value = GetWordValue();
Idle();
WriteWord(_operand, ShiftLeft<uint16_t>(value));
}
}
void Cpu::LSR_Acc()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
_state.A = (_state.A & 0xFF00) | ShiftRight<uint8_t>((uint8_t)_state.A);
} else {
_state.A = ShiftRight<uint16_t>(_state.A);
}
}
void Cpu::LSR()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
uint8_t value = GetByteValue();
Idle();
Write(_operand, ShiftRight<uint8_t>(value));
} else {
uint16_t value = GetWordValue();
Idle();
WriteWord(_operand, ShiftRight<uint16_t>(value));
}
}
void Cpu::ROL_Acc()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
_state.A = (_state.A & 0xFF00) | RollLeft<uint8_t>((uint8_t)_state.A);
} else {
_state.A = RollLeft<uint16_t>(_state.A);
}
}
void Cpu::ROL()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
uint8_t value = GetByteValue();
Idle();
Write(_operand, RollLeft<uint8_t>(value));
} else {
uint16_t value = GetWordValue();
Idle();
WriteWord(_operand, RollLeft<uint16_t>(value));
}
}
void Cpu::ROR_Acc()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
_state.A = (_state.A & 0xFF00) | RollRight<uint8_t>((uint8_t)_state.A);
} else {
_state.A = RollRight<uint16_t>(_state.A);
}
}
void Cpu::ROR()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
uint8_t value = GetByteValue();
Idle();
Write(_operand, RollRight<uint8_t>(value));
} else {
uint16_t value = GetWordValue();
Idle();
WriteWord(_operand, RollRight<uint16_t>(value));
}
}
/***************
Move operations
****************/
void Cpu::MVN()
{
_state.DBR = _operand & 0xFF;
uint32_t destBank = _state.DBR << 16;
uint32_t srcBank = (_operand << 8) & 0xFF0000;
uint8_t value = ReadData(srcBank | _state.X);
Write(destBank | _state.Y, value);
Idle();
Idle();
_state.X++;
_state.Y++;
if(CheckFlag(ProcFlags::IndexMode8)) {
_state.X &= 0xFF;
_state.Y &= 0xFF;
}
_state.A--;
if(_state.A != 0xFFFF) {
//Operation isn't done, set the PC back to the start of the instruction
_state.PC -= 3;
}
}
void Cpu::MVP()
{
_state.DBR = _operand & 0xFF;
uint32_t destBank = _state.DBR << 16;
uint32_t srcBank = (_operand << 8) & 0xFF0000;
uint8_t value = ReadData(srcBank | _state.X);
Write(destBank | _state.Y, value);
Idle();
Idle();
_state.X--;
_state.Y--;
if(CheckFlag(ProcFlags::IndexMode8)) {
_state.X &= 0xFF;
_state.Y &= 0xFF;
}
_state.A--;
if(_state.A != 0xFFFF) {
//Operation isn't done, set the PC back to the start of the instruction
_state.PC -= 3;
}
}
/********************
Push/pull operations
*********************/
void Cpu::PEA()
{
//Push Effective Address
PushWord((uint16_t)_operand);
}
void Cpu::PEI()
{
//Push Effective Indirect address
PushWord(ReadDataWord(_operand));
}
void Cpu::PER()
{
//Push Effective Relative address
PushWord((uint16_t)((int16_t)_operand + _state.PC));
}
void Cpu::PHB()
{
Idle();
PushByte(_state.DBR);
}
void Cpu::PHD()
{
Idle();
PushWord(_state.D);
}
void Cpu::PHK()
{
//"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack"
Idle();
PushByte(_state.K);
}
void Cpu::PHP()
{
//"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack"
Idle();
PushByte(_state.PS);
}
void Cpu::PLB()
{
//"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack"
Idle();
Idle();
SetRegister(_state.DBR, PopByte());
}
void Cpu::PLD()
{
//"PHD and PLD push and pull two bytes from the stack."
Idle();
Idle();
SetRegister(_state.D, PopWord(), false);
}
void Cpu::PLP()
{
//"For PLP, (all of) the flags are pulled from the stack. Note that when the e flag is 1, the m and x flag are forced to 1, so after the PLP, both flags will still be 1 no matter what value is pulled from the stack."
Idle();
Idle();
if(_state.EmulationMode) {
SetPS(PopByte() | ProcFlags::MemoryMode8 | ProcFlags::IndexMode8);
} else {
SetPS(PopByte());
}
}
void Cpu::PHA()
{
//"When the m flag is 0, PHA and PLA push and pull a 16-bit value, and when the m flag is 1, PHA and PLA push and pull an 8-bit value. "
Idle();
PushRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::PHX()
{
Idle();
PushRegister(_state.X, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::PHY()
{
Idle();
PushRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::PLA()
{
//"When the m flag is 0, PHA and PLA push and pull a 16-bit value, and when the m flag is 1, PHA and PLA push and pull an 8-bit value."
Idle();
Idle();
PullRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::PLX()
{
Idle();
Idle();
PullRegister(_state.X, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::PLY()
{
Idle();
Idle();
PullRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::PushRegister(uint16_t reg, bool eightBitMode)
{
//"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value."
if(eightBitMode) {
PushByte((uint8_t)reg);
} else {
PushWord(reg);
}
}
void Cpu::PullRegister(uint16_t &reg, bool eightBitMode)
{
//"When the x flag is 0, PHX, PHY, PLX, and PLY push and pull a 16-bit value, and when the x flag is 1, PHX, PHY, PLX, and PLY push and pull an 8-bit value."
if(eightBitMode) {
SetRegister(reg, PopByte(), true);
} else {
SetRegister(reg, PopWord(), false);
}
}
/*********************
Store/load operations
**********************/
void Cpu::LoadRegister(uint16_t &reg, bool eightBitMode)
{
if(eightBitMode) {
SetRegister(reg, GetByteValue(), true);
} else {
SetRegister(reg, GetWordValue(), false);
}
}
void Cpu::StoreRegister(uint16_t val, bool eightBitMode)
{
if(eightBitMode) {
Write(_operand, (uint8_t)val);
} else {
WriteWord(_operand, val);
}
}
void Cpu::LDA()
{
//"When the m flag is 0, LDA, STA, and STZ are 16-bit operations"
LoadRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::LDX()
{
//"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations"
LoadRegister(_state.X, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::LDY()
{
//"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations"
LoadRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::STA()
{
//"When the m flag is 0, LDA, STA, and STZ are 16-bit operations"
StoreRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::STX()
{
//"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations"
StoreRegister(_state.X, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::STY()
{
//"When the x flag is 0, LDX, LDY, STX, and STY are 16-bit operations"
StoreRegister(_state.Y, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::STZ()
{
//"When the m flag is 0, LDA, STA, and STZ are 16-bit operations"
StoreRegister(0, CheckFlag(ProcFlags::MemoryMode8));
}
/*******************
Bit test operations
********************/
template<typename T> void Cpu::TestBits(T value, bool alterZeroFlagOnly)
{
if(alterZeroFlagOnly) {
//"Immediate addressing only affects the z flag (with the result of the bitwise And), but does not affect the n and v flags."
if(((T)_state.A & value) == 0) {
SetFlags(ProcFlags::Zero);
} else {
ClearFlags(ProcFlags::Zero);
}
} else {
ClearFlags(ProcFlags::Zero | ProcFlags::Overflow | ProcFlags::Negative);
if(((T)_state.A & value) == 0) {
SetFlags(ProcFlags::Zero);
}
if(value & (1 << (sizeof(T) * 8 - 2))) {
SetFlags(ProcFlags::Overflow);
}
if(value & (1 << (sizeof(T) * 8 - 1))) {
SetFlags(ProcFlags::Negative);
}
}
}
void Cpu::BIT()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
TestBits<uint8_t>(GetByteValue(), _immediateMode);
} else {
TestBits<uint16_t>(GetWordValue(), _immediateMode);
}
}
void Cpu::TRB()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
uint8_t value = GetByteValue();
TestBits<uint8_t>(value, true);
value &= ~_state.A;
Idle();
Write(_operand, value);
} else {
uint16_t value = GetWordValue();
TestBits<uint16_t>(value, true);
value &= ~_state.A;
Idle();
WriteWord(_operand, value);
}
}
void Cpu::TSB()
{
if(CheckFlag(ProcFlags::MemoryMode8)) {
uint8_t value = GetByteValue();
TestBits<uint8_t>(value, true);
value |= _state.A;
Idle();
Write(_operand, value);
} else {
uint16_t value = GetWordValue();
TestBits<uint16_t>(value, true);
value |= _state.A;
Idle();
WriteWord(_operand, value);
}
}
/******************
Transfer operations
*******************/
void Cpu::TAX()
{
SetRegister(_state.X, _state.A, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::TAY()
{
SetRegister(_state.Y, _state.A, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::TCD()
{
SetRegister(_state.D, _state.A, false);
}
void Cpu::TCS()
{
SetSP(_state.A);
}
void Cpu::TDC()
{
SetRegister(_state.A, _state.D, false);
}
void Cpu::TSC()
{
SetRegister(_state.A, _state.SP, false);
}
void Cpu::TSX()
{
SetRegister(_state.X, _state.SP, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::TXA()
{
SetRegister(_state.A, _state.X, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::TXS()
{
SetSP(_state.X);
}
void Cpu::TXY()
{
SetRegister(_state.Y, _state.X, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::TYA()
{
SetRegister(_state.A, _state.Y, CheckFlag(ProcFlags::MemoryMode8));
}
void Cpu::TYX()
{
SetRegister(_state.X, _state.Y, CheckFlag(ProcFlags::IndexMode8));
}
void Cpu::XBA()
{
Idle();
_state.A = ((_state.A & 0xFF) << 8) | ((_state.A >> 8) & 0xFF);
SetZeroNegativeFlags((uint8_t)_state.A);
}
void Cpu::XCE()
{
bool carry = CheckFlag(ProcFlags::Carry);
if(_state.EmulationMode) {
SetFlags(ProcFlags::Carry);
} else {
ClearFlags(ProcFlags::Carry);
}
_state.EmulationMode = carry;
if(_state.EmulationMode) {
SetPS(_state.PS | ProcFlags::IndexMode8 | ProcFlags::MemoryMode8);
_state.SP = 0x100 | (_state.SP & 0xFF);
}
}
/*****************
No operation (NOP)
******************/
void Cpu::NOP()
{
//1-byte NOP
}
void Cpu::WDM()
{
//2-byte NOP
}
/****************
Misc. operations
*****************/
void Cpu::STP()
{
//Stop the CPU
_state.StopState = CpuStopState::Stopped;
}
void Cpu::WAI()
{
//Wait for interrupt
_state.StopState = CpuStopState::WaitingForIrq;
}
/****************
Addressing modes
*****************/
void Cpu::AddrMode_Abs()
{
_operand = GetDataAddress(ReadOperandWord());
}
void Cpu::AddrMode_AbsIdxX(bool isWrite)
{
uint32_t baseAddr = GetDataAddress(ReadOperandWord());
_operand = (baseAddr + _state.X) & 0xFFFFFF;
if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
Idle();
}
}
void Cpu::AddrMode_AbsIdxY(bool isWrite)
{
uint32_t baseAddr = GetDataAddress(ReadOperandWord());
_operand = (baseAddr + _state.Y) & 0xFFFFFF;
if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
Idle();
}
}
void Cpu::AddrMode_AbsLng()
{
_operand = ReadOperandLong();
}
void Cpu::AddrMode_AbsLngIdxX()
{
_operand = (ReadOperandLong() + _state.X) & 0xFFFFFF;
}
void Cpu::AddrMode_AbsJmp()
{
_operand = GetProgramAddress(ReadOperandWord());
}
void Cpu::AddrMode_AbsLngJmp()
{
_operand = ReadOperandLong();
}
void Cpu::AddrMode_AbsIdxXInd()
{
_operand = GetProgramAddress(ReadDataWord(GetProgramAddress(ReadOperandWord() + _state.X)));
Idle();
}
void Cpu::AddrMode_AbsInd()
{
_operand = ReadDataWord(ReadOperandWord());
}
void Cpu::AddrMode_AbsIndLng()
{
_operand = ReadDataLong(ReadOperandWord());
}
void Cpu::AddrMode_Acc()
{
IdleOrRead();
}
void Cpu::AddrMode_BlkMov()
{
_operand = ReadOperandWord();
}
uint8_t Cpu::ReadDirectOperandByte()
{
uint8_t value = ReadOperandByte();
if(_state.D & 0xFF) {
//Add 1 cycle for direct register low (DL) not equal 0
Idle();
}
return value;
}
void Cpu::AddrMode_Dir()
{
_operand = GetDirectAddress(ReadDirectOperandByte());
}
void Cpu::AddrMode_DirIdxX()
{
_operand = GetDirectAddress(ReadDirectOperandByte() + _state.X);
Idle();
}
void Cpu::AddrMode_DirIdxY()
{
_operand = GetDirectAddress(ReadDirectOperandByte() + _state.Y);
Idle();
}
void Cpu::AddrMode_DirInd()
{
_operand = GetDataAddress(GetDirectAddressIndirectWord(ReadDirectOperandByte()));
}
void Cpu::AddrMode_DirIdxIndX()
{
uint8_t operandByte = ReadDirectOperandByte();
Idle();
_operand = GetDataAddress(GetDirectAddressIndirectWord(operandByte + _state.X));
}
void Cpu::AddrMode_DirIndIdxY(bool isWrite)
{
uint32_t baseAddr = GetDataAddress(GetDirectAddressIndirectWord(ReadDirectOperandByte()));
_operand = (baseAddr + _state.Y) & 0xFFFFFF;
if(isWrite || !CheckFlag(ProcFlags::IndexMode8) || (_operand & 0xFF00) != (baseAddr & 0xFF00)) {
Idle();
}
}
void Cpu::AddrMode_DirIndLng()
{
_operand = GetDirectAddressIndirectLong(ReadDirectOperandByte());
}
void Cpu::AddrMode_DirIndLngIdxY()
{
_operand = (GetDirectAddressIndirectLong(ReadDirectOperandByte()) + _state.Y) & 0xFFFFFF;
}
void Cpu::AddrMode_Imm8()
{
_immediateMode = true;
_operand = ReadOperandByte();
}
void Cpu::AddrMode_Imm16()
{
_immediateMode = true;
_operand = ReadOperandWord();
}
void Cpu::AddrMode_ImmX()
{
_immediateMode = true;
_operand = CheckFlag(ProcFlags::IndexMode8) ? ReadOperandByte() : ReadOperandWord();
}
void Cpu::AddrMode_ImmM()
{
_immediateMode = true;
_operand = CheckFlag(ProcFlags::MemoryMode8) ? ReadOperandByte() : ReadOperandWord();
}
void Cpu::AddrMode_Imp()
{
IdleOrRead();
}
void Cpu::AddrMode_RelLng()
{
_operand = ReadOperandWord();
Idle();
}
void Cpu::AddrMode_Rel()
{
_operand = ReadOperandByte();
}
void Cpu::AddrMode_StkRel()
{
_operand = (uint16_t)(ReadOperandByte() + _state.SP);
Idle();
}
void Cpu::AddrMode_StkRelIndIdxY()
{
uint16_t addr = (uint16_t)(ReadOperandByte() + _state.SP);
Idle();
_operand = (GetDataAddress(ReadDataWord(addr)) + _state.Y) & 0xFFFFFF;
Idle();
}