#include #include #include #include "app/emu/cpu/cpu.h" namespace yaze { namespace app { namespace emu { /** * 65816 Instruction Set * * TODO: STP, WDM */ void CPU::ADC(uint16_t operand) { bool C = GetCarryFlag(); if (GetAccumulatorSize()) { // 8-bit mode uint16_t result = static_cast(A & 0xFF) + static_cast(operand) + (C ? 1 : 0); SetCarryFlag(result > 0xFF); // Update the carry flag // Update the overflow flag bool overflow = (~(A ^ operand) & (A ^ result) & 0x80) != 0; SetOverflowFlag(overflow); // Update the accumulator with proper wrap-around A = (A & 0xFF00) | (result & 0xFF); SetZeroFlag((A & 0xFF) == 0); SetNegativeFlag(A & 0x80); } else { uint32_t result = static_cast(A) + static_cast(operand) + (C ? 1 : 0); SetCarryFlag(result > 0xFFFF); // Update the carry flag // Update the overflow flag bool overflow = (~(A ^ operand) & (A ^ result) & 0x8000) != 0; SetOverflowFlag(overflow); // Update the accumulator A = result & 0xFFFF; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } } void CPU::AND(uint32_t value, bool isImmediate) { uint16_t operand; if (GetAccumulatorSize()) { // 8-bit mode operand = isImmediate ? value : memory.ReadByte(value); A &= operand; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { // 16-bit mode operand = isImmediate ? value : memory.ReadWord(value); A &= operand; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } } // New function for absolute long addressing mode void CPU::ANDAbsoluteLong(uint32_t address) { uint32_t operand32 = memory.ReadWordLong(address); A &= operand32; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } void CPU::ASL(uint16_t address) { uint8_t value = memory.ReadByte(address); SetCarryFlag(!(value & 0x80)); // Set carry flag if bit 7 is set value <<= 1; // Shift left value &= 0xFE; // Clear bit 0 memory.WriteByte(address, value); SetNegativeFlag(!value); SetZeroFlag(value); } void CPU::BCC(int8_t offset) { if (!GetCarryFlag()) { // If the carry flag is clear next_pc_ = offset; } } void CPU::BCS(int8_t offset) { if (GetCarryFlag()) { // If the carry flag is set next_pc_ = offset; } } void CPU::BEQ(int8_t offset) { if (GetZeroFlag()) { // If the zero flag is set next_pc_ = offset; } } void CPU::BIT(uint16_t address) { uint8_t value = memory.ReadByte(address); SetNegativeFlag(value & 0x80); SetOverflowFlag(value & 0x40); SetZeroFlag((A & value) == 0); } void CPU::BMI(int8_t offset) { if (GetNegativeFlag()) { // If the negative flag is set next_pc_ = offset; } } void CPU::BNE(int8_t offset) { if (!GetZeroFlag()) { // If the zero flag is clear // PC += offset; next_pc_ = offset; } } void CPU::BPL(int8_t offset) { if (!GetNegativeFlag()) { // If the negative flag is clear next_pc_ = offset; } } void CPU::BRA(int8_t offset) { next_pc_ = offset; } void CPU::BRK() { next_pc_ = PC + 2; // Increment the program counter by 2 memory.PushWord(next_pc_); memory.PushByte(status); SetInterruptFlag(true); try { next_pc_ = memory.ReadWord(0xFFFE); } catch (const std::exception& e) { std::cout << "BRK: " << e.what() << std::endl; } } void CPU::BRL(int16_t offset) { next_pc_ = offset; } void CPU::BVC(int8_t offset) { if (!GetOverflowFlag()) { // If the overflow flag is clear next_pc_ = offset; } } void CPU::BVS(int8_t offset) { if (GetOverflowFlag()) { // If the overflow flag is set next_pc_ = offset; } } void CPU::CLC() { status &= ~0x01; } void CPU::CLD() { status &= ~0x08; } void CPU::CLI() { status &= ~0x04; } void CPU::CLV() { status &= ~0x40; } // n Set if MSB of result is set; else cleared // z Set if result is zero; else cleared // c Set if no borrow; else cleared void CPU::CMP(uint32_t value, bool isImmediate) { if (GetAccumulatorSize()) { // 8-bit uint8_t result; if (isImmediate) { result = A - (value & 0xFF); } else { uint8_t memory_value = memory.ReadByte(value); result = A - memory_value; } SetZeroFlag(result == 0); SetNegativeFlag(result & 0x80); SetCarryFlag(A >= (value & 0xFF)); } else { // 16-bit uint16_t result; if (isImmediate) { result = A - (value & 0xFFFF); } else { uint16_t memory_value = memory.ReadWord(value); result = A - memory_value; } SetZeroFlag(result == 0); SetNegativeFlag(result & 0x8000); SetCarryFlag(A >= (value & 0xFFFF)); } } void CPU::COP() { next_pc_ += 2; // Increment the program counter by 2 memory.PushWord(next_pc_); memory.PushByte(status); SetInterruptFlag(true); if (E) { next_pc_ = memory.ReadWord(0xFFF4); } else { next_pc_ = memory.ReadWord(0xFFE4); } SetDecimalFlag(false); } void CPU::CPX(uint32_t value, bool isImmediate) { if (GetIndexSize()) { // 8-bit uint8_t memory_value = isImmediate ? value : memory.ReadByte(value); compare(X, memory_value); } else { // 16-bit uint16_t memory_value = isImmediate ? value : memory.ReadWord(value); compare(X, memory_value); } } void CPU::CPY(uint32_t value, bool isImmediate) { if (GetIndexSize()) { // 8-bit uint8_t memory_value = isImmediate ? value : memory.ReadByte(value); compare(Y, memory_value); } else { // 16-bit uint16_t memory_value = isImmediate ? value : memory.ReadWord(value); compare(Y, memory_value); } } void CPU::DEC(uint32_t address, bool accumulator) { if (accumulator) { if (GetAccumulatorSize()) { // 8-bit A = (A - 1) & 0xFF; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { // 16-bit A = (A - 1) & 0xFFFF; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } return; } if (GetAccumulatorSize()) { uint8_t value = memory.ReadByte(address); value--; memory.WriteByte(address, value); SetZeroFlag(value == 0); SetNegativeFlag(value & 0x80); } else { uint16_t value = memory.ReadWord(address); value--; memory.WriteWord(address, value); SetZeroFlag(value == 0); SetNegativeFlag(value & 0x8000); } } void CPU::DEX() { if (GetIndexSize()) { // 8-bit X = static_cast(X - 1); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } else { // 16-bit X = static_cast(X - 1); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x8000); } } void CPU::DEY() { if (GetIndexSize()) { // 8-bit Y = static_cast(Y - 1); SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x80); } else { // 16-bit Y = static_cast(Y - 1); SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x8000); } } void CPU::EOR(uint32_t address, bool isImmediate) { if (GetAccumulatorSize()) { A ^= isImmediate ? address : memory.ReadByte(address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { A ^= isImmediate ? address : memory.ReadWord(address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } } void CPU::INC(uint32_t address, bool accumulator) { if (accumulator) { if (GetAccumulatorSize()) { // 8-bit A = (A + 1) & 0xFF; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { // 16-bit A = (A + 1) & 0xFFFF; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } return; } if (GetAccumulatorSize()) { uint8_t value = memory.ReadByte(address); value++; memory.WriteByte(address, value); SetNegativeFlag(value & 0x80); SetZeroFlag(value == 0); } else { uint16_t value = memory.ReadWord(address); value++; memory.WriteWord(address, value); SetNegativeFlag(value & 0x8000); SetZeroFlag(value == 0); } } void CPU::INX() { if (GetIndexSize()) { // 8-bit X = static_cast(X + 1); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } else { // 16-bit X = static_cast(X + 1); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x8000); } } void CPU::INY() { if (GetIndexSize()) { // 8-bit Y = static_cast(Y + 1); SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x80); } else { // 16-bit Y = static_cast(Y + 1); SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x8000); } } void CPU::JMP(uint16_t address) { next_pc_ = address; // Set program counter to the new address } void CPU::JML(uint32_t address) { next_pc_ = static_cast(address & 0xFFFF); // Set the PBR to the upper 8 bits of the address PB = static_cast((address >> 16) & 0xFF); } void CPU::JSR(uint16_t address) { memory.PushWord(PC); // Push the program counter onto the stack next_pc_ = address; // Set program counter to the new address } void CPU::JSL(uint32_t address) { memory.PushLong(PC); // Push the program counter onto the stack as a long // value (24 bits) next_pc_ = address; // Set program counter to the new address } void CPU::LDA(uint16_t address, bool isImmediate, bool direct_page, bool data_bank) { uint8_t bank = PB; if (direct_page) { bank = 0; } if (GetAccumulatorSize()) { A = isImmediate ? address : memory.ReadByte((bank << 16) | address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { A = isImmediate ? address : memory.ReadWord((bank << 16) | address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } } void CPU::LDX(uint16_t address, bool isImmediate) { if (GetIndexSize()) { X = isImmediate ? address : memory.ReadByte(address); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } else { X = isImmediate ? address : memory.ReadWord(address); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x8000); } } void CPU::LDY(uint16_t address, bool isImmediate) { if (GetIndexSize()) { Y = isImmediate ? address : memory.ReadByte(address); SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x80); } else { Y = isImmediate ? address : memory.ReadWord(address); SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x8000); } } void CPU::LSR(uint16_t address, bool accumulator) { if (accumulator) { if (GetAccumulatorSize()) { // 8-bit SetCarryFlag(A & 0x01); A >>= 1; SetZeroFlag(A == 0); SetNegativeFlag(false); } else { // 16-bit SetCarryFlag(A & 0x0001); A >>= 1; SetZeroFlag(A == 0); SetNegativeFlag(false); } return; } uint8_t value = memory.ReadByte(address); SetCarryFlag(value & 0x01); value >>= 1; memory.WriteByte(address, value); SetNegativeFlag(false); SetZeroFlag(value == 0); } void CPU::MVN(uint16_t source, uint16_t dest, uint16_t length) { for (uint16_t i = 0; i < length; i++) { memory.WriteByte(dest, memory.ReadByte(source)); source++; dest++; } } void CPU::MVP(uint16_t source, uint16_t dest, uint16_t length) { for (uint16_t i = 0; i < length; i++) { memory.WriteByte(dest, memory.ReadByte(source)); source--; dest--; } } void CPU::NOP() { // Do nothing } void CPU::ORA(uint16_t address, bool isImmediate) { if (GetAccumulatorSize()) { A |= isImmediate ? address : memory.ReadByte(address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { A |= isImmediate ? address : memory.ReadWord(address); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } } void CPU::PEA() { uint16_t address = FetchWord(); memory.PushWord(address); } void CPU::PEI() { uint16_t address = FetchWord(); memory.PushWord(memory.ReadWord(address)); } void CPU::PER() { uint16_t address = FetchWord(); memory.PushWord(PC + address); } void CPU::PHA() { if (GetAccumulatorSize()) { memory.PushByte(static_cast(A)); } else { memory.PushWord(A); } } void CPU::PHB() { memory.PushByte(DB); } void CPU::PHD() { memory.PushWord(D); } void CPU::PHK() { memory.PushByte(PB); } void CPU::PHP() { memory.PushByte(status); } void CPU::PHX() { if (GetIndexSize()) { memory.PushByte(static_cast(X)); } else { memory.PushWord(X); } } void CPU::PHY() { if (GetIndexSize()) { memory.PushByte(static_cast(Y)); } else { memory.PushWord(Y); } } void CPU::PLA() { if (GetAccumulatorSize()) { A = memory.PopByte(); SetNegativeFlag((A & 0x80) != 0); } else { A = memory.PopWord(); SetNegativeFlag((A & 0x8000) != 0); } SetZeroFlag(A == 0); } void CPU::PLB() { DB = memory.PopByte(); SetNegativeFlag((DB & 0x80) != 0); SetZeroFlag(DB == 0); } // Pull Direct Page Register from Stack void CPU::PLD() { D = memory.PopWord(); SetNegativeFlag((D & 0x8000) != 0); SetZeroFlag(D == 0); } // Pull Processor Status Register from Stack void CPU::PLP() { status = memory.PopByte(); } void CPU::PLX() { if (GetIndexSize()) { X = memory.PopByte(); SetNegativeFlag((A & 0x80) != 0); } else { X = memory.PopWord(); SetNegativeFlag((A & 0x8000) != 0); } SetZeroFlag(X == 0); } void CPU::PLY() { if (GetIndexSize()) { Y = memory.PopByte(); SetNegativeFlag((A & 0x80) != 0); } else { Y = memory.PopWord(); SetNegativeFlag((A & 0x8000) != 0); } SetZeroFlag(Y == 0); } void CPU::REP() { auto byte = FetchByte(); status &= ~byte; } void CPU::ROL(uint32_t address, bool accumulator) { if (accumulator) { if (GetAccumulatorSize()) { // 8-bit uint8_t carry = GetCarryFlag() ? 0x01 : 0x00; SetCarryFlag(A & 0x80); A <<= 1; A |= carry; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { // 16-bit uint8_t carry = GetCarryFlag() ? 0x01 : 0x00; SetCarryFlag(A & 0x8000); A <<= 1; A |= carry; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } return; } uint8_t value = memory.ReadByte(address); uint8_t carry = GetCarryFlag() ? 0x01 : 0x00; SetCarryFlag(value & 0x80); value <<= 1; value |= carry; memory.WriteByte(address, value); SetNegativeFlag(value & 0x80); SetZeroFlag(value == 0); } void CPU::ROR(uint32_t address, bool accumulator) { if (accumulator) { if (GetAccumulatorSize()) { // 8-bit uint8_t carry = GetCarryFlag() ? 0x80 : 0x00; SetCarryFlag(A & 0x01); A >>= 1; A |= carry; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } else { // 16-bit uint8_t carry = GetCarryFlag() ? 0x8000 : 0x00; SetCarryFlag(A & 0x0001); A >>= 1; A |= carry; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } return; } uint8_t value = memory.ReadByte(address); uint8_t carry = GetCarryFlag() ? 0x80 : 0x00; SetCarryFlag(value & 0x01); value >>= 1; value |= carry; memory.WriteByte(address, value); SetNegativeFlag(value & 0x80); SetZeroFlag(value == 0); } void CPU::RTI() { status = memory.PopByte(); PC = memory.PopWord(); } void CPU::RTL() { next_pc_ = memory.PopWord(); PB = memory.PopByte(); } void CPU::RTS() { last_call_frame_ = memory.PopWord(); } void CPU::SBC(uint32_t value, bool isImmediate) { uint16_t operand; if (!GetAccumulatorSize()) { // 16-bit mode operand = isImmediate ? value : memory.ReadWord(value); uint16_t result = A - operand - (GetCarryFlag() ? 0 : 1); SetCarryFlag(!(result > 0xFFFF)); // Update the carry flag // Update the overflow flag bool overflow = ((A ^ operand) & (A ^ result) & 0x8000) != 0; SetOverflowFlag(overflow); // Update the accumulator A = result & 0xFFFF; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } else { // 8-bit mode operand = isImmediate ? value : memory.ReadByte(value); uint8_t result = A - operand - (GetCarryFlag() ? 0 : 1); SetCarryFlag(!(result > 0xFF)); // Update the carry flag // Update the overflow flag bool overflow = ((A ^ operand) & (A ^ result) & 0x80) != 0; SetOverflowFlag(overflow); // Update the accumulator A = result & 0xFF; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } } void CPU::SEC() { status |= 0x01; } void CPU::SED() { status |= 0x08; } void CPU::SEI() { status |= 0x04; } void CPU::SEP() { auto byte = FetchByte(); status |= byte; } void CPU::STA(uint32_t address) { if (GetAccumulatorSize()) { memory.WriteByte(address, static_cast(A)); } else { memory.WriteWord(address, A); } } // TODO: Make this work with the Clock class of the CPU void CPU::STP() { // During the next phase 2 clock cycle, stop the processors oscillator input // The processor is effectively shut down until a reset occurs (RES` pin). } void CPU::STX(uint16_t address) { if (GetIndexSize()) { memory.WriteByte(address, static_cast(X)); } else { memory.WriteWord(address, X); } } void CPU::STY(uint16_t address) { if (GetIndexSize()) { memory.WriteByte(address, static_cast(Y)); } else { memory.WriteWord(address, Y); } } void CPU::STZ(uint16_t address) { if (GetAccumulatorSize()) { memory.WriteByte(address, 0x00); } else { memory.WriteWord(address, 0x0000); } } void CPU::TAX() { X = A; SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } void CPU::TAY() { Y = A; SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x80); } void CPU::TCD() { D = A; SetZeroFlag(D == 0); SetNegativeFlag(D & 0x80); } void CPU::TCS() { memory.SetSP(A); } void CPU::TDC() { A = D; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } void CPU::TRB(uint16_t address) { uint8_t value = memory.ReadByte(address); SetZeroFlag((A & value) == 0); value &= ~A; memory.WriteByte(address, value); } void CPU::TSB(uint16_t address) { uint8_t value = memory.ReadByte(address); SetZeroFlag((A & value) == 0); value |= A; memory.WriteByte(address, value); } void CPU::TSC() { A = SP(); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } void CPU::TSX() { X = SP(); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } void CPU::TXA() { A = X; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } void CPU::TXS() { memory.SetSP(X); } void CPU::TXY() { Y = X; SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } void CPU::TYA() { A = Y; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } void CPU::TYX() { X = Y; SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x80); } // TODO: Make this communicate with the SNES class void CPU::WAI() { // Pull the RDY pin low // Power consumption is reduced(?) // RDY remains low until an external hardware interupt // (NMI, IRQ, ABORT, or RESET) is received from the SNES class } void CPU::XBA() { uint8_t lowByte = A & 0xFF; uint8_t highByte = (A >> 8) & 0xFF; A = (lowByte << 8) | highByte; } void CPU::XCE() { uint8_t carry = status & 0x01; status &= ~0x01; status |= E; E = carry; } } // namespace emu } // namespace app } // namespace yaze