#include "stdafx.h" #include "Cpu.h" /************************ 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 = (uint32_t)_state.A + 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 & 0x100) { SetFlags(ProcFlags::Carry); } _state.A = (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 = (uint32_t)_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 & 0x10000) { 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) { uint32_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 = (uint32_t)_state.A + 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 & 0x100) { SetFlags(ProcFlags::Carry); } _state.A = (uint8_t)result; } void Cpu::Sub16(uint16_t value) { uint32_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 = (uint32_t)_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 & 0x10000) { 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); } void Cpu::BVC() { BranchRelative(!CheckFlag(ProcFlags::Overflow)); } void Cpu::BVS() { BranchRelative(CheckFlag(ProcFlags::Overflow)); } void Cpu::BranchRelative(bool branch) { int8_t offset = _operand; if(branch) { _state.PC = (uint16_t)(_state.PC + offset); } } /*************************** 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() { ClearFlags((uint8_t)_operand); } void Cpu::SEP() { SetFlags((uint8_t)_operand); } /****************************** 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::IncDecReg(uint16_t ®, int8_t offset) { SetRegister(reg, reg + offset, CheckFlag(ProcFlags::IndexMode8)); } void Cpu::IncDec(int8_t offset) { if(_instAddrMode == AddrMode::Acc) { SetRegister(_state.A, _state.A + offset, CheckFlag(ProcFlags::MemoryMode8)); } else { if(CheckFlag(ProcFlags::MemoryMode8)) { uint8_t value = GetByteValue() + offset; SetZeroNegativeFlags(value); Write(_operand, value); } else { uint16_t value = GetWordValue() + offset; SetZeroNegativeFlags(value); 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; } void Cpu::JMP() { _state.PC = (uint16_t)_operand; } void Cpu::JSL() { PushByte(_state.K); PushWord(_state.PC - 1); _state.K = (_operand >> 16) & 0xFF; _state.PC = (uint16_t)_operand; } void Cpu::JSR() { PushWord(_state.PC - 1); _state.PC = (uint16_t)_operand; } void Cpu::RTI() { if(_state.EmulationMode) { _state.PS = PopByte(); //TODO: incorrect _state.PC = PopWord(); } else { _state.PS = PopByte(); //TODO: incorrect _state.PC = PopWord(); _state.K = PopByte(); } } void Cpu::RTL() { _state.PC = PopWord(); _state.PC++; _state.K = PopByte(); } void Cpu::RTS() { _state.PC = PopWord(); _state.PC++; } /********** Interrupts ***********/ void Cpu::ProcessInterrupt(uint16_t vector) { if(_state.EmulationMode) { PushWord(_state.PC + 1); PushByte(_state.PS | 0x20); SetFlags(ProcFlags::IrqDisable); ClearFlags(ProcFlags::Decimal); _state.PC = ReadDataWord(vector); } else { PushByte(_state.K); PushWord(_state.PC + 1); PushByte(_state.PS); SetFlags(ProcFlags::IrqDisable); ClearFlags(ProcFlags::Decimal); _state.PC = ReadDataWord(vector); } } void Cpu::BRK() { ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyBreakVector : Cpu::BreakVector); } void Cpu::COP() { ProcessInterrupt(_state.EmulationMode ? Cpu::LegacyCoprocessorVector : Cpu::CoprocessorVector); } /****************** 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 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 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 T Cpu::ShiftRight(T value) { T result = value >> 1; if(value & 0x01) { SetFlags(ProcFlags::Carry); } else { ClearFlags(ProcFlags::Carry); } SetZeroNegativeFlags(result); return result; } template 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() { if(_instAddrMode == AddrMode::Acc) { if(CheckFlag(ProcFlags::MemoryMode8)) { _state.A = ShiftLeft((uint8_t)_state.A); } else { _state.A = ShiftLeft(_state.A); } } else { if(CheckFlag(ProcFlags::MemoryMode8)) { Write(_operand, ShiftLeft(GetByteValue())); } else { WriteWord(_operand, ShiftLeft(GetWordValue())); } } } void Cpu::LSR() { if(_instAddrMode == AddrMode::Acc) { if(CheckFlag(ProcFlags::MemoryMode8)) { _state.A = ShiftRight((uint8_t)_state.A); } else { _state.A = ShiftRight(_state.A); } } else { if(CheckFlag(ProcFlags::MemoryMode8)) { Write(_operand, ShiftRight(GetByteValue())); } else { WriteWord(_operand, ShiftRight(GetWordValue())); } } } void Cpu::ROL() { if(_instAddrMode == AddrMode::Acc) { if(CheckFlag(ProcFlags::MemoryMode8)) { _state.A = RollLeft((uint8_t)_state.A); } else { _state.A = RollLeft(_state.A); } } else { if(CheckFlag(ProcFlags::MemoryMode8)) { Write(_operand, RollLeft(GetByteValue())); } else { WriteWord(_operand, RollLeft(GetWordValue())); } } } void Cpu::ROR() { if(_instAddrMode == AddrMode::Acc) { if(CheckFlag(ProcFlags::MemoryMode8)) { _state.A = RollRight((uint8_t)_state.A); } else { _state.A = RollRight(_state.A); } } else { if(CheckFlag(ProcFlags::MemoryMode8)) { Write(_operand, RollRight(GetByteValue())); } else { WriteWord(_operand, RollRight(GetWordValue())); } } } /*************** Move operations ****************/ void Cpu::MVN() { //TODO } void Cpu::MVP() { //TODO } /******************** Push/pull operations *********************/ void Cpu::PEA() { //Push Effective Address PushWord((uint16_t)_operand); } void Cpu::PEI() { //Push Effective Indirect address PushWord((uint16_t)_operand); } void Cpu::PER() { //Push Effective Relative address PushWord((uint16_t)_operand); } void Cpu::PHB() { PushByte(_state.DBR); } void Cpu::PHD() { PushWord(_state.D); } void Cpu::PHK() { //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" PushByte(_state.K); } void Cpu::PHP() { //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" PushByte(_state.PS); } void Cpu::PLB() { //"PHP, PHK, PHP, PLB, and PLP push and pull one byte from the stack" SetRegister(_state.DBR, PopByte()); } void Cpu::PLD() { //"PHD and PLD push and pull two bytes from the stack." 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." if(_state.EmulationMode) { _state.PS = PopByte() | ProcFlags::MemoryMode8 | ProcFlags::IndexMode8; } else { _state.PS = 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. " PushRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); } void Cpu::PHX() { PushRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); } void Cpu::PHY() { 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." PullRegister(_state.A, CheckFlag(ProcFlags::MemoryMode8)); } void Cpu::PLX() { PullRegister(_state.X, CheckFlag(ProcFlags::IndexMode8)); } void Cpu::PLY() { 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 ®, 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 ®, 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 void Cpu::TestBits(T value) { if(_instAddrMode <= AddrMode::ImmM) { //"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(GetByteValue()); } else { TestBits(GetWordValue()); } } void Cpu::TRB() { //TODO } void Cpu::TSB() { //TODO } /****************** 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, CheckFlag(ProcFlags::MemoryMode8)); } void Cpu::TSC() { SetRegister(_state.A, _state.SP, CheckFlag(ProcFlags::MemoryMode8)); } 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() { _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) { SetFlags(ProcFlags::IndexMode8 | ProcFlags::MemoryMode8); //_state.A &= 0xFF; _state.Y &= 0xFF; _state.X &= 0xFF; _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 } void Cpu::WAI() { //Wait for interrupt }